SpringBoot Part4(3)
์ฐ๊ด๊ด๊ณ ๋งคํ
๊ฐ์ฒด ์ฐ๊ด๊ด๊ณ VS ํ ์ด๋ธ ์ฐ๊ด๊ด๊ณ
ํ ์ด๋ธ์ ์ธ๋ํค๋ก ์ฐ๊ด ๊ด๊ณ๋ฅผ ๋งบ๋๋ค.
๊ฐ์ฒด๋ ์ฐธ์กฐ(์ฃผ์)๋ก ์ฐ๊ด ๊ด๊ณ๋ฅผ ๋งบ๋๋ค.
ํต์ฌ ํค์๋
๋ฐฉํฅ์ฑ (๋จ๋ฐฉํฅ, ์๋ฐฉํฅ)
- ํ ์ด๋ธ์์ ๊ด๊ณ๋ ํฅ์ ์๋ฐฉํฅ์ด๋ค.
์ธ๋ํค๋ฅผ ์ด์ฉํด์ ์๋ฐฉํฅ์ผ๋ก ์กฐ์ธ์ด ๊ฐ๋ฅ
SELECT * FROM orders AS o JOIN member AS m
ON o.member._id = m.id
- ๊ฐ์ฒด์์์ ๋จ,์๋ฐฉํฅ ๊ด๊ณ
// ํ์ -> ์ฃผ๋ฌธ ๋จ๋ฐฉํฅ ๋งคํ ๊ฒฝ์ฐ. ํ์์์ ์ฃผ๋ฌธ๋ง ์ฐธ์กฐ๊ฐ ๊ฐ๋ฅํ๋ค.
class Member {
private long id;
private List<Order> orders; // ํ์ -> ์ฃผ๋ฌธ
}
class Order {
private String id;
}
Member member = new Member();
Order order = meber.getOrders().get(0);
// ์ฃผ๋ฌธ -> ํ์ ๋จ๋ฐฉํฅ ๋งคํ
class Member {
private long id;
}
class Order {
private String id;
private Member member;
}
// ํ์ -> ์ฃผ๋ฌธ, ์ฃผ๋ฌธ -> ํ์ ๋ชจ๋ ์ฐธ์กฐ๊ฐ ๊ฐ๋ฅํ ์๋ฐฉํฅ ๋งคํ
class Member {
private long id;
private List<Order> orders;
}
class Order {
private String id;
private Member member;
}
๋ค์ค์ฑ (๋ค๋๋ค, ์ผ๋๋ค, ๋ค๋๋ค)
- ํ์์ ์ฌ๋ฌ ์ฃผ๋ฌธ์ ํ ์ ์๊ธฐ ๋๋ฌธ์, ํ์(1)๊ณผ ์ฃผ๋ฌธ(N)์ ์ผ๋๋ค ๊ด๊ณ
- ์ฃผ๋ฌธ์ ์ฌ๋ฌ ํ์์ ์ํด ๋ฐ์ํ ์ ์๊ธฐ ๋๋ฌธ์, ์ฃผ๋ฌธ(N)๊ณผ ํ์(1)์ ๋ค๋์ผ ๊ด๊ณ์ด๋ค.
์ฐ๊ด๊ด๊ณ ์ฃผ์ธ (mappedBy)
- ๊ฐ์ฒด๋ฅผ ์๋ฐฉํฅ ์ฐ๊ด๊ด๊ณ๋ก ๋ง๋ค๋ฉด, ์ฐ๊ด๊ด๊ณ์ ์ฃผ์ธ์ ์ ํด์ผ ํ๋ค.
- ์ธ๋ํค๋ฅผ ๊ด๋ฆฌํ ๊ฐ์ฒด๋ฅผ ์ง์ ํ๋ค. (INSERT, UPDATE, DELETE)
- ์ฐ๊ด๊ด๊ณ ์ฃผ์ธ๋ง์ด, ์ธ๋ํค๋ฅผ ๋ฑ๋ก ์์ ์ญ์ ํ ์ ์๋ค. (์ฃผ์ธ์ด ์๋์ชฝ์ ์ฝ๊ธฐ SELECT๋ง ๊ฐ๋ฅํ๋ค.)
- @OneToMany(mappedBy = ์ฐ๊ด๊ด๊ณ ์ฃผ์ธ ๊ฐ์ฒด์ ๋งคํ ํ๋๋ช )
- @ManyToOne์ ํญ์ ์ฐ๊ด๊ด๊ณ์ ์ฃผ์ธ์ด ๋๋ฏ๋ก mappedBy๋ฅผ ์ค์ ํ ์ ์์ต๋๋ค.
// Order_Item ํ
์ด๋ธ์ order_id๋ฅผ ์ธ๋ํค๋ก ๊ฐ๋๋ค. ์์ ์ด ์ฐ๊ด๊ด๊ณ์ ์ฃผ์ธ
@Entity
@Table
public class OrderItem extends BaseEntity{
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private int id;
@ManyToOne
private Order order;
}
// ์๋ฐฉํฅ ์ฐ๊ด๊ด๊ณ ์ฒ๋ผ ๋ณด์ด์ง๋ง ์ค์ 2๊ฐ์ ๋จ๋ฐฉํฅ ์ฐ๊ด๊ด๊ณ
// ํ
์ด๋ธ์ ๋จ๋ฐฉํฅ ๋งคํ์ด๊ธฐ์ ์ค์ Order ํ
์ด๋ธ์๋ orderItem ์ ๋ณด๊ฐ ์๋ค.
// ์ฐ๊ด๊ด๊ณ์ ์ฃผ์ธ orderItem.order์ ํ๋๋ช
์ mappedBy์ ๊ธฐ์ฌํด์ค๋ค.
@Entity
@Table(name = "orders")
public class Order extends BaseEntity{
@Id
@Column(name = "id")
private String uuid;
@OneToMany(mappedBy = "Order")
private List<OrderItem> orderItems;
}
๊ฐ์ฒด ๊ทธ๋ํ ํ์
๊ฐ์ฒด์ ์ฐธ์กฐ๋ฅผ ํตํด ๊ฐ์ฒด๋ฅผ ๊ฐ์ ธ์จ๋ค.
class Member {
private long id;
private List<Order> orders;
}
class Order {
private String id;
private Member member;
}
...
@Test
void graph() {
Member member1 = new Mebmer(1);
Order order1 = new Order(1)
member1.setOrders(Lists.newArrayList(order1));
Order findOrder= member1.getOrders().get(o); // ๊ฐ์ฒด ๊ทธ๋ํ ํ์์ด๋ผ ํ๋ค.
findOrder.getMember();
}
๋จ๋ฐฉํฅ ์ฐ๊ด ๊ด๊ณ
@Entity
@Table(name = "orders")
@Getter
@Setter
public class Order {
@Id
@Column(name = "id")
private String uuid;
@Column(name = "order_datetime", columnDefinition = "TIMESTAMP")
private LocalDateTime orderDatetime;
@Enumerated(EnumType.STRING)
private OrderStatus orderStatus;
@Lob
private String memo;
@Column(name = "member_id", insertable = false, updatable = false) // fk
private Long memberId;
@ManyToOne // ํ์ ํ๋ช
์๊ฒ ์ฌ๋ฌ ์ฃผ๋ฌธ
@JoinColumn(name = "member_id", referencedColumnName = "id") // ๋ช
์ํ์ง ์์๊ฒฝ์ฐ ํด๋นํ๋๋ช
_PKํ๋๋ช
private Member member;
}
์๋ฐฉํฅ ์ฐ๊ด ๊ด๊ณ
@Entity
@Table(name = "member")
@Getter
@Setter
public class Member {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE)
private Long id;
@Column(name = "name", nullable = false, length = 30)
private String name;
@Column(nullable = false, length = 30, unique = true)
private String nickName;
@Column
private int age;
@Column(name = "address", nullable = false)
private String address;
@Column(name = "description")
private String description;
@OneToMany(mappedBy = "member") // ์ฐ๊ด๊ด๊ณ ์ฃผ์ธ ์ค์ -> ์ฐ๊ด๊ด๊ณ ์ฃผ์ธ ๊ฐ์ฒด์ ํ๋๊ฐ
private List<Order> orders = new ArrayList<>();
}
ํ์ธ
@Test
void ์ฐ๊ด๊ด๊ณ_ํ
์คํธ(){
EntityManager entityManager = emf.createEntityManager();
EntityTransaction transaction = entityManager.getTransaction();
transaction.begin();
Member member = new Member();
member.setName("kanghonggu");
member.setAddress("์์ธ์ ๋์๊ตฌ(๋ง) ์์ง์ด๋ฉด ์๋ค.");
member.setAge(33);
member.setNickName("guppy.kang");
entityManager.persist(member);
Order order = new Order();
order.setUuid(UUID.randomUUID().toString());
order.setOrderDatetime(LocalDateTime.now());
order.setOrderStatus(OPENED);
order.setMemo("๋ถ์ฌ์ ์ ํ์ฃผ์ธ์.");
order.setMember(member); // setMemberId๋ฅผ ํ์ง์์๋ ํ
์ด๋ธ์ FK๊ฐ ๋งคํ๋จ.
entityManager.persist(order);
transaction.commit();
entityManager.clear();
Order entity = entityManager.find(Order.class, order.getUuid());
log.info("{}", entity.getMember().getName()); // ๊ฐ์ฒด ๊ทธ๋ํ ํ์์ ํตํด member ๊ฐ์ ธ์ค๊ธฐ
log.info("{}", entity.getMember().getOrders().size());
log.info("{}", order.getMember().getOrders().size());
// 0 -> ์ฝ๋๋ ๋ฒจ memder์๋ setOrders๋ฅผ ์ํด์ค์ ์ฌ์ด์ฆ๊ฐ 0์ด ๋์จ๋ค.
// ํด๋น ์์
์ด ๋ฒ๊ฑฐ๋ก์ฐ๋ ์ฐ๊ด๊ด๊ณ ํธ์ ๋ฉ์๋๋ฅผ ๋ง๋ค์ ์๋ค.
}
์ฟผ๋ฆฌ ํ์ธ
Hibernate: insert into member (address, age, description, name, nickName, id) values (?, ?, ?, ?, ?, ?)
Hibernate: insert into orders (member_id, memo, order_datetime, orderStatus, id) values (?, ?, ?, ?, ?)
Hibernate: select o1_0.id,m1_0.id,m1_0.address,m1_0.age,m1_0.description,m1_0.name,m1_0.nickName,o1_0.member_id,o1_0.memo,o1_0.order_datetime,o1_0.orderStatus from orders o1_0 left join member m1_0 on m1_0.id=o1_0.member_id where o1_0.id=?
์ฐ๊ด๊ด๊ณ ํธ์ ๋ฉ์๋
: ์ฐ๊ด๊ด๊ณ ํธ์ ๋ฉ์๋๋ฅผ ๋ง๋ค์ด์ ๋์น ์ ์๋ ๋ถ๋ถ์ ์ต์ํํ๋ค.
@Entity
@Table(name = "orders")
@Getter
@Setter
public class Order {
@Id
@Column(name = "id")
private String uuid;
@Column(name = "order_datetime", columnDefinition = "TIMESTAMP")
private LocalDateTime orderDatetime;
@Enumerated(EnumType.STRING)
private OrderStatus orderStatus;
@Lob
private String memo;
@ManyToOne // ํ์ ํ๋ช
์๊ฒ ์ฌ๋ฌ ์ฃผ๋ฌธ
@JoinColumn(name = "member_id", referencedColumnName = "id") // ๋ช
์ํ์ง ์์๊ฒฝ์ฐ ํด๋นํ๋๋ช
_PKํ๋๋ช
private Member member;
public void setMember(Member member){
if(Objects.nonNull(this.member)){
this.member.getOrders().remove(this);
}
this.member = member;
member.getOrders().add(this);
}
}
@Entity
@Table(name = "member")
public class Member {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE)
private Long id;
@Column(name = "name", nullable = false, length = 30)
private String name;
@Column(nullable = false, length = 30, unique = true)
private String nickName;
@Column
private int age;
@Column(name = "address", nullable = false)
private String address;
@Column(name = "description")
private String description;
@OneToMany(mappedBy = "member") // ์ฐ๊ด๊ด๊ณ ์ฃผ์ธ ์ค์ -> ์ฐ๊ด๊ด๊ณ ์ฃผ์ธ ๊ฐ์ฒด์ ํ๋๊ฐ
private List<Order> orders = new ArrayList<>();
public void addOrder(Order order){
order.setMember(this);
}
}
๊ณ ๊ธ ๋งคํ
JPA๋ RDB ํ ์ด๋ธ๊ณผ ๋ฏธํ๋ ๊ฐ์ฒด(Entity)๋ฅผ ๊ฐ์ฒด๋ต๊ฒ ์ฌ์ฉํ ์ ์๋๋ก ์ฌ๋ฌ๊ฐ์ง ๊ณ ๊ธ ๋งคํ ์ ๋ต์ ์ ๊ณตํด์ค๋ค. → ์์ ๊ด๊ณ ๋งคํ, ์๋ณ์ ํด๋์ค (๋ณตํฉํค) ๋ฑ
์์๊ด๊ณ๋งคํ
์กฐ์ธํ ์ด๋ธ ์ ๋ต (InheritanceType.JOINED)
Item.class
@Entity
@Table(name = "item")
@Getter
@Setter
@Inheritance(strategy = InheritanceType.JOINED)
public abstract class Item { // ์ถ์ class
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE)
private Long id;
private int price;
private int stockQuantity;
}
Food.class
@Entity
@Getter
@Setter
public class Food extends Item {
private String chef;
}
ํ์ธ
@Test
void inheritance_test() {
EntityManager entityManager = emf.createEntityManager();
EntityTransaction transaction = entityManager.getTransaction();
transaction.begin();
Food food = new Food();
food.setPrice(5000);
food.setStockQuantity(100);
food.setChef("๋ฐฑ์ข
์");
entityManager.persist(food);
transaction.commit();
entityManager.clear();
entityManager.find(Food.class, food.getId());
}
→ item ํ ์ด๋ธ์ ๋ง๋ค๊ณ food ํ ์ด๋ธ์ ๋ง๋ ๋ค.
→ join์ ํด์ find๋ฅผ ํ๋ค.
์ฑ๊ธํ ์ด๋ธ ์ ๋ต, ๋จ์ผํ ์ด๋ธ ์ ๋ต(InheritanceType.SINGLE_TABLE)
Item.class
@Entity
@Table(name = "item")
@Getter
@Setter
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name = "DTYPE") // ์ด๋ค ์ปฌ๋ผ์ ๊ตฌ๋ถ์๋ก ์ฌ์ฉํ ์ง
public abstract class Item { // ์ถ์ class
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE)
private Long id;
private int price;
private int stockQuantity;
}
Food.class
@Entity
@Getter
@Setter
@DiscriminatorValue("FOOD")
public class Food extends Item {
private String chef;
}
ํ์ธ
create table item(
DTYPE varchar(31) not null,
id bigint not null,
price integer not null,
stockQuantity integer not null,
power integer,
chef varchar(255),
height integer,
width integer,
primary key (id)
)
→ item table๋ง ์์ฑ๋จ
: ๋จ์ผ table์ด ์์ฑ๋๊ณ DiscriminatorColumn ์ปฌ๋ผ ๊ฐ์ผ๋ก ๊ตฌ๋ถ์ ํด์ค๋ค.
: ์์ ์ํฐํฐ๊ฐ ๋งคํํ ์ปฌ๋ผ์ ๋ชจ๋ null ์ ํ์ฉํด์ผํ๋ค๋ ๋จ์ ์ด ์๋ค.
: ์ฌ๋ฌ ํ ์ด๋ธ์ด ์์ฑ๋๋ฉด ๊ด๋ฆฌํด์ผํ ํ ์ด๋ธ์ด ๋ง์์ง๊ณ ๊ฐ์ฒด์งํฅ์ค๋ฝ์ง์์, ํ์ ์์ ๋ ์ฑ๊ธํ ์ด๋ธ ์ ๋ต์ ๋ง์ด ์ฌ์ฉํ๋ค.
MappedSuperClass
๋ถ๋ชจ class๊ฐ ์ค์ Entity๊ฐ ๋๋ class๋ ์๋์ง๋ง ์์์ ํ๋ฉด ์์ clss์์ ๋ถ๋ชจ class์ ํ๋๊ฐ ์ปฌ๋ผ ๊ฐ์ผ๋ก ์ถ๊ฐ mapping๋๋ค.
๊ณตํต์ผ๋ก ์ฌ์ฉ๋ ํ๋๊ฐ ์์ ๋ ์์ฃผ ์ฌ์ฉ๋๋ค.
@MappedSuperclass
@Getter
@Setter
public class BaseEntity {
@Column(name = "created_by")
private String createdBy;
@Column(name = "created_at", columnDefinition = "TIMESTAMP")
private LocalDateTime cratedAt;
}
//--------
@Entity
@Table(name = "orders")
@Getter
@Setter
public class Order extends BaseEntity{
@Id
@Column(name = "id")
private String uuid;
@Column(name = "order_datetime", columnDefinition = "TIMESTAMP")
private LocalDateTime orderDatetime;
@Enumerated(EnumType.STRING)
private OrderStatus orderStatus;
@Lob
private String memo;
@Column(name = "member_id", insertable = false, updatable = false) // fk
private Long memberId;
@ManyToOne // ํ์ ํ๋ช
์๊ฒ ์ฌ๋ฌ ์ฃผ๋ฌธ
@JoinColumn(name = "member_id", referencedColumnName = "id") // ๋ช
์ํ์ง ์์๊ฒฝ์ฐ ํด๋นํ๋๋ช
_PKํ๋๋ช
private Member member;
public void setMember(Member member){
if(Objects.nonNull(this.member)){
member.getOrders().remove(this);
}
this.member = member;
member.getOrders().add(this);
}
}
์๋ณ์ ํด๋์ค
JPA์์ ์๋ณ์๋ฅผ ๋ ์ด์ ์ฌ์ฉํ๋ ค๋ฉด ๋ณ๋์ ์๋ณ์ ํด๋์ค๋ฅผ ๋ง๋ค์ด์ผํ๋ค.
๋ณตํฉํค ๊ด๋ฆฌ๋ฅผ ์ํ 2๊ฐ์ง ์ ๋ต ์ ๊ณตํ๋ค.
@Id 2๊ฐ๋ฅผ ์ฌ์ฉํ๋๊ฑด ์ ์๋ ๊น?
→ JPA๋ ์์์ฑ ์ปจํ ์คํธ์์ ์ํฐํฐ๋ฅผ ๋น๊ตํ ๋, Id๋ฅผ key ๊ฐ์ผ๋ก ๋ณด๊ดํ๋ค. ํด๋น key๋ Equals & hashCode๋ฅผ ์ด์ฉํ์ฌ ๋๋ฑ์ฑ ๋น๊ต๋ฅผ ํ๋ค. ๊ทธ๋ ๊ธฐ๋๋ฌธ์ 2๊ฐ์ ํ๋๋ฅผ ๋ฐ๋ก ์ง์ ํ๋ฉด runtime error๋ฐ์.
์ค๋ณ์ ํด๋์ค ์ฌ์ฉ์
- Serializable ์ธํฐํ์ด์ค๋ฅผ ๊ตฌํํด์ผ ํ๋ค.
- eqauls, hashCode๋ฅผ ๊ตฌํํด์ผ ํ๋ค.
- ๊ธฐ๋ณธ ์์ฑ์๊ฐ ์์ด์ผ ํ๋ค.
- ์๋ณ์ ํด๋์ค๋ public ์ด์ด์ผ ํ๋ค.
@IdClass
@EqualsAndHashCode
@NoArgsConstructor
@AllArgsConstructor
public class ParentId implements Serializable {
private String id1;
private String id2;
}
// ---
@Entity
@Getter
@Setter
@IdClass(ParentId.class)
public class Parent {
@Id
private String id1;
@Id
private String id2;
}
์กฐํ
Parent parent = new Parent();
parent.setId1("id1");
parent.setId2("id2");
entityManager.persist(parent);
transaction.commit();
Parent entity = entityManager.find(Parent.class, new ParentId("id1", "id2"));
log.info("{}, {}", entity.getId1(), entity.getId2());
@Embeddedld (์ถ์ฒ)
์๋ณ์ ์์ฒด๋ฅผ ๋ช ์. ๋ ๋ง์ด ์ฌ์ฉํ๋ ๋ฐฉ์์ด๋ค.
@Getter
@Setter
@EqualsAndHashCode
@NoArgsConstructor
@AllArgsConstructor
@Embeddable
public class ParentId implements Serializable {
private String id1;
private String id2;
}
// ---
@Entity
@Getter
@Setter
public class Parent {
@EmbeddedId
private ParentId id;
}
์กฐํ
Parent parent = new Parent();
parent.setId(new ParentId("id1","id2"));
entityManager.persist(parent);
transaction.commit();
Parent entity = entityManager.find(Parent.class, new ParentId("id1", "id2"));
log.info("{}, {}", entity.getId().getId2(), entity.getId().getId1());
ํ๋ก์ ๊ฐ์ฒด
๊ฐ์ฒด ๊ทธ๋ํ ํ์
๊ฐ์ฒด๋ ๊ฐ์ฒด ๊ทธ๋ํ๋ก ์ฐ๊ด๋ ๊ฐ์ฒด๋ฅผ ํ์ํ๋ค. (order.getMember(), member.getOrders())
Entity๋ ๊ฐ์ฒด๊ฐ RDS์ ๋งคํ๋์ด ์์ด ์์ ๋กญ๊ฒ ๊ฐ์ฒด๋ฅผ ํ์ํ๋๋ฐ ์ ํ์ด ์๋ค.
JPA๋ ํ๋ก์ ๊ฐ์ฒด๋ผ๋ ๊ธฐ์ ์ ์์ฉํ์ฌ ์ฐ๊ด๋ ๊ฐ์ฒด๋ฅผ ์ฒ์๋ถํฐ ์กฐํํ์ง์๊ณ , ์ค์ ์ฌ์ฉํ๋ ์์ ์ ์กฐํํ ์ ์๋ค.
ํ๋ก์ ๊ฐ์ฒด
@Entity
@Table(name = "member")
public class Member extends BaseEntity {
...
@OneToMany(mappedBy = "member", fetch = FetchType.LAZY)
private List<Order> orders = new ArrayList<>(); // proxy
...
}
@Entity
@Table(name = "orders")
public class Order extends BaseEntity {
...
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "member_id", referencedColumnName = "id")
private Member member;
...
}
ํ ์คํธ
@Test
void proxy(){
EntityManager entityManager = emf.createEntityManager();
Order order = entityManager.find(Order.class,uuid);
Member member = order.getMember();
log.info("Member use before is-Loaded : {}",emf.getPersistenceUnitUtil().isLoaded(member)); // ์ฌ์ฉํ๊ธฐ์ member ๊ฐ์ฒด๋ proxy ๊ฐ์ฒด - false
var orders = member.getOrders();
log.info("Member use after is-Loaded : {}",emf.getPersistenceUnitUtil().isLoaded(member));
log.info(orders.toString());
}
ํ๋ก์์ ํน์ง
- ํ๋ก์ ๊ฐ์ฒด๋ Entity class๋ฅผ ์์ ๋ฐ์ ๋ง๋ค์ด์ง๋ค. (jpa๊ฐ ๋ด๋ถ์ ์ผ๋ก ์์์ ๋ฐ์ ๋ง๋ฌ)
- ์ค์ ํด๋์ค์ ๊ฒ ๋ชจ์์ด ๊ฐ๋ค. (์ฌ์ฉ์ ์ ์ฅ์์ ์ง์ง ๊ฐ์ฒด์ธ์ง ํ๋ก์ ๊ฐ์ฒด์ธ์ง ๊ตฌ๋ถํ์ง ์๊ณ ์ฌ์ฉ)
- ํ๋ก์ ๊ฐ์ฒด๋ ์ค์ ๊ฐ์ฒด์ ์ฐธ์กฐ๋ฅผ ๋ณด๊ดํ๋ค.(target)
- ํ๋ก์ ๊ฐ์ฒด๋ฅผ ํธ์ถํ๋ฉด ํ๋ก์ ๊ฐ์ฒด๋ ์ค์ ๊ฐ์ฒด์ ๋ฉ์๋๋ฅผ ํธ์ถํ๋ค.
์ด๊ธฐํ
- ํ๋ก์ ๊ฐ์ฒด๋ ์ฒ์ ์ฌ์ฉํ ๋ ํ๋ฒ๋ง ์ด๊ธฐํ ๋๋ค. (์ด๊ธฐํ์ ์๋ target๊ฐ์ด ์กด์ฌํ์ง์๋๋ค.)
- ํ๋ก์ ๊ฐ์ฒด๊ฐ ์ด๊ธฐํ๋๋ฉด, ํ๋ก์ ๊ฐ์ฒด๋ฅผ ํตํด ์ค์ Entity์ ์ ๊ทผํ ์ ์๋ค.
- ์์์ฑ ์ปจํ ์คํธ์ ๋์์ ๋ฐ์ ์ด๊ธฐํํ๋ค. ๋ฐ๋ผ์ ์ค์์ ์ํ์ ํ๋ก์๋ฅผ ์ด๊ธฐํํ๋ฉด (ํธ๋์ญ์ ๋ฐ์์ ํ๋ก์ ๊ฐ์ฒด๋ฅผ ์กฐํํ ๊ฒฝ์ฐ) LazyInitializationException ์์ธ๊ฐ ๋ฐ์ํ๋ค. - OSIV
์ง์ฐ๋ก๋ฉ(Lazy) vs ์ฆ์๋ก๋ฉ(EAGER)
- ์ง์ฐ ๋ก๋ฉ → ์ฐ๊ด๋ Entity๋ ์ค์ ์ฌ์ฉํ ๋ ์กฐํํ๋ค.
- @OneToMany, @ManyToMany์ฒ๋ผ Many๋ก ๋๋๋ ๊ฒ๋ค์ ๊ธฐ๋ณธ๊ฐ์ด ์ง์ฐ ๋ก๋ฉ์ด๋ค.
- ์ฆ์ ๋ก๋ฉ → ์ํฐํฐ๋ฅผ ์กฐํํ ๋, ์ฐ๊ด๋ Entity๋ ํจ๊ป ์กฐํํ๋ค.
- @ManyToOne, @OneToOne์ฒ๋ผ One์ผ๋ก ๋๋๋ ๊ฒ๋ค์ ๊ธฐ๋ณธ๊ฐ์ด ์ฆ์ ๋ก๋ฉ์ด๋ค.
ํ๋ก์ ๊ด๋ จ util ๋ฉ์๋
- PersistenceUnitUtil.isLoaded(Object entity) → ํ๋ก์ ์ธ์คํด์ค ์ด๊ธฐํ ์ฌ๋ถ ํ์ธ
- entity.getClass().getName() ์ถ๋ ฅ → ํ๋ก์ ํด๋์ค ํ์ธ
์ถ๊ฐ ํ์ต → n+1 ๋ฌธ์ , ์ง์ฐ ๋ก๋ฉ๊ณผ ํ์น ์กฐ์ธ
์์์ฑ ์ ์ด(CASCADE)
ํน์ ์ํฐํฐ๋ฅผ ์์ ์ํ๋ก ๋ง๋ค ๋, ์ฐ๊ด๋ ์ํฐํฐ๋ ํจ๊ป ์์์ํ๋ก ๋ง๋ค๊ณ ์ถ์๋, ์ฌ์ฉํ๋ค.
public enum CascadeType {
ALL, // ๋ชจ๋ ์ ์ฉ
PERSIST, // ์์
MERGE, // ๋ณํฉ
REMOVE, // ์ญ์
REFRESH, // REFRESH
DETACH; // DETACH
private CascadeType() {
}
}
@Test
void move_persist(){
EntityManager entityManager = emf.createEntityManager();
EntityTransaction transaction = entityManager.getTransaction();
transaction.begin();
Order order = entityManager.find(Order.class,uuid);
OrderItem item = new OrderItem();
item.setQuantity(10);
item.setPrice(10000);
order.addOrderItem(item);
transaction.commit();
}
์์์ฑ ์ ์ด๋ฅผ ํ์ง ์์์ ๊ฒฝ์ฐ
→ Order์ ์์์ฑ ์ํ์ด์ง๋ง, OrderItem์ ์์์ํ๊ฐ ๋์ง ๋ชปํ์๋ค.
→ addOrderItem๋ฅผ ํ์ง๋ง OrderItem์ insert ์ฟผ๋ฆฌ๊ฐ ๋ ๋ผ๊ฐ์ง ์์.
์์์ฑ ์ ์ด ์ถ๊ฐ
@Entity
@Table(name = "orders")
public class Order extends BaseEntity {
...
@OneToMany(mappedBy = "order", cascade = CascadeType.ALL)
private List<OrderItem> orderItems = new ArrayList<>();
...
}
๋ณดํต ์๋ณ๊ด๊ณ(identifying)์ ์๋ Entity๋ค ์ค @OneToMany์ต์ ์ ๊ฑด๋ค.
๊ณ ์๊ฐ์ฒด
@Test
void orphan() {
EntityManager entityManager = emf.createEntityManager();
// ํ์ ์กฐํ -> ํ์์ ์ฃผ๋ฌธ ๊น์ง ์กฐํ
Order order = entityManager.find(Order.class, uuid);
order.getOrderItems().remove(0); // orderItem๋ฅผ ์ ๊ฑฐํ๋ค. (๊ณ ์๊ฐ์ฒด ๋ฐ์)
EntityTransaction transaction = entityManager.getTransaction();
transaction.begin();
transaction.commit();
}
delete ์ฟผ๋ฆฌ๊ฐ ๋ ๋ผ๊ฐ์ง ์๊ณ ๊ณ ์๊ฐ์ฒด ๋ฐ์
@Entity
@Table(name = "orders")
public class Order extends BaseEntity {
...
// ๊ณ ์๊ฐ์ฒด๋ฅผ flush ์๊ฐ RDS์์๋ ์ญ์ ํ๊ฒ ๋ค.
@OneToMany(mappedBy = "order", cascade = CascadeType.ALL, orphanRemoval = true)
private List<OrderItem> orderItems = new ArrayList<>();
...
}
delete ์ฟผ๋ฆฌ๊ฐ ๋ ๋ผ๊ฐ๊ฒ๋๋ค.
orphanRemoval=true์ Cascade.REMOVE ์ฐจ์ด
https://tecoble.techcourse.co.kr/post/2021-08-15-jpa-cascadetype-remove-vs-orphanremoval-true/
Cascade.REMOVE๋ one์ ํด๋นํ๋ ์ํฐํฐ๊ฐ remove ๋ ๋, many์ ํด๋น๋๋ ์ํฐํฐ๋ค๋ ๋ชจ๋ ์ญ์ ๋๋๊ฒ. orphanRemoval=true๋ ์์ ๊ฒฝ์ฐ ํฌํจ, one์ list์ ์์๋ค์ ์ญ์ ํ๊ธฐ๋ง ํด๋(์ฆ, ๊ด๊ณ๋ฅผ ์ ๊ฑฐํ๊ธฐ๋ง ํด๋) ํด๋น ์ํฐํฐ๊น์ง ์ญ์ ๋๋ ๊ธฐ๋ฅ.
→ ์ฆ ๋ถ๋ชจ entity๊ฐ ์์์ ์๋ช ์ฃผ๊ธฐ๋ฅผ ๊ด๋ฆฌ ํ๊ฒ๋๋ค. ์ด๋ด ๊ฒฝ์ฐ ์์ ์์์ repository๊ฐ ์์ ํ์์๊ฒ๋ ๋๋ค.
์ถ์ฒ - backend dev course ๊ฐํ๊ตฌ๋
'Back-end ๋ฐ๋ธ์ฝ์ค > week 08 TIL (Jpa)' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
[TIL] 221209 - JPA : REST-API, API ๋ฌธ์ํ (0) | 2022.12.20 |
---|---|
[TIL] 221208 - JPA : SpringDataJPA (0) | 2022.12.14 |
[TIL] 221206 - JPA : ์์์ฑ ์ปจํ ์คํธ (0) | 2022.12.07 |
[TIL] 221205 - JPA : JPA ์๊ฐ (0) | 2022.12.07 |
๋๊ธ