[λ°°μ보μ Spring Data JPA] λ§€ν ν μ΄λΈκ³Ό μ°κ΄κ΄κ³ λ§€ννκΈ°

ν΄λΉ κΈμ λ°°μ보μ Spring Data JPA μλ¦¬μ¦ μ λλ€.
ν΄λΉ μ리μ¦μ λ΄μ©μ΄ μ΄μ΄μ§λ ννμ΄λ―λ‘ κΈμ λ΄μ© μ€μ μλ΅λλ λ§λ€μ΄ μμ μ μμΌλ, μμΈν μ¬νμ μλ λ§ν¬λ₯Ό μ°Έκ³ ν΄μ£ΌμΈμ!
- Spring Data JPAμ κΈ°λ³Έκ³Ό νλ‘μ νΈ μμ± :: μλ¦¬μ¦ νμ΅ νκ²½ μ€λΉ
- JPAμ κΈ°λ³Έκ³Ό Spring Data JPA
- Springboot project μμ JPA μ€μ νκΈ°
- JPAμ κΈ°λ³Έ μ΄λ
Έν
μ΄μ
λ€ :: JPAμ μμκ³Ό λμμ λ
- μν°ν°μ ν μ΄λΈ λ§€ν
- νλμ μ»¬λΌ λ§€ν
- λ§€ν ν
μ΄λΈκ³Ό μ°κ΄κ΄κ³ λ§€ννκΈ° :: RDBμ κ½, μ°κ΄ κ΄κ³
- μ°κ΄κ΄κ³λ?, μΈλ ν€λ?, λ§€ν ν μ΄λΈμ΄λ?
- μΌλμΌ, μΌλλ€, λ€λμΌ μ°κ΄κ΄κ³
- κ³΅ν΅ μΈν°νμ΄μ€ κΈ°λ₯ :: μ΄λ»κ² Data JPA λ λμν κΉ?
- λ¨κ±΄ μ‘°ν λ°ν νμ
- 컬λ μ μ‘°ν λ°ν νμ
- μ¬μ©μ μ μ 쿼리 μ΄μ©νλ λ°©λ² :: λ΄κ° μνλ 쿼리λ₯Ό JPA μμ λ§λ€μ΄λ³΄μ!
- λ©μλ μ΄λ¦μΌλ‘ 쿼리 μμ±νκΈ°
@Queryλ₯Ό μ΄μ©νμ¬ λ©μλμ μ μ 쿼리 μμ±νκΈ°
- νμ΄μ§κ³Ό μ λ ¬ :: κ²μνκ³Ό κ°μ νμ΄μ§κ° μλ μλΉμ€μμ λΉμ λ°νλ JPA νμ΄μ§!
- Data JPAμ νμ΄μ§κ³Ό μ λ ¬
- Web MVC μμ JPA νμ΄μ§κ³Ό μ λ ¬
- Auditing :: λͺ¨λ μμ²κ³Ό μλ΅μ
λκ°, μΈμ μ κ·Όνλμ§ νλμ μν°ν°λ‘ κ΄λ¦¬νμ.- μμ JPAμ Auditing
- Spring Data JPAμ Auditing
- λ°°μ보μ Spring Data JPA μ리μ¦λ₯Ό λ§μΉλ©°...
- μ리μ¦λ₯Ό λ§μΉλ©° λλμ
- λ΄κ° μ 보λ₯Ό μ»μ κ³³
- ν΄λΉ μ리μ¦λ₯Ό μμ£Όνμ ¨λμ?
ν μ΄λΈ μ°κ΄κ΄κ³λ RDBμ μμ΄ μ€μν κ°λ μ€ νλλΌκ³ μκ°νλ€.
μ΄λ° ν μ΄λΈ μ°κ΄κ΄κ³λ₯Ό μκΈ° μν΄μλ μΈλ ν€μ λν κ°λ κ³Ό λ§€ν ν μ΄λΈμ λν κ°λ μ μ΄ν΄ν΄μΌ νλ€.
μΈλ ν€μ λ§€ν ν μ΄λΈ
μΈλ ν€λ?
κ΄κ³ν λ°μ΄ν°λ² μ΄μ€μμ μΈλ ν€λ ν ν μ΄λΈμ νλ μ€ λ€λ₯Έ ν μ΄λΈμ νμ μλ³ν μ μλ ν€λ₯Ό λ§νλ€.
μ΄λ° μΈλ ν€λ₯Ό μ¬μ©νλ κ³³μ μ£Όλ‘ λ€μκ³Ό κ°μ κ³³μΌ κ²μ΄λ€.
μΌνλͺ°μμ μ¬μ©μμ μ£Όλ¬Έ λͺ©λ‘μ μλ‘ λ€μ΄λ³΄μ.
λ§μ½ μ¬μ©μ Jκ° μ΄ μΉ«μμ ꡬ맀νλ€κ³ κ°μ ν΄λ³΄μ.
κ·ΈλΌ ν μ΄λΈμ΄ μ΄λ»κ² λ κΉ?

μ κ·νλ₯Ό μ νλ€λ©΄ μ΄λ° μμΌλ‘ ꡬμ±ν μΌμ μκ² μ§λ§ μ°μ μ΄λ κ² νλ€κ³ κ°μ ν΄λ³΄μ.
κ·ΈλΌ μ¬μ©μ Aκ° μ΄λ€ λ¬Όνμ μλμ§λ₯Ό νμΈνλ 쿼리λ μλ§ λ€μκ³Ό κ°μ κ²μ΄λ€.
SELECT order_item
FROM user
WHERE username = "J"
κ·ΈλΌ μ΄λ κ² κ°μ ν΄λ³΄μ.
λ§μ½ μΉ«μκ³Ό μΉμ½μ ν¨κ» ꡬ맀νλ€λ©΄? μΉ«μ, μΉμ½, λΉλ, μ΄νΈ λ₯Ό κ°μ΄ ꡬ맀νλ€λ©΄?
μ΄λ»κ² ν΄μΌν κΉ?
μ§κΈ DBμλ Itemμ μ μ₯ν 곡κ°μΈ order_itemμ νλλ§ μλ μνλΌ κ΅¬λ§€κ° λΆκ°λ₯νλ€.
κ·ΈλΌ order_item1, order_item2 μ΄λ° μμΌλ‘ κ³μ λλ €μΌ ν΄μΌ νλ?
μ΄λ΄ λ λ°λ‘ μΈλ ν€μ λ§€ν ν μ΄λΈμ΄ λ±μ₯νλ€.
λ§€ν ν μ΄λΈ
λ§€ν ν μ΄λΈμ κ° ν μ΄λΈμ PKλ₯Ό μΈλ ν€λ‘ μ°Έμ‘°νλ ν μ΄λΈλ‘ κ° μ§ν©μ μ μ₯ν λ μ£Όλ‘ μ¬μ©λλ€.
λ§μ΄ μ΄λ ΅μ§ λ§μ 보면 μ½λ€.

Order_item ν μ΄λΈ
Order_Item ν μ΄λΈμμλ item_idμ user_Id κ° μ‘΄μ¬νλ€.
μ΄λ° μμΌλ‘ ν μ΄λΈμ λλλ€λ©΄ μ΄λ€ μ΄μ μ΄ μμκΉ?
- user_idλ User ν μ΄λΈμ PKλ‘ μλ³νλ μΈλν€λ‘ order_item μ΄ μ΄λ€ μ¬μ©μμκ² κ΅¬λ§€λμλμ§λ₯Ό μλ³ν μ μκ³ ,
- item_idλ Item ν μ΄λΈμ PKλ‘ μλ³νλ μΈλν€λ‘ order_itemμ΄ μ΄λ€ μμ΄ν μ κ°λμ§λ₯Ό μλ³ν μ μκ² λλ€.
κ·ΈλΌ μμμ λ§ νλ λ¬Έμ , λ§μ½μΉ«μκ³Ό μΉμ½μ ν¨κ» ꡬ맀νλ€λ©΄? μΉ«μ, μΉμ½, λΉλ, μ΄νΈ λ₯Ό κ°μ΄ ꡬ맀νλ€λ©΄? μ λ¬Έμ λ₯Ό ν΄κ²°ν μ μλ€.
μ΄λ»κ²?
itemμ΄ λμ΄λ μλ‘ Order_Item ν μ΄λΈμ λ°μ΄ν°λ₯Ό μΆκ°νκΈ°λ§ νκ³ , SELECT ν λ, user_id κ° νΉμ νμμΈ λ‘μ°λ§ μ‘μμ€λ©΄ λκΈ° λλ¬Έμ!
ν μ΄λΈ μ°κ΄ κ΄κ³
κ·ΈλΌ λ΄μΉκΉμ μͺΌκΈλ§ λ μκ°ν΄λ³΄μ.
μ΄λ¬ν λ§€ν ν μ΄λΈμλ κ΄κ³κ° μμ£Ό μ€μν μ‘΄μ¬μΈλ°, κ΄κ³λ μΌλ° ν μ΄λΈκ³Ό *_λ§€ν ν μ΄λΈμ΄ μ΄λ€ *_ννλ‘ μ°κ²°λμλμ§λ₯Ό λ»νλ κ²μ΄λ€.
- User ν μ΄λΈ μ μ₯μμ ν λͺ μ Userλ μ¬λ¬ Order_Itemμ κ°μ§ μ μλ€.
- Item ν μ΄λΈ μ μ₯μμ νλμ Itemμ μ¬λ¬ Order_itemμ κ°μ§ μ μλ€.
μ°λ¦¬λ μ΄ κ²λ€μ ν μ΄λΈ μ°κ΄ κ΄κ³λΌκ³ νννκ³ , μ΄ κ΄κ³λ₯Ό ν¬κ² 3κ°μ§λ‘ λλ μ μλ€.
- 1:1
- 1:N
- N:1
μλ N:M κ΄κ³λ μ‘΄μ¬νμ§λ§ μ¬μ©νμ§ μμμΌ νλ―λ‘ μλ΅νλ€.
λ€μ Javaλ‘ λμμμ μκ°ν΄λ³΄μ.
μ°λ¦¬λ μ§κΈ κ°μ²΄μ§ν₯ ν¨λ¬λ€μμμ SQL ν¨λ¬λ€μμ μ΄μ©νλ €κ³ νκ³ μλ€.
μ΄λ μ μ λ§ νλ ν¨λ¬λ€μ, μνΌλμ€ λΆμΌμΉμ λ¬Έμ λ₯Ό μκ³ μλ κ²μΈλ°, κΆκ·Ήμ μΌλ‘ μ°λ¦¬κ° νλ €κ³ νλ κ²μ κ°μ²΄ κ·Έλν νμμ νλ κ²μ΄λ€.
μ¦, μ΄λ¬ν SQL 쿼리λ₯Ό
SELECT name
FROM item I
JOIN order_item OI ON U.id = I.id
WHERE OI.user_id = 10;
μ΄λ¬ν μλ°μ μ½λλ‘ λ°κΎΈκ³ μΆμ κ²μ΄λ€.
μ΄λ₯Ό κ°μ²΄ κ·Έλν νμμ΄λΌκ³ λ νλ€.
OrderItem orderItem = user.getOrderItem();
Item item = orderItem.getItem();
item.getName();
μ°κ΄κ΄κ³ μ΄λ Έν μ΄μ
μ΄λ° μ°κ΄ κ΄κ³λ₯Ό JPA μμ νΉμ μλ°μμ μ΄μ©νκΈ° μν΄μλ λ€μ μ΄λ Έν μ΄μ μ μμμΌ νλ€.
@OneToOne: μΌλμΌ λ§€ν@OneToMany: μΌλλ€ λ§€ν (μ¬μ©μ, 1) : (μ£Όλ¬Έ λͺ©λ‘, N)@ManyToOne: λ€λμΌ λ§€ν (μ£Όλ¬Έ λͺ©λ‘, N) : (μ¬μ©μ, 1)
μ€μ μ½λλ‘ λ¨Όμ λ΄λ³΄μ.
OrderItem ν΄λμ€
@Entity
public class OrderItem {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@ManyToOne(fetch = FetchType.LAZY) // 1
@JoinColumn(name = "userId") // 2
private User userId;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "itemId")
private Item itemId;
}
User ν΄λμ€
@Entity
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String username;
@OneToMany(mappedBy = "user")
private List<OrderItem> orderItems = new ArrayList<>();
}
Item ν΄λμ€λ
List<>λ₯Ό μ μΈν΄μΌ νλκ² μλκ°? λΌλ μκ°μ΄ λ€ λ, λΉμ¦λμ€ λ‘μ§μ ν λ² μκ°ν΄λ³΄λ©΄ λλ€.
Item μ μ₯μμλ μ΄λ€ μ£Όλ¬Έμ΄ λμλμ§ μμμΌ ν νμκ° μμκΉ?
μ§λ μκ°μ νμΈνλ μν°ν° λ§€νμμ λ³΄μ§ λͺ»νλ μ΄λ Έν μ΄μ λ€μ΄ μ‘΄μ¬νλ€.
@ManyToOne(fetch = FetchType.LAZY)@JoinColumn(name = "")@OneToMany(mappedBy = "")
@ManyToOne, λ€λμΌ λ§€ν
μ΄λ¦ κ·Έλλ‘ λ€λμΌ κ΄κ³μμ μ¬μ©νλ€.
OrderItemμ μ₯μμλ νλμ μμ΄ν μ ν λͺ μ User μκ² λ§€νλμ΄μΌ νκ³
κ·ΈλΌ(OrderItem) λ€ : μΌ (User)μ κ΄κ³κ° μ±λ¦½νλ€.
λ³΄ν΅ μ°Έμ‘° νλ μν°ν° μμ μ¬μ©μ νλλ°, μΈλ ν€λ₯Ό κ°μ§κ³ μλ μν°ν°λΌκ³ μκ°νλ©΄ νΈνλ€.
@ManyToOne μ΄λ
Έν
μ΄μ
μμ 보면 λ€μ fetch = FetchType.LAZY κ° λΆμ΄μλ€.
FetchType
FetchType μ μ΄ν΄νκΈ° μν΄μλ JPA νλ‘μλΌλ κ°λ μ μ΄ν΄ν΄μΌ νλ€.
νμ¬ λͺ©μ μ JPAλ₯Ό λΆμνλκ² μλκΈ° λλ¬Έμ μΌλ¨ λμ΄κ°λλ‘ νκ³ , λ κ°μ§λ§ κΈ°μ΅νμ.
- μ¦μ λ‘λ© (
FecthType.EAGER)- μν°ν°λ₯Ό μ‘°νν λ, μ°κ΄λ μν°ν°λ₯Ό μ¦μ ν λ²μ μ‘°ννλ€.
- μ¦, μ€μ κ°μ²΄κ° μ¬μ©λμ§ μλλΌλ μ‘°νλ₯Ό ν΄μ¨λ€.
- μ§μ° λ‘λ© (
FetchType.LAZY)- μν°ν°λ₯Ό μ‘°νν λ, μ°κ΄λ μν°ν°λ μ€μ μ¬μ© μμ μ μ‘°ννλ€.
- μ¦, μ€μ κ°μ²΄κ° μ¬μ©λλ μμ κΉμ§ μ‘°νλ₯Ό 미룬λ€.
κ°κΈμ μ΄λ©΄ FetchType.LAZY λ₯Ό κΆμ₯νλ€.
@OneToMany, μΌλλ€ λ§€ν
μ΄λ¦ κ·Έλλ‘ μΌλλ€ κ΄κ³ μμ μ¬μ©νλ€.
,
Userμ μ₯μμ νλμ μ¬μ©μλ μ¬λ¬ μ£Όλ¬Έ μμ΄ν μ κ°μ§ μ μλ€.
κ·ΈλΌ(User) μΌ : λ€ (OrderItem)μ κ΄κ³κ° μ±λ¦½νλ€.
λ³΄ν΅ μ°Έμ‘° λΉνλ μν°ν° μμ μ¬μ©νλλ°, List 컬λ μ
μ μ°Έμ‘°λ³μλ‘ νλ€.
@OneToMany μ΄λ
Έν
μ΄μ
μ 보면 λ€μ mappedBy = "" κ° λΆμ΄μλ€.
mappedBy
mappedByλ μλ°©ν₯ λ§€νμμ μ¬μ©λλ κ°λ
μ΄λ€.
μλ°©ν₯μΌλ‘ μ°Έμ‘°λ λ μ°Έμ‘° λΉνλ μν°ν°μμ μ¬μ©νλ€.
νμμ @OneToMany(mappedBy = "μ°Έμ‘°νλ μν°ν°μ μλ λ³μ μ΄λ¦") μΌλ‘ μμ±ν μ μλ€.
mappedByλ₯Ό μ¬μ©νλ μ΄μ λ, νμ¬ μμ μ μ°Έμ‘°κ° ν΄λΉ μν°ν°μ μ΄λ€ λ³μλ‘ μ§μ λμλμ§ JPA μκ² μλ €μ£ΌκΈ° μν¨ μ―€μ΄λΌκ³ μκ°νμ.
μλ°©ν₯κ³Ό λ¨λ°©ν₯
@ManyToOne λ§ μ‘΄μ¬νλ€λ©΄, μ¦ OrderItem ν΄λμ€λ§ Userμ μ 보λ₯Ό κ°κ³ μλ€λ©΄, μ΄λ λ¨λ°©ν₯ μ°κ΄κ΄κ³λΌκ³ νλ€.
λ¨λ°©ν₯ μ°κ΄κ΄κ³κ° κ°μ²΄μ§ν₯μ μΌλ‘ λ΄λ, κ΄μ¬μ¬λ‘ λ΄λ ν¨μ¬ μ΄λμ΄ λ§λ€.
κ·Έλλ μ°λ¦¬λ μλ°©ν₯μ μ¬μ©ν΄μΌ νλ μ΄μ© μ μλ μν©λ€μ΄ μκΈ°κ² λλ€.
κ·ΈλΌ λ€μμ κΈ°μ΅νμ
- μλ°©ν₯ μ°κ΄κ΄κ³κ° λ λ _μΈλ ν€_λ₯Ό κ΄λ¦¬νκ³ μμ 주체λ₯Ό νμ€ν ν κ²
- μΈλ ν€λ₯Ό κ°λ 주체λ DB ν μ΄λΈμ μΈλ ν€κ° μλ μͺ½μΌλ‘ νλ€.
- μΈλ ν€λ₯Ό κ°λ μͺ½μμλ§ UPDATEμ INSERT λ₯Ό μννκ³ , μλ μͺ½μ SELECT λ§ μνν κ²
- μλ°©ν₯ μ°κ΄κ΄κ³μμ μνΈ μ°Έμ‘°λ₯Ό μ£Όμν κ²
- Lombok μ
@ToString@ToString(exclude = "")λ₯Ό μ΄μ©νμ¬ ν΄κ²°
- MVC μμ JSON Converting λ λ
- μν°ν°λ₯Ό ν΅μ μμ κ·Έλλ‘ μ¬μ©νμ§ λ§κ³ DTO κ°μ²΄λ₯Ό λ§λ€μ΄μ μ¬μ©ν κ²
- Lombok μ
ν μ€νΈ
μ¬κΈ° κΉμ§ μ λκ³ μλμ§λ₯Ό νμΈνκΈ° μν΄μ μ§λ μκ°κ³Ό λμΌνκ² μμ κ°λ λ€μ μ½λλ‘ μ¦λͺ ν΄λ³΄μ.
μ°λ¦¬κ° μ¦λͺ ν κ²μ, νλμ μ¬μ©μκ° μ¬λ¬ μμ΄ν μ κ°μ‘μ λ, ν΄λΉ μ£Όλ¬Έ λͺ©λ‘μμ μ£Όλ¬Έν μ¬μ©μ μ΄λ¦μ΄ ν΄λΉ μ¬μ©μμ μ΄λ¦κ³Ό λμΌνμ§λ₯Ό ν μ€νΈν κ²μ΄λ€.
- Entity
User.classItem.classOrderItem.class
- Repository
UserRepository.interfaceItemRepository.interfaceOrderItemRepository.interface
- Test
OrderItemRepository.class
μ ν΄λμ€λ₯Ό μμ±νλ€.

User, Item, OrderItem
@Entity
@AllArgsConstructor
@NoArgsConstructor
@Builder @Getter
public class User {
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String username;
@OneToMany(mappedBy = "user")
private List<OrderItem> orderItems = new ArrayList<>();
}
// μ΄λ
Έν
μ΄μ
μλ΅
public class Item {
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
}
// μ΄λ
Έν
μ΄μ
μλ΅
public class OrderItem {
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "user_id")
private User user;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "item_id")
private Item item;
}
UserRepository, ItemRepository, OrderItemRepository
public interface UserRepository extends JpaRepository<User, Long> {}
public interface OrderItemRepository extends JpaRepository<OrderItem, Long> {}
public interface ItemRepository extends JpaRepository<Item, Long> {}
OrderItemRepository
@SpringBootTest
@Transactional
@Rollback(false)
class OrderItemRepositoryTest {
@Autowired ItemRepository itemRepository;
@Autowired UserRepository userRepository;
@Autowired OrderItemRepository orderItemRepository;
@Test
void createTest() {
// given
String username = "James";
User user = User.builder()
.username(username)
.build();
userRepository.save(user);
List<OrderItem> orderItems = new ArrayList<>();
for (int i = 1; i <= 2; i++) {
Item item = itemRepository.save(Item.builder()
.name("item " + i)
.build());
OrderItem orderItem = OrderItem.builder()
.item(item)
.user(user)
.build();
orderItems.add(orderItem);
}
// when
List<OrderItem> savedOrderItems = orderItemRepository.saveAll(orderItems);
//then
assertEquals(username, savedOrderItems.get(0).getUser().getUsername());
assertEquals(username, savedOrderItems.get(1).getUser().getUsername());
}
}
μ¬κΈ°μ ν
μ€νΈ ν΄λμ€ μλ¨μ @Transactionalκ³Ό @Rollback μ΄λ
Έν
μ΄μ
μ λκΉ?
@Trasacntional- JPAμ λͺ¨λ μμ μ νλμ νΈλμμ λ΄μμ μΌμ΄λκ² λλ€.
- ν΄λΉ ν΄λμ€μ νΈλμμ λ°μ΄λ리λ₯Ό κ±Έμ΄μ£Όλ κ²μ΄λ€.
@Rollback- νΈλμμ μ Rollback κ°λ κ³Ό λμΌνλ€.
- false λ‘ μ€μ νλ€λ©΄ μ€ν μμ DBμ μ μ₯νλ€. : rollback μ μννμ§ μλλ€.
- true λ‘ μ€μ νλ€λ©΄ μ€ν μμ DBμ μ μ₯λμ§ μλλ€. : rollbackμ μννλ€.
νΈλμμ κ³Ό Rollbackμ λν΄μλ λ°μ΄ν°λ² μ΄μ€ μ΄λ‘
Transaction, νΈλμμ μ΄λ? κ³Ό Isolation Level, κ³ λ¦½ μμ€μ΄λ? μ μ°Έκ³ νλ κ²λ μ’μ κ² κ°λ€.