2021. 3. 22. 16:34ㆍJPA
1. Entity 에 Setter 는 닫아두자.
이론적으로 Entity는 Getter, Setter 모두 제공하지 않고 별도의 메서드를 제공하는 게 가장 이상적이다. 하지만 실무에서는 엔티티의 데이터를 조회할 일이 많고 또 Getter 같은 경우 아무리 호출해도 어떠한 일이 발생하지 않는다. 그래서 Getter 의 경우 열어두는 것이 편리하다.
하지만 Setter 는 다르다. Setter 를 호출하면 데이터가 변한다. 미래에 Setter 로 인하여 Entity 의 데이터가 왜 변경되었는지 추적하기 힘들어지므로 Setter 는 Close 해두게는 좋다.
즉, Entity는 상태가 변하지 않는 불변성을 지켜야 한다.
2. @ManyToMany를 사용하지 말자
@ManyToMany 는 편리한 것 같지만, 중간 테이블에 컬럼을 추가할 수 없고, 세밀하게 쿼리 실행하기 어렵기 때문에 실무에서 사용하기에는 한계가 있다.
중간 Entity를 만들고 @ManyToOne, @OneToMany로 매핑해서 사용하자.
3. 값 타입은 변경 불가능하게 설계해야 한다.
값 타입은 변경 불가능하게 설계해야 한다.
- @Setter 를 제거하고, 생성자에서 값을 모두 초기화해서 변경 불가능한 클래스를 만들자. JPA 스펙상 엔티
티나 임베디드 타입( @Embeddable )은 자바 기본 생성자(default constructor)를 public 또는
protected 로 설정해야 한다. public 으로 두는 것보다는 protected 로 설정하는 것이 그나마 더 안전
하다.
- JPA가 이런 제약을 두는 이유는 JPA 구현 라이브러리가 객체를 생성할 때 리플랙션 같은 기술을 사용할 수 있도록 지원해야 하기 때문이다.
4. 모든 연관관계는 지연로딩으로 설정하자
- 즉시(EAGER)로딩은 예측이 어렵고, 어떤 SQL이 실행될지 추적하기 어렵다. 특히 JPQL을 실행할 때 N+1 문제가 자주 발생한다.
- 실무에서 모든 연관관계는 지연(LAZY) 로딩으로 설정해야 한다.
- 연관된 엔티티를 함께 DB에서 조회해야 하면, fetch join 또는 엔티티 그래프 기능을 사용한다.
- @XToOne(OneToOne, ManyToOne) 관계는 기본이 즉시 로딩이므로 직접 지연 로딩으로 설정해야 한다.
5. 컬렉션은 필드에서 초기화 하자
- 컬렉션은 필드에서 바로 초기화 하는 것이 안전하다.
- null 문제에서 안전하다.
@Entity
public class Member {
public Member() {
orders = new ArrayList<>();
}
@OneToMany(mappedBy = "member")
private List<Order> orders;
}
<비추천>
@Entity
public class Member {
@OneToMany(mappedBy = "member")
private List<Order> orders = new ArrayList<>();
}
<추천>
- 하이버네이트는 엔티티를 영속화 할 때, 컬랙션을 감싸서 하이버네이트가 제공하는 내장 컬렉션으로 변경한
다. 만약 getOrders() 처럼 임의의 메서드에서 컬력션을 잘못 생성하면 하이버네이트 내부 메커니즘에 문
제가 발생할 수 있다. 따라서 필드레벨에서 생성하는 것이 가장 안전하고, 코드도 간결하다.
Member member = new Member();
System.out.println(member.getOrders().getClass()); // 1번 로그
// 영속화 !
em.persist(team);
System.out.println(member.getOrders().getClass()); // 2번 로그
// 1번 로그 출력: class java.util.ArrayList
// 2번 로그 출력: class org.hibernate.collection.internal.PersistentBag
위 코드의 출력 결과를 보면, 영속화 후에는 컬렉션의 Class 가 달라진 걸 볼 수 있다. 이는 하이버네이트가 컬렉션의 변경된 점을 추적하기 위해 하이버네이트 내장 컬렉션으로 변경한 결과이다.
만약 하이버네이트 내장 컬렉션으로 바뀐 시점에, 누군가 Entity 의 Setter 를 호출하여 값을 바꾼다면 장애로 이어진다
6. 테이블, 컬럼명 생선 전략
- 스프링 부트에서 하이버네이트 기본 매핑 전략을 변경해서 실제 테이블 필드명은 다르다.
- SpringPhysicalNamingStrategy 전략
1. 카멜 케이스 → 언더스코어
2. .(점) → _(언더스코어)
3. 대문자 → 소문자
7. 연관관계 편의 메소드를 잘 이용하자.
public void setMember(Member member) {
this.member = member;
member.getOrders().add(this);
}
public void addOrderItem(OrderItem orderItem) {
orderItems.add(orderItem);
orderItem.setOrder(this);
}
public void setDelivery(Delivery delivery) {
this.delivery = delivery;
delivery.setOrder(this);
}
엔티티에 Setter 가 없으니, 위처럼 변경(Update) 가능 한 메소드를 잘 활용하자.
'JPA' 카테고리의 다른 글
[JPA] "InstantiationException" 발생 - 기본 생성자의 중요성 (0) | 2021.03.11 |
---|