2020-4-12 스프링 Data common (3) JPA 단방향, 양방향 매핑

1대다 맵핑

관계에는 항상 두 엔티티가 존재. 3개의 관계 이런건 전혀없다.

  • 하나는 주인이고 하나는 종속된쪽이다.
  • 해당 관계의 반대쪽 레퍼런스를 가지고 있는 쪽이 주인.
ManyToOne(단방향)
@Entity
public class Study {

    @Id @GeneratedValue
    private Long id;

    private String name;

    @ManyToOne
    private Account owner;
}

create table study (
       id bigint not null,
        name varchar(255),
        owner_id bigint,
        primary key (id)
    ) engine=MyISAM

alter table study
       add constraint FK210g5r7wftvloq2ics531e6e4
       foreign key (owner_id)
       references account (id)


+----+-------------------+----------+
| id | name              | owner_id |
+----+-------------------+----------+
|  2 | spring data study |        1 |
+----+-------------------+----------+

mysql> select * from account;
+----+-----------+----------+
| id | password  | username |
+----+-----------+----------+
|  1 | hibernate | jimmy    |
+----+-----------+----------+  
  • oneToMany, ManyToOne이 많이 헷갈리는데 현재 레퍼런스를 보면된다. 위에서 Account가 콜렉션이 아니라 한개이면 one이다. 항상 오른쪽의 끝을 봐라.
  • 실제 코드를 보면 owner_id Column이라고 생성이 된다. Owner_id에 대한 제약이 foreign_key로 잡힌다.
  • 관계에서 주인은 Study가 주인이다.
  • 주인 : 관계를 설정했을때, 반영이 되는것

ManyToOne(단방향)

@Entity
public class Account {

    @Id @GeneratedValue
    private Long id;
		...

    @OneToMany
    private Set<Study> studies = new HashSet<>();
}

create table account (
       id bigint not null,
        password varchar(255),
        username varchar(255),
        primary key (id)
    ) engine=MyISAM

create table account_studies (
       account_id bigint not null,
        studies_id bigint not null,
        primary key (account_id, studies_id)
    ) engine=MyISAM

create table study (
       id bigint not null,
        name varchar(255),
        primary key (id)
    ) engine=MyISAM


// 저장하는 순서

Hibernate:
    insert
    into
        account
        (password, username, id)
    values
        (?, ?, ?)
Hibernate:
    insert
    into
        study
        (name, id)
    values
        (?, ?)
Hibernate:
    insert
    into
        account_studies
        (account_id, studies_id)
    values
        (?, ?)

// 데이터 조회
mysql> select * from account;
+----+-----------+----------+
| id | password  | username |
+----+-----------+----------+
|  1 | hibernate | jimmy    |
+----+-----------+----------+

mysql> select * from study;
+----+-------------------+
| id | name              |
+----+-------------------+
|  2 | spring data study |
+----+-------------------+

mysql> select * from account_studies;
+------------+------------+
| account_id | studies_id |
+------------+------------+
|          1 |          2 |
+------------+------------+  
  • 테이블을 생성할때, account_study를 따로 만든다.
  • 삽입할때, account, study를 먼저 삽입하고 두 정보를 가지고 있는 account_studies에 account_id, studies_id를 함께 넣는다.
  • 데이터를 조회하면 account, study테이블에는 관계에 대한 정보가 아무것도 없다. account_studies에 다 들어가있다.

양방향

  • Account에 OneToMany붙여서 studies조회하고, Study에 ManyToOne붙여서 owner를 조회하면양방향 관계 아니다. 각각 단방향관계다.
  • 양방향을 만들기 위해 OneToMany에 mappedBy설정을 해야한다. mappedBy를 안붙이는것과 붙이는것의 sql 구조적인 차이는 아직 잘 모르겠다.
  • 양방향관계에서는 foreignKey를 가진쪽이 owner이다.

  • 관계의 주인이 굉장히 중요해진다.
@Component
@Transactional
public class JpaRunner implements ApplicationRunner {
    @PersistenceContext
    EntityManager entityManager;

    @Override
    public void run(ApplicationArguments args) throws Exception {
        Account account = new Account();
  			...
        Study study = new Study();
        study.setName("spring data study");
        account.getStudies().add(study);
  			study.setOwner(account); // 이 코드를 반드시 추가해야한다.
    }
}

// 코드추가 안했을때
mysql> select * from study;
+----+-------------------+----------+
| id | name              | owner_id |
+----+-------------------+----------+
|  2 | spring data study |     NULL |
+----+-------------------+----------+

// 코드추가  
mysql> select * from study;
+----+-------------------+----------+
| id | name              | owner_id |
+----+-------------------+----------+
|  2 | spring data study |        1 |
+----+-------------------+----------+  
  • 관계의 주인은 Study인데, Account쪽에서 추가를 하고 있다.
  • 위에 있는 2줄의 코드를 하나의 메서드로 만들어서 설정을 해주면 좋다.
// In Account Class
public void addStudy(Study study) {
        this.getStudies().add(study);
        study.setOwner(this);
    }

public void removeStudy(Study study) {
        this.getStudies().remove(study);
        study.setOwner(null);
    }
  • 위와 같은 메서드를 convenient메서드라고 한다.
Written on April 12, 2020