π μ€νλ§ λΆνΈμ JPA μ€λ¬΄ μμ μ 볡 λ‘λ맡
κΉμνμ μ€νλ§ λΆνΈμ JPA μ€λ¬΄ μμ μ 볡 λ‘λ맡 λ΄μ©μ κΈ°λ°μΌλ‘ Spring boot + JPA + Querydsl νμ΅ λ΄μ©μ μ 리ν μλ£μ λλ€.
μ°κ΄κ΄κ³ 맀ν
λ€μ€μ±, λ¨λ°©ν₯VSμλ°©ν₯, μ°κ΄κ΄κ³μ μ£ΌμΈ
JPAλ₯Ό μ²μ λ°°μ°λ μ¬λλ€μ΄ κ°μ₯ ν·κ°λ¦¬κ³ μ΄λ ΅κ² λλ μ μλ λΆλΆμΌ κ² κ°λ€κ³ μκ°ν©λλ€. μ΄ν λ΄μ©μ κΉμνλ μΈνλ° λ‘λ맡 νμ΅λ΄μ©μ κΈ°λ°μΌλ‘ μ°κ΄κ΄κ³ 맀νμ λν΄ μ 리νμ΅λλ€.
μ°κ΄κ΄κ³λ λ°μ΄ν°λ² μ΄μ€μμ μΈλν€λ‘ RDB κ΄κ³λ₯Ό μ€μ νλ κ²μ²λΌ κ°μ²΄μ§ν₯ μ€κ³μμ κ°μ²΄λ₯Ό μ°Έμ‘°νλ λ°©μμ μ΄μΌκΈ°ν©λλ€. μ°κ΄μ΄ μλ κ΄κ³, μλ₯Ό λ€λ©΄, μ΄λ€ λ°μ μνλ νμμ΄ μ¬λ¬ λͺ
μ΄λΌλ©΄ κ·Έ λ°κ³Ό νμλ€μ 1:N 맀ν
μΌλ‘ μ€λͺ
ν μ μμ΅λλ€. μ΄λ κ² JPAμμλ μ°κ΄κ΄κ³λ₯Ό μΈλν€κ° μμ΄ κ°μ²΄μ μ°Έμ‘° λ°©μμΌλ‘ μ°κ΄μ μ§μ μ μλ λ°©λ²μ μ 곡ν©λλ€.
μ°κ΄κ΄κ³ 맀νμ κ³ λ €μ¬ν
μ°κ΄κ΄κ³λ ν¬κ² λ¨λ°©ν₯κ³Ό μλ°©ν₯ 맀νμΌλ‘ λλ©λλ€. λ¨λ°©ν₯μ λ§ κ·Έλλ ν μͺ½μμ λ€λ₯Έ κ΄κ³λ₯Ό μ°Έμ‘°νλ κ²μ΄κ³ , μλ°©ν₯μ μμͺ½μμ μ°Έμ‘°κ° κ°λ₯ν©λλ€. μ°κ΄κ΄κ³μμλ μ£ΌμΈμ μ νκ³ μ£ΌμΈμ΄ μλ λ°©ν₯μμλ λ¨μ μ½κΈ°λ₯Ό μν΄ μ£Όλ‘ μ°Έμ‘°λ₯Ό κ±Έμ§λ§, μ€λ¬΄μμλ μμͺ½μ λ°μ΄ν°λ₯Ό κ°νΈνκ² κ°μ§κ³ μ€κΈ° μν΄ μλ°©ν₯ 맀νμ΄ λ§μ΄ μ¬μ©λ©λλ€.
μ°κ΄κ΄κ³μ μ£ΌμΈμ μΈλν€κ° μλ ν
μ΄λΈ(N)κ³Ό 맀νλλ entityμ μ€μ νλ κ²μ κΆμ₯λ©λλ€. κ·Έ λ°λμͺ½μλ mappedBy
λ‘ κ΄κ³λ₯Ό μ μ μλ λ³μλͺ
μ 맀νν΄μ£Όμ΄μΌ ν©λλ€. μΈλν€κ° μλ κ³³μ μ£ΌμΈμΌλ‘ μ νλ μ΄μ λ λ°μ΄ν°λ² μ΄μ€ μμ
μ ν λ, μ£ΌμΈμΈ entityλ₯Ό μ€μ¬μΌλ‘ JPAλ μμ
μ μνν©λλ€. μΈλν€ κ°μ΄ κ°μ΄ μ
λ°μ΄νΈνλ©΄μ κ΄λ¦¬μ μ μ§λ³΄μκ° λ νΈνκΈ° λλ¬Έμ
λλ€.
μ°κ΄κ΄κ³λ₯Ό μ¬μ©ν λλ μμ κ°μ²΄ μνλ₯Ό κ³ λ €ν΄μ νμ μμͺ½μ κ°μ μ€μ νλ κ²μ΄ μ’μ΅λλ€. λ°λΌμ ν μͺ½ entityμ setter λ©μλ λμ μ°κ΄κ΄κ³μ λ°λμλ μλμΌλ‘ setμ΄ λ μ μλλ‘ μμ보기 μ¬μ΄ λ©μλλ₯Ό λ°λ‘ μ§μ ν΄μ£Όλ κ²μ΄ μ’μ΅λλ€. κ·ΈλμΌ μμͺ½ λ€ κ°μ μμ§ μκ³ μ€μ νκΈ°μ νΈλ¦¬ν©λλ€.
μ°κ΄κ΄κ³ μ’ λ₯μ μ λ΅
λ€λμΌ [N:1]
맀νμΌλ‘λ λ€λμΌ μλ°©ν₯
μΌλ‘ μ¬μ©νλ κ²μ μΆμ²ν©λλ€. 맨 μ²μ μμλ‘ λ€μλ νμκ³Ό λ°μ λν μ°κ΄κ΄κ³ entityλ₯Ό μ½λλ‘ μμ±νμ΅λλ€. μλ μ½λμμλ λ€λμΌ μλ°©ν₯ 맀ν
μ΄ λ μνμ
λλ€.
// νμ entity
@ManyToOne
@JoinColumn(name="TEAM_ID")
private Team team;
@JoinColumnμ λνμ¬
μΈλν€λ₯Ό 맀νν λ μ¬μ©ν©λλ€. @JoinColumn
μ μ°μ§ μμΌλ©΄ @JoinTable
μ λ΅μΌλ‘ λμνκΈ° λλ¬Έμ λΆνμν ν
μ΄λΈμ΄ νλ λ μκΈ°κ³ κ΄λ¦¬νκΈ°μ λΉν¨μ¨μ μ
λλ€.
μΌλλ€ [1:N]
λ€λμΌκ³Ό λ¬λ¦¬ mappedBy
μμ±μ΄ μμ΅λλ€. μ¬κΈ°μ μ°κ²°ν΄μ£Όλ λμμ μΌλλ€μ λ¬ΆμΈ μλ μ¦, μ¬κΈ°μλ @ManyToOne
μΌλ‘ μ€μ ν entity μμ±μ λ³μλͺ
μ μμ±ν΄μ£Όλ©΄ λ©λλ€.
// ν entity
@OneToMany(mappedBy="student")
@JoinColumn(name="STUDENT_ID")
private List<Student> students = new ArrayList<>();
μΌλμΌ [1:1]
μΌλμΌ μλ°©ν₯
μ λ€λμΌ μλ°©ν₯
맀νκ³Ό μ μ¬ν©λλ€. νμ§λ§ μΌλμΌμ΄κΈ° λλ¬Έμ μ΄λ λ°©ν₯μ μΈλν€λ₯Ό λ£μ΄λ λμ§λ§, ν₯ν κΈ°λ₯ μΆκ°μ DBAμμ νμ
μΌλ‘ μ΄λ»κ² μ£ΌμΈμ μ€μ ν μ§ κ³ λ―Όν΄λ΄μΌ ν©λλ€. μΌλ°μ μΌλ‘ μ ν΅μ μΈ λ°μ΄ν°λ² μ΄μ€ μ€κ³μ λμΌνκ² λμν
μ΄λΈμ μΈλ ν€λ₯Ό λ£λ λ°©μμΌλ‘ νλ©΄, λμ€μ μΌλλ€ λ§€νμΌλ‘ λ³κ²½μμλ ν
μ΄λΈ κ΅¬μ‘°κ° μ μ§λλ€λ μ₯μ μ΄ μμ΅λλ€. νμ§λ§, νλ‘μ κΈ°λ₯μ νκ³λ‘ μ§μ°λ‘λ©μΌλ‘ μ€μ ν΄λ νμ μ¦μλ‘λ©λλ€λ λ¨μ μ΄ μμ΅λλ€.
// ν entity
@OneToOne(mappedBy="student")
@JoinColumn(name="STUDENT_ID")
private Student student;
// νμ entity
@OneToOne
@JoinColumn(name="TEAM_ID")
private Team team;
λ€λλ€ [N:M]
κ΄κ³ν λ°μ΄ν°λ² μ΄μ€μμλ ννν μ μλ λ°©μμΌλ‘ μ€λ¬΄μμ μ¬μ©νμ§ μμ΅λλ€. μ€κ° ν
μ΄λΈμ λ§λ€μ΄ μΌλλ€ λλ λ€λμΌ κ΄κ³λ‘ λ§λ€μ΄μΌ ν©λλ€. νμ§λ§ μ€κ°ν
μ΄λΈμ λ°λ‘ κ΄λ¦¬νλ κ²λ³΄λ€ μ°¨λΌλ¦¬ μ€κ°ν
μ΄λΈμ entityλ‘ μΉκ²©ν΄μ κ΄λ¦¬νλ κ²μ κΆμ₯λ©λλ€.
@ManyToMany(fetch=LAZY)
@JoinTable(name="CATEGORY_ITEM",
joinColumn=@JoinColumn(name="CATEGORY_ID"),
inverseJoinColumns=@JoinColumn(name="ITEM_ID")
)
private List<Item> items = new ArrayList<>();
μ¦μλ‘λ©κ³Ό μ§μ°λ‘λ©
μ°κ΄κ΄κ³ μμ±μμ fetch μ λ΅μ fetchType.LAZY
μΌλ‘ μ§μ°λ‘λ©μΌλ‘ μ ννκ±°λ, fetchType.EAGER
μΌλ‘ μ¦μλ‘λ©μ μ€μ ν μ μμ΅λλ€. νμ§λ§ μ¦μλ‘λ©μ ν κ²½μ° μ±λ₯μ΄ λλΉ μ§κ±°λ μμμΉ λͺ»ν 쿼리λ€μ΄ λ°μνκΈ° λλ¬Έμ μ¬μ©μ νΌνλ κ²μ΄ μ’μ΅λλ€. @ManyToOne
, @OneToOne
μ κΈ°λ³Έκ°μ΄ μ¦μλ‘λ©μ΄λ―λ‘ @ManyToOne(fetch=fetchType.LAZY)
μ κ°μ΄ λ°λμ μ§μ°λ‘λ© μ€μ ν΄μ μνμ λ°©μ§ν΄μΌ ν©λλ€.
λμ JPQL fetch μ‘°μΈ
μ΄λ μν°ν° κ·Έλν κΈ°λ₯
μ μ¬μ©ν΄μ μ΄ λ¬Έμ λ₯Ό ν΄κ²°ν μ μμ΅λλ€.
μμμ± μ μ΄ CASCADE
νΉμ μν°ν°λ₯Ό μμ μνλ‘ λ§λ€ λ μ°κ΄λ μν°ν°λ ν¨κ» μμ μνλ‘ λ§λ€κ³ μΆμ λ μ¬μ©λ©λλ€. μ°κ΄κ΄κ³ 맀νκ³Όλ μλ¬΄λ° κ΄λ ¨μ΄ μμ§λ§ cascade=CascadeType.PERSIST
μ κ°μ΄ μ΄λ
Έν
μ΄μ
μ λ΅μΌλ‘ μμ±λκΈ° λλ¬Έμ μ¬κΈ°μ λΆλ₯νμ΅λλ€. CASECADE
μ’
λ₯μλ ALL
, PERSIST
, REMOVE
, MERGE
, REFRESH
, DETACH
κ° μμ΅λλ€. μμμ± μ μ΄κ° λ€λ₯Έ λΆλͺ¨μ μ°κ΄μ΄ μλ μμ μν°ν°μλ§ μ μ©νλ κ²μ΄ μ’μ΅λλ€.
κ³ μκ°μ²΄
κ³ μκ°μ²΄λ λΆλͺ¨ μν°ν°μ κ΄κ³κ° λμ΄μ§ μμ μν°ν°λ₯Ό λ§ν©λλ€. orphanRemoval = true
μ κ°μ΄ μ¬μ©λλλ° μ€μ ν λΆλͺ¨ μν°ν°μ μμ 리μ€νΈμμ νλλ₯Ό μμ νλ©΄, κ·Έ μμ μν°ν°μλ κ΄κ³λ₯Ό λ§Ίμ§ μμ΅λλ€. νΉμ μν°ν°κ° κ°μΈ μμ ν λλ§ μ¬μ©νλ κ²μ΄ μ’μ΅λλ€. CascadeType.REMOVE
μ κ°μ΄ λΆλͺ¨ μν°ν°κ° μμ λλ κ²½μ°, μμ μν°ν°λ κ°μ΄ μ κ±°λ©λλ€.
CASCADE
μ κ³ μκ°μ²΄λ₯Ό λͺ¨λ μ¬μ©νλ©΄ μμ μν°ν°μ μλͺ
μ£ΌκΈ°λ₯Ό λͺ¨λ κ΄λ¦¬ν μ μμ΅λλ€.(DDDμ Aggregate Root κ°λ
ꡬνμ μ μ©)
μμκ΄κ³ 맀ν
@Inheritance
μμκ΄κ³κ° μλ entityλ₯Ό λ§λ€λ, JPAλ extends
λ μν°ν°λ₯Ό λͺ¨λ νλμ λ¨μΌ ν
μ΄λΈλ‘ νλ μ λ΅μ κΈ°λ³ΈμΌλ‘ ν©λλ€. νμ§λ§ λ€λ₯Έ λ°©μμΌλ‘ μμ맀νμ μ²λ¦¬νκ³ μΆμ κ²½μ°μλ @Inheritance
μ λ€λ₯Έ μ λ΅μ μ¬μ©ν΄μ£Όλ©΄ λ©λλ€.
@Inheritance(strategy=InherianceType.JOINED) // μ‘°μΈμ λ΅
@Inheritance(strategy=InherianceType.SINGLE_TABLE) // λ¨μΌ ν
μ΄λΈ μ λ΅
@Inheritance(strategy=InherianceType.TABLE_PER_CLASS) // ꡬν ν΄λμ€λ§λ€ ν
μ΄λΈ μ λ΅
λ¨μΌ ν
μ΄λΈ μ λ΅μμλ μμλ ν
μ΄λΈμ λͺ¨λ 컬λΌμ κ°μ§κ³ μκΈ° λλ¬Έμ nullμ νμ©ν΄μΌ νλ λΆλΆμ΄ λ§λ€λ λ¨μ μ΄ μμ΅λλ€. ν
μ΄λΈ κ°μ κ΄κ³λ νμ₯μ κ³ λ €ν΄μ μ€μν μ€κ³κ° νμν κ²½μ°μλ JOIN
μ λ΅μ μ¬μ©νλ κ²μ΄ μ’μ΅λλ€. TABLE_PER_CLASS
μ λ΅μ μμ 맀νμ νμ
νκΈ°μ μ’μ§ μκΈ° λλ¬Έμ μ¬μ©νμ§ μλ κ²μ΄ μ’μ΅λλ€.
@DiscriminatorColumn
λΆλͺ¨ ν΄λμ€μμ ꡬλΆμ μν΄μ μ¬μ©ν©λλ€. λ¨μΌ ν
μ΄λΈ μ λ΅μμλ μλμΌλ‘ @DiscriminatorColumn
λ₯Ό μ¬μ©νμ§ μμλ ꡬλΆμ΄ λλ 컬λΌμΌλ‘ DTYPE
μ΄ μμ±λ©λλ€. νμ§λ§ JOIN
μ λ΅μμλ DTYPE
μ΄ μμ±λμ§ μκ³ νμ ν
μ΄λΈμ μ μ½μ‘°κ±΄μ΄ μμ±λκΈ° λλ¬Έμ λ³λμ ꡬλΆμ© 컬λΌμ΄ νμνμ§ μκΈ° λλ¬Έμ
λλ€.
@DiscriminatorValue
μμ ν΄λμ€μμ λΆλͺ¨ν΄λμ€ @DiscriminatorColumn
μ© μ»¬λΌμ λ€μ΄κ° κ°μΌλ‘ μμκ΄κ³μ 맀ν ν
μ΄λΈμ ꡬλΆνκΈ° μν΄ μ¬μ©ν©λλ€. μμν΄λμ€μ DTYPE
μ λ€μ΄κ° κ°μ μ§μ ν μ μμ΅λλ€.
@MappedSupperclass
κ³΅ν΅ λ§€ν μμ±μ 보λ₯Ό μ¬μ©νκ³ μΆμ λλ₯Ό μ¬μ©ν©λλ€. λͺ¨λ ν
μ΄λΈμ λμΌνκ² λκ° μμ νκ³ , μμ μΌμκ° μΈμ μΈμ§μ λν μ λ³΄κ° νμνλ€κ³ νλ€λ©΄, BaseEntity
λ₯Ό λ§λ€μ΄ 맀νμ λ³΄λ§ μ 곡ν μ μλλ‘ ν©λλ€. μ΄ classλ entityλ μλκ³ λ¨μν κ³΅ν΅ μμ±μ λΆμ¬νκΈ° μν κ²μΌλ‘ μμκ΄κ³ ν
μ΄λΈμ΄ μμ±λλ κ°λ
μ΄ μλλλ€.
μ½λλ‘ μ΄ν΄λ³΄κΈ°
μμ κ΄κ³μ λΆλͺ¨ν΄λμ€μ @Inheritance
μ @DiscriminatorColumn
λ₯Ό μ§μ ν΄μ μμ ν΄λμ€κ° μμλ μ μλλ‘ ν©λλ€. μ΄ λ, μμ ν΄λμ€μλ @DiscriminatorValue
λ‘ DTYPE
μ λ€μ΄κ° κ°μ μ ν΄μ£Όκ³ , extends
λ₯Ό νκΈ°ν΄ μμμμ λνλ
λλ€.
@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name = "dtype")
@Getter
@Setter
@Table(name = "ITEM")
public class Item {
@Id @GeneratedValue
@Column(name="item_id")
private Long id;
private String name;
private int price;
private int stockQuantity;
@ManyToMany(mappedBy = "items")
private List<Category> categories = new ArrayList<>();
}
@Entity
@DiscriminatorValue(value = "M")
@Getter
@Setter
public class Movie extends Item{
private String director;
private String actor;
}
νλ‘μμ μ°κ΄κ΄κ³
μ€μ ν΄λμ€λ₯Ό μμλ°μμ λ§λ€μ΄μ§ κ²μΌλ‘ νλ‘μλ₯Ό μ΄μ©ν΄μ μ‘°ννλ κΈ°λ₯μΈ em.getReference()
λ₯Ό μ¬μ©νλ κ²½μ° νλ‘μ κ°μ²΄λ₯Ό μ‘°ννλ©°, μμμ± μ»¨ν
μ€νΈμ μ΄λ―Έ entityκ° μλ€λ©΄ μ€μ entityλ₯Ό λ°νν©λλ€. μ°Έκ³ λ‘, em.find()
λ μ€μ κ°μ²΄λ₯Ό μ‘°νν©λλ€.
νλ‘μ κ°μ²΄μ μμμ± μ»¨ν
μ€νΈ
μ¬κΈ°μ νλ‘μ κ°μ²΄λ JPAμμ μ€μ λ°μ΄ν°λ² μ΄μ€ μ‘°νλ₯Ό μ§μ°ν μ μκ² νλ κ°μ§ κ°μ²΄μ
λλ€. νλ‘μ κ°μ²΄λ μ€μ κ°μ²΄λ₯Ό target λ³μλ‘ κ°μ§κ³ μκ³ , μ‘°νλ₯Ό νλ©΄ κ·Έ μ°Έμ‘°κ°μΌλ‘ μμμ± μ»¨ν
μ€νΈμκ² μ΄κΈ°ν μμ²μ 보λ΄μ μ€μ κ°μ²΄λ₯Ό μ‘°νν©λλ€. ν λ² μ‘°νλ μ΄νμλ target μ°Έμ‘°κ° κ±Έλ¦¬κΈ° λλ¬Έμ λ λ²μ§ΈλΆν°λ μ΄κΈ°ν μμ²μ νμ§ μμ΅λλ€.
μ 리νλ©΄, νλ‘μ κ°μ²΄λ₯Ό μ΄κΈ°ν ν λ, νλ‘μ κ°μ²΄κ° μ€μ μν°ν°λ‘ λ°λλ κ²μ μλλ©°, μ΄κΈ°νλλ©΄ νλ‘μ κ°μ²΄λ₯Ό ν΅ν΄μ μ€μ μν°ν°μ μ κ·Ό κ°λ₯μ΄ κ°λ₯ν©λλ€.
μ½λλ‘ μ΄ν΄λ³΄κΈ°
μ¬κΈ°μ m1, m2λ λͺ¨λ κ°μ νλ‘μ κ°μ²΄μμ μ‘°νν΄ μ¨ κ²μ μλ―Έν©λλ€. λ°λΌμ μ¬κΈ°μλ λλ±λΉκ΅(==)λ₯Ό ν΄λ true
κ° λμ€λ κ²μ μ μ μμ΅λλ€. νλ‘μ κ°μ²΄ νμ
μ λΉκ΅ν λλ instanceOf
λ₯Ό μ¬μ©ν΄μΌ ν©λλ€.
Member m1 = em.getReference(Member.class, "m1Id");
Member m2 = em.getReference(Member.class, "m1Id");
System.out.println(m1 == m2); // true
μ²μμλ νλ‘μ κ°μ²΄λ₯Ό μμ±νκ³ , getName()
μ‘°νλ₯Ό νλ μκ°, μμμ± μ»¨ν
μ€νΈμ μ΄κΈ°ν μμ²μΌλ‘ μ€μ κ°μ²΄λ₯Ό μ°Έμ‘°νκ² λ©λλ€. μ΄ν κ°μ κ°μ²΄λ₯Ό μ μΈνλ m2
μλ μ€μ κ°μ²΄λ₯Ό λ°λ‘ μ‘°ννκ² λ©λλ€. μ΄λ―Έ μμμ± μ»¨ν
μ€νΈμμ κ΄λ¦¬νλ κ°μ²΄μ΄κΈ° λλ¬Έμ
λλ€.
Member m1 = em.getReference(Member.class, "m1Id"); // νλ‘μ κ°μ²΄
m1.getName(); // μ΄κΈ°ν μμ²
Member m2 = em.getReference(Member.class, "m1Id"); // μ€μ κ°μ²΄
μλ° ORM νμ€ JPA νλ‘κ·Έλλ° - κΈ°λ³ΈνΈ
Proxy Objects and Eager & Lazy Fetch Types in Hibernate
[JPA] μμμ± μ μ΄, κ³ μ κ°μ²΄ (cascade λ²μ)
OSIV
μ€νλ§λΆνΈμμλ Open Session In Viewλ₯Ό κΈ°λ³Έκ°μΌλ‘ νκ³ μκΈ° λλ¬Έμ μμμ± μ»¨ν
μ€νΈλ₯Ό λ·° λ λλ§μ΄ λλλ μμ κΉμ§ κ°λ°©ν μνλ‘ μ μ§ν©λλ€.
μ€μμ μνμμλ νλ‘μλ₯Ό μ΄κΈ°ννλ©΄ hibernateλ org.hibernate.LazyInitializationException
μμΈκ° λ°μν μ μμ΅λλ€. μ΄ λ¬Έμ λ μ€νλ§ @Transaction
μ μ¬μ©νλ κ³Όμ μμ λ°μνλλ°, μλΉμ€λ¨μμ λ°μ entityλ₯Ό 컨νΈλ‘€λ¬μμ mapperλ‘ DTO μ²λ¦¬νλ κ³Όμ μμ νν λ³Ό μ μλ λ¬Έμ μ
λλ€. @Transaction
μ΄ λͺ
μλ μλΉμ€λ¨ λ©μλκ° μ’
λ£λλ©΄, μμμ± μ»¨ν
μ€νΈμμ κ·Έ κ°μ²΄λ₯Ό κ΄λ¦¬νμ§ μκΈ° λλ¬Έμ μ€μ κ°μ²΄λ‘ mapper μ²λ¦¬λ₯Ό μ λλ‘ νμ§ λͺ»νλ λ¬Έμ κ° λ°μνκΈ° λλ¬Έμ
λλ€.
μ€μ ! μ€νλ§ λΆνΈμ JPA νμ©2 - API κ°λ°κ³Ό μ±λ₯ μ΅μ ν
λ°μ΄ν° νμ
JPA νμ
μ μν°ν° νμ
(@Entity
μλ³μλ‘ μΈμ)κ³Ό κ° νμ
(μλ³μ μμ΄ κ°λ§ μλ μΆμ λΆκ°λ₯ν νμ
)μΌλ‘ λΆλ₯ν μ μμ΅λλ€. κ° νμ
μ μΆμ μ΄ λΆκ°λ₯νκΈ° λλ¬Έμ μ λ 곡μ λλ©΄ μλ©λλ€. μ¦, Interger, String κ°μ 곡μ κ°λ₯ν μ£Όμκ°λ§ λμ΄κ°κΈ° λλ¬Έμ λ³κ²½μ΄ λΆκ°λ₯ν©λλ€. JPAμμ κ° νμ
μ μ’
λ₯λ‘λ κΈ°λ³Έκ° νμ
, μλ² λλ νμ
, 컬λ μ
κ° νμ
μ΄ μμ΅λλ€.
κ° νμ
μ μμνμ
μΈ κ²½μ°λ λμΌμ± λΉκ΅λ‘ ==
λ₯Ό μ¬μ©νλ©΄ λμ§λ§, κ·Έ μΈμ μλ² λλ νμ
κ³Ό κ°μ κ°μ λΉκ΅ν λλ equals()
λ₯Ό μ¬μ©ν΄μ λλ±μ± λΉκ΅λ₯Ό ν΄μΌ ν©λλ€.
immutableν κ°μ²΄μ λν΄ λ μμΈν μμ보μ!
Javaμμ Integerλ Stringμ immutableν κ°μ²΄μ
λλ€. μ΄λ¬ν κ°μ²΄λ€μ 곡μ κ°λ₯ν μ£Όμκ°λ§ λμ΄κ°κΈ° λλ¬Έμ λ³κ²½μ΄ λΆκ°λ₯ν©λλ€. μλ₯Ό λ€μ΄, Integer a = 5; μ Integer b = a; λΌλ μ½λκ° μλ€λ©΄, aμ bλ κ°μ μ£Όμκ°μ 곡μ νκ² λ©λλ€. λ°λΌμ aλ b μ€ νλλ₯Ό λ³κ²½νλ©΄ λ€λ₯Έ νλλ ν¨κ» λ³κ²½λλ κ²μ΄ μλλΌ, μλ‘μ΄ κ°μ²΄κ° μμ±λ©λλ€. JPAμ μν°ν° μΌλΆλ‘ μ¬μ©λ μ μλ κ° νμ
μ
λλ€.
μλ² λλ νμ
(λ³΅ν© κ° νμ
)
μλ² λλ νμ
λ μν°ν° νμ
μ΄ μλκΈ° λλ¬Έμ μΆμ μ΄ λΆκ°λ₯ν©λλ€. Member μν°ν°μμ Address κ°μ²΄λ₯Ό κ° νμ
μ²λΌ μ¬μ©ν μ μμ΅λλ€. @Embedded
μΌλ‘ μλ² λλ νμ
μ κ°μμ λͺ
μνκ³ , @Embeddable
λ‘ ν΄λμ€κ° μλ² λλ νμ
μμ λͺ
μν©λλ€. νμ
μμλ μ£Όμλ μ°λ½μ²μ κ°μ΄ μν°ν°μμ μμ£Ό μ¬μ©λλ κ°λ€μ μλ² λλ νμ
μΌλ‘ μ μνμ¬ μ¬μ©ν©λλ€. μλ₯Ό λ€μ΄, νμ(Member) μν°ν°μμλ νμμ μ΄λ¦, λμ΄, μ£Όμ, μ°λ½μ² λ±μ΄ μμ£Ό μ¬μ©λλλ°, μ΄λ¬ν κ°λ€μ μλ² λλ νμ
μΌλ‘ μ μνμ¬ Member μν°ν°μμ μ¬μ©ν μ μμ΅λλ€.
κ° νμ
μ λ³λμ ν
μ΄λΈλ‘ λΆλ¦¬νμ¬ μ μ₯νλ©΄ μ‘°μΈμ΄ λ°μνλ―λ‘ μ±λ₯μ΄ μ νλ μ μμ΅λλ€. μ΄λ¬ν κ²½μ°μλ μλ² λλ νμ
μ μ¬μ©νλ©΄ Member ν
μ΄λΈκ³Ό ν¨κ» μ μ₯λλ―λ‘ μ±λ₯μ μ΄μ μ΄ μμ΅λλ€. λν, μλ² λλ νμ
μ μ¬μ©νλ©΄ μ½λμ κ°λ
μ±μ΄ μ’μμ§κ³ , κ°μ²΄λ₯Ό λ€λ£¨λ μ½λκ° κ°κ²°ν΄μ§λλ€. λ°λΌμ νμ
μμλ μλ² λλ νμ
μ μμ£Ό μ¬μ©νλλ°, μ΄λ κ°μ²΄μ§ν₯μ μΈ μ€κ³λ₯Ό ν μ μκ² ν΄μ£ΌκΈ° λλ¬Έμ
λλ€.
μλ μ½λλ‘ λ³΄λ©΄, Address κ°μ²΄λ₯Ό λ°λ‘ ν
μ΄λΈλ‘ λ§λ€μ§ μκ³ Member ν
μ΄λΈκ³Ό ν¨κ» κ°κ°μ 컬λΌμΌλ‘ μμ±λμ΄ μ μ₯λ©λλ€.
@Entity
public class Member {
@Id
private Long id;
private String name;
@Embedded
private Address address;
// ...
}
@Embeddable
public class Address {
private String city;
private String street;
private String zipcode;
// ...
}
μλ² λλ νμ
μμ @Embeddable
κ°μ²΄λ₯Ό 곡μ μ°Έμ‘°νλ κ²½μ°, κ·Έ κ°μ²΄λ₯Ό μ¬μ©νλ entityλ€μμ λͺ¨λ λ³κ²½μ΄ λ°μνλ λ¬Έμ κ° λ°μν μ μμ΅λλ€. μ΄ κ²½μ° μΆμ μ΄ μ΄λ ΅κΈ° λλ¬Έμ λ°λμ 곡μ μ°Έμ‘°λ₯Ό νλ €λ λμμ λΆλ³κ°μ²΄λ‘ μμ±ν΄μΌ ν©λλ€. setter
λ©μλλ₯Ό μ¬μ©νμ§ μμμΌ νλ©°, λ³κ²½ν νμκ° μλ κ²½μ°μλ μλ‘μ΄ κ°μ²΄λ₯Ό λ€μ λ§λ€μ΄μ κ°μ μμ νλ λ°©μμ΄ μμ ν©λλ€.
[λΆλ³κ°μ²΄λ‘ μ€μ νμ§ μμ κ²½μ°μ λ¬Έμ μ ]
Address address = new Address("city", "street", "10000");
Member member1 = new Member();
member1.setName("member1");
member1.setHomeAddress(address);
Member member2 = new Member();
member2.setName("member2");
member2.setHomeAddress(address);
em.persist(member1);
em.persist(member2);
address.setCity("seoul");
// member1κ³Ό member2 λͺ¨λ addressμ cityκ° 'seoul'λ‘ λ³κ²½λ©λλ€.
[λΆλ³κ°μ²΄λ‘ μ€μ νμ λμ μλ² λλ νμ μ κ° λ³κ²½]
Address address = new Address("city", "street", "10000");
Member member1 = new Member();
member1.setName("member1");
member1.setHomeAddress(address);
em.persist(member1);
// μμ μ΄ νμν κ²½μ°
Address newAddress = new Address("seoul", address.getStreet(), address.getZipcode());
member1.setHomeAddress(newAddress);
BaseEntityλ₯Ό μ¬μ©νλ©΄ λμ§ μμκΉ?
μ¬κΈ°μ μ΄λ° μλ¬Έμ΄ λ€μμ΅λλ€. κ³΅ν΅ μμ±μ΄λΌλ©΄ BaseEntity
λ‘ κ΄λ¦¬νλ λ°©λ²λ μλλ°, 컬λΌμ λν μλ² λλ κ° νμ
μΌλ‘ μ€μ νλκ² μ΄λ€ λΆλΆμμ ν¬κ² λ€λ₯Έμ§ μκ°ν΄λ΄€μ΅λλ€.
@Embedded
μ @Embeddable
μ μ¬μ©νλ©΄ νλμ μν°ν°μμ μ¬λ¬ κ°μ κ°μ κ·Έλ£Ήνν μ μμ΅λλ€. μ΄λ κ² κ·Έλ£Ήνλ κ°μ λ€λ₯Έ μν°ν°μμλ μ¬μ¬μ©ν μ μμ΅λλ€. λν, κ° νμ
μ λ³λμ ν
μ΄λΈλ‘ λΆλ¦¬νμ¬ κ΄λ¦¬ν μ μκΈ° λλ¬Έμ λ°μ΄ν° μΌκ΄μ±κ³Ό μ€λ³΅μ±μ μ€μΌ μ μμ΅λλ€.
κ°λ
μ μΈ λΆλΆμμλ BaseEntity
λ μμμ νλ κ°λ
μΌλ‘ κ³΅ν΅ μ»¬λΌλ€μ μ μν©λλ€. νμ§λ§ @Embedded
λ₯Ό μ΄μ©νλ건 μ£ΌμλΌλ μ»¬λΌ λ΄μ μ°νΈλ²νΈ, κΈ°λ³Έμ£Όμ, μμΈμ£Όμ
μ κ°μ νλμ κ·Έλ£Ήνλ μμ±λ€μ μ§μ νλ€λ μ μμ μ°¨μ΄κ° μμ΅λλ€. κ²°λ‘ μ μΌλ‘ μ¬λ¬ μν°ν°μ κ³΅ν΅ μμ±μ λ¬Άμμ κ΄λ¦¬νκΈ° μν΄μλ μμ κ°λ
μ μ΄μ©νλ κ²μ΄ λ°λμ§νκ³ , νλμ μμ±μ μΈλΆ μμ±λ€μ κ·Έλ£Ήνν΄μ μ¬μ©νκΈ° μ’μ κ²μ μλ² λλ κ° νμ
μ μ¬μ©νλ κ²μ΄ μ μ ν©λλ€.
컬λ μ
κ° νμ
κ° νμ
μ νλ μ΄μ μ μ₯ν λ μ¬μ©λλ©°, μλ°μ 컬λ μ
μ μ¬μ©ν©λλ€. κ° νμ
컬λ μ
μ CASCADE
λ₯Ό κΈ°λ³Έκ°μΌλ‘ κ°κ³ μμΌλ©°, μμμ± μ μ(Cascade) + κ³ μ κ°μ²΄ μ κ±° κΈ°λ₯μ νμλ‘ κ°μ§λ€κ³ λ³Ό μ μμ΅λλ€. λ§μ½ κ° νμ
컬λ μ
μ λ³κ²½ μ¬νμ΄ λ°μνλ©΄, μ£ΌμΈ μν°ν°μ μ°κ΄λ λͺ¨λ λ°μ΄ν°λ₯Ό μμ νκ³ , κ° νμ
컬λ μ
μ μλ νμ¬ κ°μ λͺ¨λ λ€μ μ μ₯ν©λλ€.
@ElementCollection
μ μ¬μ©ν΄μ 컬λ μ
νμ
μ μ¬μ©ν μ μλλ°, μλμΌλ‘ λΆλͺ¨ idλ₯Ό κΈ°λ³Έν€λ‘ κ°λ ν
μ΄λΈμ΄ member_roles
λΌλ ν
μ΄λΈμ΄ μλμΌλ‘ μμ±λ©λλ€. @CollectionTable
μΌλ‘ 컬λ μ
κ° νμ
μ μ¬μ©λ ν
μ΄λΈ μμ±μ λͺ
μμ μΌλ‘ μ§μ ν΄ μ€ μ μμ΅λλ€.
@Entity
public class Member {
@Id
@GeneratedValue
@Column(name = "member_id")
@Setter
private Long id;
@Column
@ElementCollection
private List<String> roles = new ArrayList<>();
@ElementCollection
@CollectionTable(name="OPTIONS", joinColumns=@JoinColumn(name="MEMBER_ID"))
private List<String> options = new ArrayList<>();
}
컬λ μ κ° νμ μ μ§μ°λ‘λ© μ λ΅μ μ¬μ©νκΈ° λλ¬Έμ 컬λ μ κ° νμ μ μ‘°ννλ λ± νμν μκ°μ μΏΌλ¦¬κ° μμ±λ©λλ€.
Membmer member = em.find(Member.class, member.getId()); // memberλ§ μ‘°ν
member.getRols(); // 컬λ μ
κ° νμ
μ‘°ν
JPA νμ μ λν΄ μ΄κ²λ§μ μκ³ κ°μ!
- μλ² λλ νμ μ 컬λ μ κ° νμ μΌλ‘ ν¨κ» νΌμ© μ¬μ©ν μ μμ΅λλ€.
- νμ
μμλ 컬λ μ
κ° νμ
μ΄ μμ£Ό κ°λ¨ν μμμ΄ μλ κ²½μ°,
μΌλλ€
λλλ€λμΌ
μ°κ΄κ΄κ³λ‘ λ³κ²½ν΄μ μ¬μ©ν΄ μν°ν° μΆμ μ μ©μ΄νκ² μ¬μ©ν©λλ€. - μΆμ μ΄ λΆκ°λ₯ν μλ² λλλ 컬λ μ
κ° νμ
μ μ¬μ©ν κ²½μ° λ°λμ
λΆλ³κ°μ²΄
λ‘ λ§λ€μ΄μ μ¬μ©ν΄μΌ ν©λλ€.
μλ° ORM νμ€ JPA νλ‘κ·Έλλ° - κΈ°λ³ΈνΈ
[JPA] κ° νμ
κ³Ό λΆλ³ κ°μ²΄ - κ° νμ
(2)
[JPA] κ° νμ
컬λ μ
: @ElementCollection, @CollectionTable
[Spring JPA] @Embedded, @Embeddable
κ°μ²΄μ§ν₯ 쿼리 μΈμ΄
JPQL, JPA Criteria, Query DSL, λ€μ΄ν°λΈ SQL, JDBC APIλ₯Ό μ¬μ©νλ λ± λ€μν 쿼리방λ²μ μ§μν©λλ€.
μμμ± μ»¨ν
μ€νΈ λκΈ°νκ° νμν κ²½μ°
JPAμ ν¨κ» JDBC APIλ MyBatisλ₯Ό μ¬μ©νλ κ²½μ°μλ μμμ± μ»¨ν
μ€νΈλ₯Ό λͺ
μμ μΌλ‘ flush()
ν΄μ€μΌ ν©λλ€. μλνλ©΄, μμ§ DBμ λ°μλμ§ μμ λ°μ΄ν°λ₯Ό JPAλ₯Ό μ°νν΄μ μ‘°ννκΈ° λλ¬Έμ λ¬΄κ²°μ± λ¬Έμ κ° λ°μν©λλ€. λ°λΌμ μ°ν μ κ·Ό μ λ°λμ μμμ± μ»¨ν
μ€νΈ λκΈ°ν μ²λ¦¬κ° νμν©λλ€.
JPQL
Java Persistence Query Language
JPAμμ μ¬μ©νλ 쿼리 μΈμ΄λ‘ μν°ν° κ°μ²΄λ₯Ό λμμΌλ‘ 쿼리λ₯Ό μμ±ν©λλ€. SQLκ³Ό μ μ¬νμ§λ§ entity κ°μ²΄λ₯Ό λμμΌλ‘ 쿼리νλ€λ νΉμ§μ΄ μμ΅λλ€. JPQLμ SQLμ μΆμνν΄μ νΉμ λ°μ΄ν°λ² μ΄μ€ SQLμ μμ‘΄νμ§ μμ΅λλ€.
TypedQuery
λ λ°ννμ
μ΄ λͺ
νν κ²½μ°μ μ¬μ©λκ³ , λͺ
ννμ§ μμ κ²½μ°λ κ·Έλ₯ Query
λ‘ μμ±ν©λλ€.
// νλΌλ―Έν° μ΄λ¦μ μ΄μ©ν λ°μΈλ©
TypedQuery<User> query = entityManager.createQuery("SELECT u FROM User u WHERE u.age > :age ORDER BY u.name DESC", User.class);
query.setParameter("age", 20);
List<User> users = query.getResultList();
// μμΉ κΈ°λ° νλΌλ―Έν°λ₯Ό μ¬μ©ν λ°μΈλ©
Query<Member> query = entityManager.createQuery("SELECT m FROM Member m WHERE age=?1");
query.setParameter(1, 20);
νλ‘μ μ
νλ‘μ μ
μ JPQLμμ SELECTμ μμ μ΄λ€ λ°μ΄ν°λ₯Ό μ‘°νν κ²μΈκ°λ₯Ό λ§ν©λλ€. μ‘°ννλ λ°μ΄ν° μμ±μ λ°λΌ κ°μ²΄ νλ‘μ μ
, Embedded νλ‘μ μ
, μ€μΉΌλΌ νλ‘μ μ
μΌλ‘ ꡬλΆλ©λλ€.
SELECT m.name FROM Member m
νμ΄μ§
쿼리λ₯Ό μμ±ν λ κ²°κ³Ό κ°μ μ²μ setFirstResult
κ³Ό λ§μ§λ§ setMaxResults
μ μ€μ νκ³ νμ΄μ§μ²λ¦¬ν΄μ Listλ‘ νμ΄μ§ μ²λ¦¬λ κ°μ κ°μ§κ³ μ¬ μ μμ΅λλ€.
List<Member> query = entityManager.createQuery("SELECT m FROM Member m WHERE age=:age")
.setFirstResult(0)
.setMaxResults(10)
.getResultList();
query.setParameter("age", 20);
μ‘°μΈ
μ‘°μΈμλ λ΄λΆμ‘°μΈ, μΈλΆμ‘°μΈ, μΈνμ‘°μΈμ΄ μμ΅λλ€. μΈνμ‘°μΈ
μ μ°κ΄κ΄κ³ μλ λ ν
μ΄λΈμ μ‘°μΈνκ³ μΆμ λ μ¬μ©ν©λλ€. μ΄ λ μ‘°μΈμ κ°μ FULL JOIN
μ²λΌ μ‘°μΈλκ³ κ±°κΈ°μ 쑰건μ μ ν΄λΉνλ κ°μ λμΆν©λλ€.
μλΈμΏΌλ¦¬
EXISTS
, ALL
, ANY
, IN
κ³Ό κ°μ κΈ°λ₯μ μ 곡ν©λλ€. νμ΄λ²λ€μ΄νΈλ SELECT
μμλ μλΈμΏΌλ¦¬λ₯Ό μ¬μ©ν μ μλλ‘ μ§μν©λλ€. JPA νμ€ μ€νμ¬νμ WHERE
κ³Ό HAVING
μ μμλ§ μ¬μ©μ΄ κ°λ₯ν©λλ€. νμ§λ§ FROM
μ μμ μλΈμΏΌλ¦¬κ° λμ§ μμ΅λλ€.
ENUM
νμ
ν¨ν€μ§λͺ
μ ν¬ν¨ν΄μ jpql.MemberType.ADMIN
μ κ°μ΄ λμ
νκ±°λ νλΌλ―Έν° λ°μΈλ©μΌλ‘ 쑰건μ μΆκ°κ° κ°λ₯ν©λλ€.
쑰건문
COALESCE
λ₯Ό μ¬μ©νλ©΄ NULLμ΄ μλ κ²μ λ°νν μ μμ΅λλ€. SELECT COALESCE(m.name, 'μ΄λ¦μ΄ μλ€') FROM Member m
μ κ°μ΄ μ¬μ©λ©λλ€. μ΄λ¦μ΄ μλ κ²½μ°λ βμ΄λ¦μ΄ μλ€βλ‘ μΆλ ₯λ©λλ€.
NULLIF
λ μνλ κ°μΈ κ²½μ°λ NULLμ λ°νν©λλ€. SELECT NULLIF(m.name, 'νκΈΈλ') FROM Member m
μ¬κΈ°μ μ΄λ¦μ΄ νκΈΈλμΌ κ²½μ° NULLμ λ°νν©λλ€.
μ¬μ©μ μ μ ν¨μ
μ¬μ© μ μ¬μ©νλ DBμ μμλ°κ³ λ±λ‘ν΄μΌ μ¬μ©ν μ μμ΅λλ€. JPQLλ‘ MySQLμ μ¬μ©μ μ μ ν¨μλ₯Ό μ΄λ»κ² μ¬μ©νλμ§ μμλ³΄κ² μ΅λλ€. λ¨Όμ , MySQLμ μ¬μ©μ μ μν¨μλ₯Ό λ±λ‘νκΈ° μν΄μλ λ€μκ³Ό κ°μ΄ ν¨μλ₯Ό μμ±ν΄μ€λλ€.
CREATE FUNCTION 'ν¨μλͺ
' (
νλΌλ―Έν°
) RETURNS λ°νν λ°μ΄ν°νμ
BEGIN
μνν 쿼리
RETURN λ°νν κ°
END
μ¬μ©μ μ μ ν¨μλ μ¬μ©νκΈ° μν΄ com.example.MySqlFunctions
μ¬μ©μ μ μ ν¨μκ° ν¬ν¨λ ν¨ν€μ§λ₯Ό κ°μ§κ³ λ±λ‘ν ν¨μλͺ
κ³Ό νλΌλ―Έν°λ₯Ό λ£μ΄μ 쿼리λ₯Ό μμ±νλ©΄ λ©λλ€.
TypedQuery<Employee> query = entityManager.createQuery(
"SELECT e FROM Employee e WHERE FUNCTION('com.example.MySqlFunctions.my_custom_function', e.salary) > 50000",
Employee.class
);
List<Employee> employees = query.getResultList();
κ²½λ‘ννμ
κ²½λ‘ννμμ κ°μ²΄ κ·Έλν νμμ νκΈ° μν ννμμ
λλ€. μννλ
λ κ°μ μ μ₯νκΈ° μν νλλ₯Ό λ§ν©λλ€. μ°κ΄νλ
λ λ¨μΌ κ° μ°κ΄ νλλ‘ λ¬΅μμ λ΄λΆ μ‘°μΈμ΄ λ°μνκΈ° λλ¬Έμ μ€λ¬΄μμ λ¬Έμ κ° λ°μν μ μμ΅λλ€. λλλ‘ λͺ
μμ μ‘°μΈμΌλ‘ κ΄λ¦¬ν μ μλ μΏΌλ¦¬λ‘ λ§λλ κ²μ΄ μ€μν©λλ€.
SELECT o.customer.name, oi.quantity FROM Order o JOIN o.orderItems oi WHERE o.id = :orderId
ν¨μΉ μ‘°μΈ(fetch join)
JPQLμμ μ±λ₯ μ΅μ νλ₯Ό μν΄ μ 곡νλ κΈ°λ₯μΌλ‘ JPA μ¦μλ‘λ©μ²λΌ κ°μ²΄ κ·Έλνλ₯Ό SQL ν λ²μ μ‘°ννλ κ°λ
μ
λλ€. μλ₯Ό λ€μ΄ νμμ μ‘°νν λ, μ°κ΄λ νμ κ°μ΄ μ‘°ννκ³ μΆμ κ²½μ° λ€μκ³Ό κ°μ΄ 쿼리λ₯Ό μμ±ν μ μμ΅λλ€.
select m from Member m join fetch m.team
- ν¨μΉμ‘°μΈμ λμμΈ
team
μ λ€ κ°μ Έμ€λλ°, λ³μΉμ μ΄μ©ν΄μ μνλ κ°λ§ κ°μ§κ³ μ¬ μκ° μλ€λ λ¨μ μ΄ μμ΅λλ€. - λ μ΄μμ 컬λ μ
μ
Cartesian Product
μ λ§λ€μ΄ λ΄κΈ° λλ¬Έμ μ€λ³΅μμκ° λ§μ΄ λ°μν μ μμ΅λλ€. μ΄λ₯Ό ν΄κ²°νκΈ° μν΄μλLEFT JOIN FETCH
λ‘ ν¨μΉ μ‘°μΈνλ λ°©λ²μΌλ‘ μ€λ³΅ λ°μ΄ν°λ₯Ό μ κ±°ν μ μμ΅λλ€. - 컬λ μ
μ ν¨μΉ μ‘°μΈνλ©΄
νμ΄μ§ API
λ₯Ό μ¬μ©ν μ μλ€λ λ¨μ μ΄ μμ΅λλ€. λͺ¨λ λ°μ΄ν°λ₯Ό κ°μ Έμ€κ² λλ―λ‘, μ΄ λ°μ΄ν°λ₯Ό APIμμ νμ΄μ§ λ¨μλ‘ μ²λ¦¬νκΈ° μ΄λ €μΈ μ μμ΅λλ€.
λ€νμ± μΏΌλ¦¬
λΆλͺ¨ ν΄λμ€ νμ
μΌλ‘ μ μΈλ λ³μμ λν΄μλ μμ ν΄λμ€μ λ°μ΄ν°λ₯Ό κ²μν μ μμ΅λλ€. μμκ΄κ³λ₯Ό 미리 @DiscriminatorColumn
λ‘ μ§μ ν΄ λ μνμ΄κΈ° λλ¬Έμ TYPE
μ μ΄μ©ν΄μ νΉμ μμ νμ
μ μ κ·Όν μ μμ΅λλ€.
SELECT a FROM Animal a WHERE TYPE(a) IN (Dog, Cat)
TREAT
μ μ¬μ©νλ©΄ λΆλͺ¨νμ
μ νΉμ μμ νμ
μΌλ‘ λ€λ£¨λ λ°©λ²μΌλ‘ μ¬μ©μ΄ κ°λ₯ν©λλ€. μλ μ½λμμ 보면 Animal
λ³μΉμ μ΄μ©ν΄μ Dog
μ λμ΄κ° 2μ΄λ³΄λ€ λ§μ κ°μ²΄λ₯Ό ꡬν μ μμ΅λλ€.
SELECT a FROM Animal a WHERE TREAT(a as Dog).age > 2
Named 쿼리
μ μ μΏΌλ¦¬λ‘ λ―Έλ¦¬ μ μν΄λκ³ μ΄λ¦μ κ°μ§κ³ μ¬μ©ν μ μλ 쿼리μ
λλ€. μ ν리μΌμ΄μ
λ‘λ© μμ μ μ΄κΈ°ν ν μ¬μ¬μ©νκ³ λ‘λ©μμ μ 쿼리λ₯Ό κ²μ¦ν μ μλ€λ μ₯μ μ΄ μμ΅λλ€. ν΄λμ€ μ체μ μ μν μλ μκ³ XMLμ μ μν μλ μμ΅λλ€.
@Entity
@NamedQuery(name = "Person.findByAge", query = "SELECT p FROM Person p WHERE p.age = :age")
public class Person {
@Id
private Long id;
private String name;
private int age;
// getters, setters, constructors
}
TypedQuery<Person> query = em.createNamedQuery("Person.findByAge", Person.class);
query.setParameter("age", 30);
List<Person> persons = query.getResultList();
λ²ν¬μ°μ°
λ²ν¬μ°μ°μ νκ² λλ©΄ μμμ± μ»¨ν
μ€νΈλ₯Ό 무μνκ³ λ°μ΄ν°λ² μ΄μ€μ μ§μ 쿼리νκΈ° λλ¬Έμ λ²ν¬μ°μ° ν μμμ± μ»¨ν
μ€νΈλ₯Ό μ΄κΈ°ννλ κ²μ΄ μ€μν©λλ€.
Query query = em.createQuery("UPDATE Person p SET p.age = :newAge WHERE p.age < :oldAge");
query.setParameter("newAge", 40);
query.setParameter("oldAge", 30);
int updatedCount = query.executeUpdate();
Querydsl
Querydslμ μμμ HQLμ λλ©μΈ νμ
κ³Ό λ¬Έμμ΄ νμ
μμ μ± μ΄μλ₯Ό ν΄κ²°νλ κ²μμ λΉλ‘―λμ΄ μ§κΈμ JPA, JDO, JDBC, MongoDB λ± λ°±μλ μ§μμ μν κΈ°μ λ‘ λ°μ νμ΅λλ€. νμ
μμ μ±κ³Ό μΌκ΄μ±μ΄ Querydslμ μ€μν μ리μ
λλ€.
QueryDSLμ λ€μκ³Ό κ°μ΄ μλ° μ½λλ‘ μΏΌλ¦¬λ₯Ό μμ±νκΈ° λλ¬Έμ μ»΄νμΌ νμμ μ€λ₯λ₯Ό νμΈν μ μμ΅λλ€. λ°λΌμ λ°νμ μ€λ₯κ° λ°μν κ°λ₯μ±μ΄ μ μ΅λλ€. κ·Έλ¦¬κ³ JPQLμ λΉν΄ κ°λ
μ±μ΄ λκ³ , μν°ν° λ³μΉκ³Ό μμ± λ±μ μλμΌλ‘ μμ±ν΄μ€λ€λ μ₯μ μ΄ μμ΅λλ€. λμ 쿼리 μ¬μ©μλ μ ν©ν©λλ€.
Querydsl μ€λΉνκΈ°
μμνκΈ° μμ gradle μ€μ μ΄ νμν©λλ€. κΈ°μ‘΄μ spring web νλ‘μ νΈλ₯Ό jpaλ₯Ό μ΄μ©ν΄μ μμ
νκ³ μμλ κ²μ κΈ°μ€μΌλ‘ μΆκ°ν΄μΌν λ΄μ©μ μλμ κ°μ΅λλ€. μ΄ μ½λλ Querydslμ μ¬μ©νκΈ° μν νλ¬κ·ΈμΈ, μμ‘΄μ± μΆκ° λΌμ΄λΈλ¬λ¦¬, λΉλλ₯Ό μν μ€μ λ΄μ©μ λ΄κ³ μμ΅λλ€.
// build.gradle
plugins {
id "com.ewerk.gradle.plugins.querydsl" version "1.0.10"
id 'java'
}
dependencies {
implementation 'com.querydsl:querydsl-jpa'
}
def querydslDir = "$buildDir/generated/querydsl"
querydsl {
jpa = true
querydslSourcesDir = querydslDir
}
sourceSets {
main.java.srcDir querydslDir
}
configurations {
querydsl.extendsFrom compileClasspath
}
compileQuerydsl {
options.annotationProcessorPath = configurations.querydsl
}
μ¬κΈ°μ κ²μ¦μ© Qνμ
μ μμ±νκΈ° μν΄μλ Gradle-Task-other-compileQuerydsl
μ€ν ν build-generated-querydsl
μμ μμ±λ κ²μ νμΈν μ μλ€. κ²μ¦μ© Qνμ
μ μ€λ¦¬μ§λ νμ
μ public
μμ±μ λ΄κ³ μλ μΏΌλ¦¬μ© νμ
μΌλ‘ λ€μκ³Ό κ°μ΄ μ¬μ©λ©λλ€.
Querydslμμ μ¬μ©λλ queryλ₯Ό 보기 μν΄μλ application.yml
μ€μ μΌλ‘ νμΈμ΄ κ°λ₯ν©λλ€.
spring.jpa.properties.hibernate.use_sql_comments: true
Querydslμ μ¬μ©νλ λ°©λ²
Qνμ
κ°μ²΄λ₯Ό μ¬μ©νλ λ°©λ²μ λ³μΉμ μ¬μ©νκ±°λ κΈ°λ³Έ μΈμ€ν΄μ€λ₯Ό μ¬μ©νλ λ°©λ²μΌλ‘ λλ©λλ€. νμ§λ§ μμ μμμ²λΌ μλμΌλ‘ Querydslμμ μ 곡νλ κΈ°λ³Έ μΈμ€ν΄μ€λ₯Ό μ¬μ©ν΄λ λ©λλ€.
QCustomer customer = new QCustomer("myCustomer");
μ¬κΈ°μ Qνμ
κ°μ²΄μΈ QCustomer
λ κΈ°λ³ΈμΌλ‘ customer
λ‘ μ 곡λλ©°, μ΄ν Querydsl μμμλ λμΌνκ² μ¬μ©μ΄ κ°λ₯ν©λλ€.
JPAQueryFactory queryFactory = new JPAQueryFactory(entityManager);
List<Customer> customers = queryFactory.selectFrom(customer)
.where(customer.age.gt(20))
.orderBy(customer.name.desc())
.fetch();
Querydsl ν΅μ¬λ¬Έλ²
Querydslμ JPQLκ³Ό λ§μ°¬κ°μ§λ‘ SQL κΈ°λ₯ λλΆλΆμ μ 곡νκ³ μκ³ , νΈμλ₯Ό μν΄ μΆκ°λλ κΈ°λ₯λ€κ³Ό λ¬Έλ²μ΄ μλλ°, κΈ°λ³Έμ μΈ λ¬Έλ²μ μ μΈνκ³ λͺ κ°μ§ λ¬Έλ²λ§ κΈ°μ΅μ νκΈ° μν΄ μ 리ν΄λ³΄μμ΅λλ€.
κ²μ 쑰건 쿼리
customer.age.goe(30) // age >= 30
customer.age.gt(30) // age > 30
customer.age.loe(30) // age <= 30
customer.age.lt(30) // age < 30
customer.username.like("jane%") // like κ²μ
customer.username.contains("e") // like %member% κ²μ
customer.username.startswith("j") // like member% κ²μ
μ‘°μΈ μΏΌλ¦¬
μ‘°μΈ μΏΌλ¦¬μμλ innerJoin()
, leftJoin()
λ± λͺ¨λ μ§μν©λλ€. Querydsl 5.0λΆν°λ fetchResult()
μ fetchCount()
κ° deprecated λμλλ°, 볡μ‘ν λ€μ€μΏΌλ¦¬μμ λ μΏΌλ¦¬κ° μ μμλνμ§ μκΈ° λλ¬Έμ λ¨μν fetch()
μ²λ¦¬ ν java size()
μ²λ¦¬νκ±°λ νμ΄μ§ μ²λ¦¬νλ κ²μ κΆμ₯νκ³ μμ΅λλ€. λ€μν μ‘°μΈ λ€μλ μ¦μλ‘λ© μ²λ¦¬λ₯Ό μν΄ fetchJoin()
μ μΆκ°ν΄μ μ¬μ©μ΄ κ°λ₯ν©λλ€.
// μ°κ΄κ΄κ³ κ°μ²΄κΉμ§ λͺ¨λ μ‘°μΈ, μ¦μλ‘λ©
selctFrom(customer).leftJoin(customer.order, order).fetchJoin().fetch()
// μ¦μλ‘λ© κ²°κ³Ό κ°μ ν κ° λ¦¬ν΄
selctFrom(customer).leftJoin(customer.order, order).fetchJoin().fetchOne()
μΈν μ‘°μΈμ μ°κ΄κ΄κ³κ° μμ΄λ λ°μ΄ν°λ₯Ό λ€ κ°μ§κ³ μμ μ‘°μΈμ νλ λ°©μμΌλ‘ μΉ΄λλλ¦¬ν° κ³±
μ λ°νν©λλ€. μ°κ΄κ΄κ³κ° μλ κ²½μ°μλ onμ
μ μ¬μ©ν΄ μΈλΆμ‘°μΈμ ν μ μμ΅λλ€.
List<Tuple> result = queryFactory
.select(member,team)
.from(member)
.leftJoin(team).on(member.username.eq(team.name))
.fetch();
βfetchJoin().fetch()βμ N+1 μ΄μμμ μμ λ‘μΈκΉ?
JPAλ₯Ό 곡λΆνλ©΄μ 쿼리 μ±λ₯ μ΅μ νλ₯Ό μν΄ κ°μ₯ λλλμλ λΆλΆμ΄ N+1
μ΄μκ° λ°μνλμ§μ λν λΆλΆμ΄μλ κ² κ°μ΅λλ€. Querydslλ μμ fetch
μ‘°μΈμ μ 곡νλλ° λ¬Έμ κ° μμκΉ κΆκΈμ¦μ΄ μκ²Όμ΅λλ€.
fetchJoin()
μΌλ‘ μ¦μλ‘λ© μ²λ¦¬λ κ°μ²΄λ₯Ό κ°μ§κ³ μ΅λλ€. μ¬κΈ°μ fetch()
λ fetchJoin()
μΌλ‘ μ΄λ―Έ λ‘λλ μν°ν°λ₯Ό μΆκ°μ μΈ μΏΌλ¦¬ μμ΄ ν λ²μ μ‘°ννλ©°, μ΄ λ μ€λ³΅λ μν°ν°λ₯Ό κ°μ§κ³ μ€μ§ μκΈ° λλ¬Έμ N+1
μ μ΄μλ₯Ό ν΄κ²°ν μ μμ΅λλ€.
μΆκ°λ‘ JPAμμ N+1 μ΄μλ₯Ό ν΄κ²°νκΈ° μν΄ distinct
λ μ‘°ν λμ κ°μ²΄μ μ€λ³΅μ μ κ±°νκ³ κ²°κ³Όλ₯Ό λμΆνλ€λ μ μμ μ°¨μ΄κ° μμ΅λλ€.
κ·Έλ λ€λ©΄ left outer joinμ μ¬μ©νλ κ²½μ° Querydslμ μ΄λ»κ² μ΄μ©ν΄μΌ ν κΉμ?
κΈ°λ³Έμ μΌλ‘ Parent κ°μ²΄λ₯Ό μ‘°ννκ³ Child κ°μ²΄λ₯Ό left join()
νλ©΄, left outer join
μΌλ‘ Parent κ°μ²΄λ₯Ό κΈ°μ€μΌλ‘ νμν Childλ§ λ½μμ€κ² λ©λλ€. Projection
μ μ¬μ©ν΄μ ν΄κ²°νκ±°λ Result Aggregation
μΌλ‘ Querydsl κ²°κ³Όλ₯Ό νΉμ ν€λ₯Ό κΈ°μ€ μΌμ κ·Έλ£Ήνν μ μμ΅λλ€.
// Result Aggressionμ μ΄μ©ν κ²½μ°
public List<Family> findFamily() {
Map<Parent, List<Child>> transform = queryFactory
.from(parent)
.leftJoin(parent.children, child)
.transform(groupBy(parent).as(list(child)));
return transform.entrySet().stream()
.map(entry -> new Family(entry.getKey().getName(), entry.getValue()))
.collect(Collectors.toList());
}
Map<Integer, List<Comment>> results = query.from(post, comment)
.where(comment.post.id.eq(post.id))
.transform(groupBy(post.id).as(list(comment)));
Projection
κΈ°μ‘΄ JPAμ νλ‘μ μ
λ³΄λ€ λ 볡μ‘ν 컨νΈλ‘€μ κ°λ₯νκ² ν©λλ€. νλ‘μ μ
λμμ΄ λ μ΄μμΈ κ²½μ°λ Tuple
μ΄λ DTO
μ‘°νλ‘ κ²°κ³Όλ₯Ό λ°νν©λλ€. Querydslμ νλ‘νΌν° Setter, νλ μ§μ μ κ·Ό, μμ±μλ₯Ό μ΄μ©ν μ κ·Ό λ°©μμ μ 곡ν©λλ€.
[νλ‘νΌν° Setterλ₯Ό μ΄μ©νλ λ°©μ]
μ΄ κ²½μ°μλ dtoμ κ° νλ‘νΌν°μ λν μμ±μκ° νμνκΈ° λλ¬Έμ λ³λμ μμ±μλ₯Ό μμ±νκ±°λ @NoArgsContructor
λ‘ μμ±μλ₯Ό λ§λ€μ΄μ£Όμ΄μΌ ν©λλ€.
List<MemberDto> result = queryFactory
.select(Projections.bean(MemberDto.class,
member.username,
member.age))
.from(member)
.fetch();
[νλ μ§μ μ κ·Ό λ°©μ]
λ³λ μμ±μλ₯Ό νμλ‘ νμ§ μμ΅λλ€. λ³μΉμ΄ λ€λ₯Έ κ²½μ°λ .as()
λ‘ ν΄κ²°νκ³ μλΈμΏΌλ¦¬λ ExpressionUtils.as(sourse, alias)
λ°©μμΌλ‘ ν΄κ²°ν©λλ€.
List<MemberDto> result = queryFactory
.select(Projections.fields(MemberDto.class,
member.username,
member.age))
.from(member)
.fetch();
[μμ±μ μ κ·Ό λ°©μ]
κ°κ³Ό μμ±μ μμκ° λ§μμΌ νλ©°, @AllArgsConstructor
λ₯Ό μ¬μ©νκ³ , setter
κ° νμνμ§ μμ΅λλ€.
List<MemberDto> result = queryFactory
.select(Projections.constructor(MemberDto.class,
member.username,
member.age))
.from(member)
.fetch();
μ΄ μμ±μ λ°©μμ @QueryProjection
μ μ§μν©λλ€. @QueryProjection
μ MemberDtoμ μ¬μ©νλ©΄, μλμ κ°μ΄ κ°κ²°ν μ½λ ννμ΄ κ°λ₯ν©λλ€. νμ§λ§ Querydslμ λν μμ‘΄μ±μ΄ dtoμ μκΈ°κΈ° λλ¬Έμ μ μ§λ³΄μμ μ ν©νμ§ μμ μ μλ€λ λ¨μ μ΄ μμ΅λλ€.
List<MemberDto> result = queryFactory
.select(new QMemberDto(member.username, member.age))
.from(member)
.fetch();
BooleanExpressionλ₯Ό μ΄μ©ν λμ 쿼리
BooleanExpression
λ₯Ό μ΄μ©νλ©΄ λ³΅ν© μ‘°κ±΄μ μμ±ν΄μ select
, where
μ μμ 쑰건μμΌλ‘ μ¬μ©μ΄ κ°λ₯ν©λλ€. BooleanBuilder
λ μνλ³κ²½μ΄ λκ³ , null 쑰건μ 무μλκ³ , λ€λ₯Έ 쿼리μμλ μ¬νμ©μ΄ κ°λ₯νλ€λ μ₯μ μ΄ μμ΅λλ€.
private List<Member> searchMember(String usernameCond, Integer ageCond) {
return queryFactory
.selectFrom(member)
.where(usernameEq(usernameCond), ageEq(ageCond))
.fetch();
}
private BooleanExpression usernameEq(String usernameCond) {
return usernameCond != null ? member.username.eq(usernameCond) : null;
}
private BooleanExpression ageEq(Integer ageCond) {
return ageCond != null ? member.age.eq(ageCond) : null;
}
μμ , μμ λ²ν¬μ°μ°
μμ κ³Ό μμ λ₯Ό νλ λ²ν¬μ°μ°μ μμμ± μ»¨ν
μ€νΈ μν°ν°λ₯Ό 무μνκ³ λ°λ‘ DBμ execute()
μ²λ¦¬λ₯Ό νκΈ° λλ¬Έμ μμμ± μ»¨ν
μ€νΈλ₯Ό μ΄κΈ°ννλ κ²μ΄ μ’μ΅λλ€.
long count = queryFactory
.delete(member)
.where(member.age.gt(18))
.execute();
em.flush();
em.clear();
μ°μν νμ λ€μ Querydsl μ¬μ©λ²
extends / implements μ¬μ©νμ§ μκΈ°
맀 repositoryλ§λ€ JpaRepository
λ₯Ό μμλ°μ§ μκ³ , JPAQueryFactory
μ Bean
μΌλ‘ λ±λ‘ν΄μ μ¬μ©ν©λλ€.
@Configuration
public class QuerydslConfiguration {
@Autowired
EntityManager em;
@Bean
public JPAQueryFactory jpaQueryFactory() {
return new JPAQueryFactory(em);
}
}
@Repository
@RequiredArgsConstructor
public class MemberRepositoryCustom {
private final JpaQueryFactory queryFactory; // λ¬Όλ‘ μ΄λ₯Ό μν΄μλ λΉμΌλ‘ λ±λ‘μ ν΄μ€μΌ νλ€.
}
λμ 쿼리λ BooleanExpression μ¬μ©νκΈ°
BooleanExpression
λ₯Ό μ¬μ©νλ©΄, null κ°μ 무μνκ³ λ¨μΌ 쑰건μ λνλ΄λ μΈν°νμ΄μ€λ‘ μ‘°ν©ν΄ 볡μ‘ν λμ 쿼리λ₯Ό λ§λ€ μ μμ΅λλ€. BooleanBuilder
λ BooleanExpression
λ€μ λͺ¨μμ μ¬μ©ν μ μκ² ν΄μ€λλ€. μν©μ λ°λΌ λ€λ₯΄κ² μ§λ§ ν΅μ BooleanBuilder
λ₯Ό μ¬μ©νλ κ²½μ°, μ¬λ¬ ννμμ κ°μ§κ³ 쑰립νκΈ° λλ¬Έμ ν λμ νμ
νλ κ²μ΄ μ΄λ ΅μ΅λλ€. κ·Έλ¦¬κ³ μ€λ³΅λλ 쑰건μμ μ¬νμ©νκΈ°μ BooleanExpression
μΌλ‘ λ©μλννλ κ²μ΄ μ 리ν©λλ€.
exist λ©μλ μ¬μ©νμ§ μκΈ°
count
μΏΌλ¦¬λ‘ λμνκΈ° λλ¬Έμ μ 체 λ€ μ‘°ννλ κ²½μ° μ±λ₯μ΄ λ¨μ΄μ§ μ μμ΅λλ€. κ·Έλ¦¬κ³ μ체μ μΌλ‘ 맀 μ€νμ μλΈ μΏΌλ¦¬λ₯Ό μμ±νκΈ° λλ¬Έμ κ°λ°μκ° μ§μ μ΅μ ννκΈ° μ΄λ ΅μ΅λλ€. λμ join
μ μ΄μ©νλ κ²μ΄ μ±λ₯μ μ΅μ νν μ μμ΅λλ€.
Cross Join νΌνκΈ°
μ‘°μΈμ λͺ
μνμ§ μλ 묡μμ μ‘°μΈμ ν¬λ‘μ€ μ‘°μΈμ νκ² λλ―λ‘ μ±λ₯μ΄ λ¨μ΄μ§ μ μμ΅λλ€. λ°λΌμ λͺ
μμ μ‘°μΈμΌλ‘ λΆνμν μ‘°νλ₯Ό μ€μ΄μΌ ν©λλ€.
μ‘°νν λ Entity 보λ€λ DTOλ₯Ό μ°μ μ μΌλ‘ κ°μ Έμ€κΈ°, Select μΉΌλΌμ Entityλ μμ νκΈ°
Entity μ체λ₯Ό κ°μ§κ³ μ€λ κ²μ μμμ± μ»¨ν
μ€νΈμ 1μ°¨ μΊμ κΈ°λ₯μ μ¬μ©νκ³ λΆνμν 컬λΌμ μ‘°ννκ² λ©λλ€. κ·Έλ¦¬κ³ OneToOne
N+1
쿼리 λ°μνλ λ¬Έμ κ° μμ΅λλ€. λ°λΌμ νμν μμ±λ§μ DTOλ‘ λ°μ 리ν΄νλ κ²μ΄ λ°λμ§ν©λλ€.
νΉνλ distinct
λ₯Ό μ¬μ©νλ κ²½μ°λ λͺ¨λ rowλ₯Ό νμΈνκΈ° λλ¬Έμ λ°λμ νμν 컬λΌλ§μ μ‘°ννλ κ²μ΄ λ°λμ§ν©λλ€.
Group By μ΅μ ννκΈ°
Querydslμ MySQLκ³Ό λ¬λ¦¬ OrderByNull
μ μ 곡νκ³ μμ§ μμ΅λλ€. κ·Έλ¦¬κ³ μΈλ±μ€κ° μλ€λ©΄ μλμ μΌλ‘ μ€νλλ μ λ ¬ μκ³ λ¦¬μ¦ Filesort
μ΄ λμνκ² λ©λλ€.
public class OrderByNull extends OrderSpecifier {
public static final OrderByNull DEFAULT = new OrderByNull();
private OrderByNull(){
super(Order.ASC, NullExpression.DEFAULT, NullHandling.Default);
}
}
Querydsl μμ 컀λ²λ§ μΈλ±μ€ μ¬μ©νκΈ°
μΈλ±μ€ κ²μμΌλ‘ λΉ λ₯΄κ² μ²λ¦¬νκ³ κ±Έλ¬μ§ νλͺ©μ λν΄μλ§ λ°μ΄ν° λΈλ‘μ μ κ·ΌνκΈ° λλ¬Έμ μ±λ₯μ μ΄μ μ μ»κ² λ©λλ€. νμ§λ§ μΈλ±μ€κ° λ§μμ§ μ μλ€λ λ¨μ μ΄ μμ΅λλ€.
νμ΄μ§ μ±λ₯ κ°μ μ μν΄ No Offset μ¬μ©νκΈ°
λ°μ΄ν°κ° λ§μμ§λ©΄ offset
+limit
λ₯Ό μ¬μ©ν λ°μ΄ν° μ‘°νμ μ±λ₯μ μ’μ§ μκΈ° λλ¬Έμ No Offset
μ μ¬μ©ν΄μ μμ μ§μ μ μΈλ±μ€λ‘ μ°Ύμ μ½λ κ²μ΄ μ’μ΅λλ€.
μΌκ΄ Update μ΅μ ννκΈ°
μΌκ΄ Updateλ₯Ό ν λλ Cache Eviction
μ²λ¦¬λ₯Ό ν΄μ£Όμ΄μΌ ν©λλ€. μμμ± μ»¨ν
μ€νΈμ Dirty Checkingμ μ¬μ©νλ©΄ μ€νλ € λ§μ μΏΌλ¦¬κ° λ°μνκ³ μ±λ₯μ λ¨μ μ΄ λ μ μμμΌλ‘ μΌκ΄ Updateλ₯Ό νλ κ²μ΄ μ’μ΅λλ€.
JPAλ‘ Bulk Insertλ μμ νκΈ°
JPA μλ auto_incrementμΌλ insert ν©μΉκΈ°κ° μ μ©λμ§ μμΌλ―λ‘ μ΄ κΈ°λ₯μ΄ νμνλ€λ©΄ JdbcTemplate
λ₯Ό μ¬μ©νλ©΄ λ©λλ€.
Querydsl Doc
Querydsl GitHub
μ€μ ! Querydsl
μλ° ORM νμ€ JPA νλ‘κ·Έλλ° - κΈ°λ³ΈνΈ
10μ₯ κ°μ²΄μ§ν₯ 쿼리μΈμ΄
JPQL DOC
Querydsl μμ OneToMany κ΄κ³μμ Left Outer Join μ΄ νμν κ²½μ°
JPAQuery.fetchResults() is deprecated, how should I replace it?
[μ°μμ½2020] μμμ΅κ±΄μμ QUERYDSL μ¬μ©νκΈ°
1. 컀λ²λ§ μΈλ±μ€ (κΈ°λ³Έ μ§μ / WHERE / GROUP BY)
Hibernate
JPA ꡬν체λ Hibernate μΈμλ EclipseLink, OpenJPA, DataNucleus λ±μ΄ μμ΅λλ€. κ·Έ μ€μμλ κ°μ₯ λ§μ΄ μ¬μ©λλ Hibernateμ λν΄ μμλ³΄λ €κ³ ν©λλ€. Hibernateλ μλ° μΈμ΄λ₯Ό μν κ°μ²΄ κ΄κ³ λ§€μΉ νλ μμν¬μ λλ€. μ€μ Spring νλ‘μ νΈμμ JPAλ₯Ό μ¬μ©νλ κ²½μ° Hibernateκ° λΌμ΄λΈλ¬λ¦¬κ° ν¬ν¨λμ΄ μλ κ²μ λ³Ό μ μμ΅λλ€.
Hibernateλ JPA ꡬνμΌλ‘ SessionFactory, Session, TransactionμΌλ‘ μμλ°κ³ κ°κ°μ Implμ ꡬννκ³ μμ΅λλ€. JDBC APIλ₯Ό μ¬μ©νμ§ μκ³ , Hibernateμμ μ 곡νλ λ©μλλ§μΌλ‘λ SQLμ λ체ν μ μλ€λ μ₯μ μ΄ μμ΅λλ€. λ°λΌμ λΉμ¦λμ€ λ‘μ§μ μ§μ€ν μ μκ² λκ³ , κ°μ²΄μ§ν₯μ κ°λ°μ΄ κ°λ₯νκ² ν©λλ€.
Hibernate ORM 5.4.33.Final User Guide
[JPA] JPAμ Hibernate κ·Έλ¦¬κ³ Spring Data JPA