양방향 조회
엔티티
@Entity
public class Passport {
@Id
@Column(name = "PASSPORT_ID")
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String number;
@OneToOne(mappedBy = "passport", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
private Person person;
public Person getPerson() {
return person;
}
public void setPerson(Person person) {
this.person = person;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getNumber() {
return number;
}
public void setNumber(String number) {
this.number = number;
}
}
@Entity
public class Person {
@Id
@Column(name = "person_id")
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
@OneToOne(fetch = FetchType.EAGER)
@JoinColumn(name = "PASSPORT_ID")
private Passport passport;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public Passport getPassport() {
return passport;
}
public void setPassport(Passport passport) {
this.passport = passport;
}
}
테스트 코드 준비
em.clear() 이후에 상황별로 조회 코드를 작성하여 테스트 해볼려고 한다.
// OneToOne bi-directional
public class Test2 {
public static void main(String[] args) {
EntityManagerFactory emf = Persistence.createEntityManagerFactory("my-pu");
EntityManager em = emf.createEntityManager();
EntityTransaction tx = em.getTransaction();
try {
tx.begin();
Person person = new Person();
person.setName("홍길동");
Passport passport = new Passport();
passport.setNumber("kor123");
person.setPassport(passport);
passport.setPerson(person);
// 2 entity persist
em.persist(passport);
em.persist(person);
em.flush();
em.clear();
tx.commit();
}finally {
em.close();
}
}
}
CASE1
Passport 조회 - (연관관계의 주인 X)
Person fetch EAGER, Passport fetch EAZER
Passport findPassport = em.find(Passport.class, 1);
System.out.println(findPassport);
// findPassport 를 이용해서 person entity의 실제 값을 사용해도 sql 다시 수행 X
System.out.println(findPassport.getPerson().getName());
처음에는 passport에 대한 select문이 발생
FetchType이 EAGER라 person에 대한 select문도 발생하는데 join으로 Passport도 함게 가져온다.
findPassport.getPerson().getName() 와 같이 Person의 실제 값 사용 여부 없이 Person도 조회하는 select문이 발생한다.
실행 쿼리
Hibernate:
select
p1_0.PASSPORT_ID,
p1_0.number
from
Passport p1_0
where
p1_0.PASSPORT_ID=?
Hibernate:
select
p1_0.person_id,
p1_0.name,
p2_0.PASSPORT_ID,
p2_0.number
from
Person p1_0
left join
Passport p2_0
on p2_0.PASSPORT_ID=p1_0.PASSPORT_ID
where
p1_0.PASSPORT_ID=?
CASE2
Person 조회 - (연관관계의 주인 O)
Person fetch EAGER, Passport fetch EAZER
{
Person findPerson = em.find(Person.class, 1);
System.out.println(findPerson);
// person 를 이용해서 passport entity 를 사용하면 sql 다시 수행 X
System.out.println(findPerson.getPassport().getNumber());
}
Person을 조회하는데 FetchType이 EAGER라 join으로 Passport도 함게 가져온다.
findPerson.getPassport().getNumber() 와 같이 Passport의 실제 값 사용 여부 없이 join으로 Passport도 같이 조회한다.
실행 쿼리
Hibernate:
select
p1_0.person_id,
p1_0.name,
p2_0.PASSPORT_ID,
p2_0.number
from
Person p1_0
left join
Passport p2_0
on p2_0.PASSPORT_ID=p1_0.PASSPORT_ID
where
p1_0.person_id=?
CASE3
Person 조회 - (연관관계의 주인 O)
Person fetch LAZY, Passport fetch EAZER
{
Person findPerson = em.find(Person.class, 1);
System.out.println(findPerson);
/*
Hibernate: select p1_0.id,p1_0.name,p1_0.passport from Person p1_0 where p1_0.id=?
Person [id=1, name=홍길동]
*/
// findPerson 를 이용해서 passport entity 를 사용하면 sql 다시 수행 (join)
System.out.println(findPerson.getPassport().getNumber());
/*
Hibernate: select p1_0.id,p1_0.number,p2_0.id,p2_0.name from Passport p1_0 left join Person p2_0 on p1_0.id=p2_0.passport where p1_0.id=?
Passport [id=1, number=kor123]
*/
}
em.find(Person.class, 1) 부분에서 Person을 조회하는 select문 발생
지연로딩이라 Passport를 조회하는 select문이 바로 발생하지 않는다.
findPerson.getPassport().getNumber()와 같이 Passport의 실제 값을 사용하는 경우에 Passport를 조회하는 select문이 발생한다. 특이점
person 를 이용해서 passport entity 를 사용하면 sql 다시 수행
Passport 가 fetch 설정이 없으면 @OneToOne 의 fetch default 인 EAGER 로 인해 연관관계를 가지는 Person 과 join
만약 Passport 의 fetch 설정이 LAZY 이면 join 대신 2개의 select sql 이 실행된다.
실행쿼리
Hibernate:
select
p1_0.person_id,
p1_0.name,
p1_0.PASSPORT_ID
from
Person p1_0
where
p1_0.person_id=?
com.mycom.myapp.entity.Person@4d4df0f4
Hibernate:
select
p1_0.PASSPORT_ID,
p1_0.number
from
Passport p1_0
where
p1_0.PASSPORT_ID=?
Hibernate:
select
p1_0.person_id,
p1_0.name,
p1_0.PASSPORT_ID
from
Person p1_0
where
p1_0.PASSPORT_ID=?
CASE4
Passport 조회 - (연관관계의 주인 X)
Person fetch EAZER, Passport fetch LAZY
{
Passport findPassport = em.find(Passport.class, 1);
System.out.println(findPassport);
System.out.println(findPassport.getPerson().getName());
}
Passport 를 위한 select 1 건 수행 + Person 과의 join 1 건 수행 : 총 2건 sql 수행
연관관계에서 fetch 를 LAZY 로 변경해도 join sql 이 수행되는 경우 발생
mappedBy 에 의해 Non Owner Entity 인 경우, 해당 테이블에 Owner Entity 에 대한 컬럼이 존재하지 않는다.
그래서 Passport 1건으로부터 이후 연관된 Person을 찾을 방법이 없다.
LAZY 정책을 수행하기 위해서는 미리 Passport 와 연관된 Person 을 찾아서 준비한다.(궁여지책)
LAZY 에 대한 JPA 구현체의 대응 : Proxy 생성 <- null (person) 어렵다.
실행쿼리
Hibernate:
select
p1_0.PASSPORT_ID,
p1_0.number
from
Passport p1_0
where
p1_0.PASSPORT_ID=?
Hibernate:
select
p1_0.person_id,
p1_0.name,
p2_0.PASSPORT_ID,
p2_0.number
from
Person p1_0
left join
Passport p2_0
on p2_0.PASSPORT_ID=p1_0.PASSPORT_ID
where
p1_0.PASSPORT_ID=?
단방향 조회
엔티티
@Entity
public class Person {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
@OneToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "PASSPORT_ID")
private Passport passport;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public Passport getPassport() {
return passport;
}
public void setPassport(Passport passport) {
this.passport = passport;
}
}
@Entity
public class Passport {
@Id
@Column(name = "PASSPORT_ID")
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String number;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getNumber() {
return number;
}
public void setNumber(String number) {
this.number = number;
}
}
테스트 코드
// OneToOne UNI-directional
public class Test2 {
public static void main(String[] args) {
EntityManagerFactory emf = Persistence.createEntityManagerFactory("my-pu");
EntityManager em = emf.createEntityManager();
EntityTransaction tx = em.getTransaction();
try {
tx.begin();
Person person = new Person();
person.setName("홍길동");
Passport passport = new Passport();
passport.setNumber("kor123");
person.setPassport(passport);
// 2 entity persist
em.persist(passport);
em.persist(person);
em.flush();
em.clear();
tx.commit();
}finally {
em.close();
}
}
}
CASE1
Passport 조회 - 연관관계주인 (X)
Person fetch EAZER, Passport fetch EAZER
{
Passport findPassport = em.find(Passport.class, 1);
System.out.println(findPassport);
}
실행 쿼리
Hibernate:
select
p1_0.PASSPORT_ID,
p1_0.number
from
Passport p1_0
where
p1_0.PASSPORT_ID=?
CASE2
Person 조회 - 연관관계주인 (O)
Person fetch EAZER, Passport fetch EAZER
{
Person findPerson = em.find(Person.class, 1);
System.out.println(findPerson);
}
실행 쿼리
Hibernate:
select
p1_0.id,
p1_0.name,
p2_0.PASSPORT_ID,
p2_0.number
from
Person p1_0
left join
Passport p2_0
on p2_0.PASSPORT_ID=p1_0.PASSPORT_ID
where
p1_0.id=?
CASE3
Person 조회 - 연관관계주인 (O)
Person fetch LAZY, Passport fetch EAZER
{
Person person = em.find(Person.class, 1);
System.out.println(person);
}
실행 쿼리
Hibernate:
select
p1_0.id,
p1_0.name,
p1_0.PASSPORT_ID
from
Person p1_0
where
p1_0.id=?
'JPA > JPA 정리' 카테고리의 다른 글
@ManyToOne, @OneToMany 조회 (0) | 2024.08.25 |
---|---|
영속성 컨텍스트와 CRUD (0) | 2024.08.24 |