์ง์ฐ ๋ก๋ฉ๊ณผ ์ฆ์ ๋ก๋ฉ
JPA์์๋ ๊ฐ๋ฐ์๊ฐ ์ฐ๊ด๋ ์ํฐํฐ์ ์กฐํ ์์ ์ ์ ํํ ์ ์๋๋ก ๋ค์ ๋๊ฐ์ง ๋ฐฉ๋ฒ์ ์ ๊ณตํฉ๋๋ค.
์ฆ์ ๋ก๋ฉ (EAGER LOADING)
@XToX(fetch = FetchType.EAGER)
์ํฐํฐ๋ฅผ ์กฐํํ ๋ ์ฐ๊ด๋ ์ํฐํฐ๋ ํจ๊ป ์กฐํํฉ๋๋ค.
์ง์ฐ ๋ก๋ฉ (LAZY LOADING)
@XToX(fetch = FetchType.LAZY)
์ฐ๊ด๋ ์ํฐํฐ๋ฅผ ์ค์ ์ฌ์ฉํ ๋ ์กฐํํฉ๋๋ค. (์ฌ์ฉ ์ ๊น์ง๋ ํ๋ก์๋ก ์กฐํํฉ๋๋ค.)
์ฆ์ ๋ก๋ฉ์ ์ธ๋ถ ์กฐ์ธ(Left Outer Join)์ด ๋ฐ์ํ๋ ์ด์
์ฆ์ ๋ก๋ฉ์ ์ฌ์ฉํ ๋, ์คํ๋๋ SQL์ด ๋ด๋ถ ์กฐ์ธ(Inner Join)์ด ์คํ๋๋ ๊ฒฝ์ฐ๊ฐ ์๊ณ , ์ธ๋ถ ์กฐ์ธ(Left Outer Join)์ด ์คํ๋๋ ๊ฒฝ์ฐ๊ฐ ์์ต๋๋ค.
์ด ์ฐจ์ด๊ฐ ๋ฐ์ํ๋ ์ด์ ๋ ๋ฌด์์ผ๊น์?
Null ์ ์ฝ์กฐ๊ฑด๊ณผ JPA ์กฐ์ธ ์ ๋ต
์ธ๋ ํค์ NULL ๊ฐ์ด ํ์ฉ๋๋ ๊ฒฝ์ฐ Inner Join์ ์ฌ์ฉํ๋ค๋ฉด, ์ฐ๊ด๋ ์ํฐํฐ๊ฐ Null์ธ ๊ฒฝ์ฐ์๋ ์ฐ๊ด๋ ์ํฐํฐ๋ ๋ฌผ๋ก ์ด๊ณ ํด๋น ์ํฐํฐ๋ ์กฐํํ ์ ์์ด์ง๋๋ค.
JPA๋ ์ด๋ฐ ์ํฉ์ ๊ณ ๋ คํด์ ์ธ๋ถ ์กฐ์ธ์ ์ฌ์ฉํฉ๋๋ค.
ํ์ง๋ง ์ธ๋ถ ์กฐ์ธ๋ณด๋ค๋ ๋ด๋ถ ์กฐ์ธ์ด ์ฑ๋ฅ๊ณผ ์ต์ ํ์์ ํจ์ฌ ์ ๋ฆฌํฉ๋๋ค.
๊ทธ๋ผ ๋ด๋ถ ์กฐ์ธ์ ์ธ์ ์ฌ์ฉ๋ ๊น์?
์ธ๋ ํค์ NOT NULL ์ ์ฝ์กฐ๊ฑด์ ์ค์ ํ๊ฒ ๋๋ฉด ๊ฐ์ด ์๋๊ฒ์ ๋ณด์ฅํฉ๋๋ค.
๋ฐ๋ผ์ ์ด๋๋ ๋ด๋ถ ์กฐ์ธ๋ง ์ฌ์ฉํด๋ ๋ฉ๋๋ค.
JPA์๊ฒ ์ด๋ฐ ์ฌ์ค์ ์๋ ค์ฃผ๊ธฐ ์ํด @JoinColumn์ nullable = false๋ฅผ ์ค์ ํด์ ์ด ์ธ๋ ํค๋ NULL ๊ฐ์ ํ์ฉํ์ง ์๋๋ค๊ณ ์๋ ค์ฃผ๋ฉด, JPA๋ ์ธ๋ถ์กฐ์ธ ๋์ ์ ๋ด๋ถ์กฐ์ธ์ ์ฌ์ฉํฉ๋๋ค.
๋๋ @ManyToOne(optinal = false)๋ฅผ ์ฌ์ฉํ๋ ๋ฐฉ๋ฒ๋ ์์ต๋๋ค.
์ปฌ๋ ์ ๋ํผ - PersistentBag
ํ์ด๋ฒ๋ค์ดํธ๋ ์ํฐํฐ๋ฅผ ์์ ์ํ๋ก ๋ง๋ค ๋ ์ํฐํฐ์ ์ปฌ๋ ์ ์ด ์์ผ๋ฉด ์ปฌ๋ ์ ์ ์ถ์ ํ๊ณ ๊ด๋ฆฌํ ๋ชฉ์ ์ผ๋ก ์๋ณธ ์ปฌ๋ ์ ์ ํ์ด๋ฒ๋ค์ดํธ๊ฐ ์ ๊ณตํ๋ ๋ด์ฅ ์ปฌ๋ ์ ์ผ๋ก ๋ณ๊ฒฝํ๋๋ฐ ์ด๊ฒ์ ์ปฌ๋ ์ ๋ํผ๋ผ ํฉ๋๋ค.
๊ฐ๋จํ ์์๋ฅผ ๋ค์ด๋ณด๊ฒ ์ต๋๋ค.
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Entity
@Table(name = "car")
public class Car {
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "car_id")
private Long id;
private String name;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "member_id")
private Member member;
public void setId(Long id) {
this.id = id;
}
public void setName(String name) {
this.name = name;
}
}
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Entity
@Setter
@Getter
public class Member {
@Id @GeneratedValue
@Column(name = "MEMBER_ID")
private Long id;
@ElementCollection
@CollectionTable(name = "MEMBER_NICKNAME", joinColumns = @JoinColumn(name = "MEMBER_ID", referencedColumnName = "MEMBER_ID"))
private List<String> nickNames = new ArrayList<>();
@OneToMany(mappedBy = "member")
private List<Car> cars = new ArrayList<>();
}
Member member = new Member();
em.persist(member);
em.flush();
em.clear();
Member member1 = em.find(Member.class, 1L);
List<String> a = member.getNickNames();
System.out.println(a.getClass().getName());
List<Car> aa = member.getCars();
System.out.println(aa.getClass().getName());
๊ฒฐ๊ณผ๋ ๋ค์๊ณผ ๊ฐ์ต๋๋ค.
์ํฐํฐ๋ฅผ ์ง์ฐ ๋ก๋ฉํ๋ฉด ํ๋ก์ ๊ฐ์ฒด๋ฅผ ์ฌ์ฉํด์ ์ง์ฐ ๋ก๋ฉ์ ์ํํ์ง๋ง, ์ฃผ๋ฌธ ๋ด์ญ๊ฐ์ ์ปฌ๋ ์ ์ ์ปฌ๋ ์ ๋ํผ๊ฐ ์ง์ฐ ๋ก๋ฉ์ ์ฒ๋ฆฌํด์ค๋๋ค.
์ปฌ๋ ์ ๋ํผ๋ ์ปฌ๋ ์ ์ ๋ํ ํ๋ก์ ์ญํ ์ ํ๋ฏ๋ก ๋ฐ๋ก ๊ตฌ๋ถํ ํ์๋ ์์ด๋ณด์ ๋๋ค.
โญ๏ธ ์ปฌ๋ ์ ์ด ์ด๊ธฐํ๋๋ ๊ฒฝ์ฐ
member.getNickNames().get(0) ์ด๋
member.getNickNames().size() ์ฒ๋ผ
์ปฌ๋ ์ ์์ ์ค์ ๋ฐ์ดํฐ๋ฅผ ์ฌ์ฉํ ๋ ๋ฐ์ดํฐ๋ฒ ์ด์ค๋ฅผ ์กฐํํด์ ์ด๊ธฐํํฉ๋๋ค.
member.getNickNames() ๋ง ํธ์ถํ๋ค๋ฉด ์ปฌ๋ ์ ์ ์ด๊ธฐํ๋์ง ์์ต๋๋ค.
๊ฒฐ๋ก
๊ฐ๊ธ์ ์ง์ฐ๋ก๋ฉ์ ์ฌ์ฉํ๋ ๊ฒ์ ๊ถ์ฅํฉ๋๋ค.
- ์ฆ์ ๋ก๋ฉ์ ์ ์ํ๋ฉด ์์ํ์ง ๋ชปํ SQL์ด ๋ฐ์ํฉ๋๋ค.
- ์ฆ์ ๋ก๋ฉ์ JPQL ์ฌ์ฉ ์ N+1 ๋ฌธ์ ๋ฅผ ์ผ์ผํต๋๋ค. ( em.createQuery("select m from Member m", Member.class) ์, Member์ ์ฆ์๋ก๋ฉ์ผ๋ก ์ฐ๊ด๋ ์ํฐํฐ๋ฅผ ๊ทธ ๊ฐ์๋งํผ ์ฟผ๋ฆฌ๋ฅผ ๋ ๋ ค ์กฐํ)
- @ManyToOne, @OneToOne์ ๊ธฐ๋ณธ์ด ์ฆ์ ๋ก๋ฉ์ด๋ฏ๋ก ๊ผญ ์ง์ฐ ๋ก๋ฉ์ผ๋ก ์ค์ ํด์ฃผ๋ ๊ฒ์ ๊ถ์ฅํฉ๋๋ค.
N+1 ๋ฌธ์ ์์
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Entity
@Table(name = "car")
public class Car {
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "car_id")
private Long id;
private String name;
@ManyToOne(fetch = FetchType.EAGER)
@JoinColumn(name = "member_id")
private Member member;
public void setId(Long id) {
this.id = id;
}
public void setName(String name) {
this.name = name;
}
public void setMember(Member member) {
this.member = member;
}
}
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Entity
@Setter
@Getter
public class Member {
@Id @GeneratedValue
@Column(name = "MEMBER_ID")
private Long id;
}
์์ ๊ฐ์ ์ํฉ์์ ์๋ ์ฝ๋๋ฅผ ์คํํ๊ฒ ์ต๋๋ค.
Member member = new Member();
Member member2 = new Member();
Car car = new Car();
car.setName("11");
Car car2 = new Car();
car2.setName("22");
car.setMember(member);
car2.setMember(member2);
em.persist(member);
em.persist(member2);
em.persist(car);
em.persist(car2);
em.flush();
em.clear();
System.out.println("=============");
List<Car> cars = em.createQuery("select c from Car c", Car.class).getResultList();
๊ฒฐ๊ณผ๋ ๋ค์๊ณผ ๊ฐ์ต๋๋ค.
Reference
[์๋ฐ ORM ํ์ค JPA ํ๋ก๊ทธ๋๋ฐ - ๊น์ํ]
'๐๏ธ Spring > JPA' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
[JPA] @AttributeOverride - ๋งคํ ์ ๋ณด ์ฌ์ ์ (1) | 2021.12.16 |
---|---|
[JPA] ์์์ฑ ์ ์ด(CASCADE)์ ๊ณ ์ ๊ฐ์ฒด (0) | 2021.12.15 |
[JPA] JPA์์์ ํ๋ก์ (0) | 2021.12.15 |
[JPA] @AssocicationOverride - ์ฐ๊ด๊ด๊ณ(์ธ๋ ํค ์ปฌ๋ผ๋ช ) ์ฌ์ ์ (0) | 2021.12.15 |
[JPA] ํ๋์ ์ปฌ๋ผ ๋งคํ - @Embedded (์๋ฒ ๋๋ ํ์ ๋งคํ) (0) | 2021.12.15 |