데이터베이스에서는 두 테이블의 연관관계를 설정하면 외래키를 통해 서로 조인해서 참조하는 구조로 생성되지만 JPA를 사용하는 객체지향 모델링에서는 엔티티 간 참조방향을 설정할 수 있다.
연관관계가 설정되면 한 테이블에서 다른 테이블의 기본값을 외래키로 갖기 된다. 일반적으로 외래키를 가진 테이블이 그 관계의 주인이 되며, 주인은 외래키를 사용할 수 있으나 상대 엔티티는 읽는 작업만 수행할 수 있다.
@Entity(name = "parent")
public class Parent {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id; //여기는 parent 의 id
}
@Entity(name = "child")
public class Child {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id; //여기는 child 의 id
@OneToOne
@JoinColumn(name = "parent_id") //child에 지정되있는 FK parent_id기준으로 parent조회
private Parent parent;
}
@OneToOne 어노테이션은 다른 엔티티 객체(ProductEntity)를 필드로 정의했을 때 일대일 연관관계로 매핑하기 위해 사용된다. 뒤이어 @JoinColumn 어노테이션을 사용해 매핑할 외래키를 설정한다.
JPA에서 관계를 정의할때 부모 Entity 객체에 정의된 PK와 자식 Entity객체에 정의된 FK를 기준으로 정의할 수 있는데 @OneToOne 어노테이션이 위치하는 곳은 기본적으로 FK가 있는쪽, 즉 자식쪽 Entity 객체쪽이 된다.
OneToOne 관계를 맺었을때 디폴트 설정으로 FetchType 이 EAGER 로 설정되어 있어 자식 Entity 를 조회 했을때 자동으로 부모 Entity 를 조회해 옵니다. 이때 바로 부모 Entity 를 사용할 필요가 없다면 속도를 위해 FetchType 을 LAZY 로 설정할 수 있습니다. FetchType 을 LAZY 로 지정하면 지정된 Entity 객체를 미리 가지고 있는 것이 아니라 Entity 객체를 사용하려고 할때 그 즉시 데이타를 데이터베이스에서 가지고 옵니다.
@Entity(name = "parent")
public class Parent {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id; //여기는 parent 의 id
}
@Entity(name = "child")
public class Child {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id; //여기는 child 의 id
@OneToOne(fetch = FetchType.LAZY) //사용시점에 조회가 됨
@JoinColumn(name = "parent_id") //child 에 지정되어 있는 FK parent_id 기준으로 parent 조회
private Parent parent;
}
@OneToMany는 데이터를 바라보는 주체가 부모 Entity이며 하나의 부모 Entity데이터와 연관이 있는 여러개의 자식 Entity 데이터를 사용한다는 의미입니다.
이때 부모 Entity에서 @OneToMany 어노테이션을 지정하게 되며, JPA 관계 중 유일하게 @어노테이션이 자식Entity이 아닌 부모 Entity에 위치하게 됩니다. 또한 속도를 위해 기본적으로 FetchType 설정이 LAZY로 설정되어 있습니다.
@Entity(name = "parent")
public class Parent {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@OneToMany
@JoinColumn(name = "parent_id") //child 테이블에 있는 parent_id FK
private List<Child> childList;
}
@Entity(name = "child")
public class Child {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
}
아래는 응용코드를 가져왔다. Member은 부모Entity이고 Phone은 자식 Entity이다.
package jpa3;
import java.util.List;
import javax.transaction.Transactional;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class Jpa3Application implements CommandLineRunner{
@Autowired
private MemberRepository mr;
@Autowired
private PhoneRepository pr;
public static void main(String[] args) {
SpringApplication.run(Jpa3Application.class, args);
}
@Override
public void run(String... args) throws Exception {
Member first = new Member("Jung"); // (1)
first.addPhone(new Phone("010-XXXX-XXXX"));
first.addPhone(new Phone("010-YYYY-YYYY"));
Member second = new Member("Dong");
second.addPhone(new Phone("010-ZZZZ-ZZZZ"));
Member third = new Member("Min"); // (2)
mr.save(first); // (3)
mr.save(second);
mr.save(third); // (4)
List<Member> list = mr.findAll(); // (5)
for( Member m : list ){
System.out.println(m.toString());
} // (6)
mr.deleteAll(); // (7)
}
}
@ManyToOne
@Entity(name = "parent")
public class Parent {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
}
@Entity(name = "child")
public class Child {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@ManyToOne
@JoinColumn(name = "parent_id")
private Parent parent;
}