Open seungriyou opened 9 months ago
파라미터가 너무 많거나 복잡하면 빌더 패턴 사용, 실용적인 관점에서 롬복 빌더를 생성자와 함께 사용(생성자 위에 애노테이션 붙여서)
setter를 열고 싶지 않을 때 빌더 패턴을 사용한다!
[!caution] 중요한 것은 바로 생성 시점에 필요한 값을 다 넣고, 꼭 필요한 경우에만 수정할 수 있게 엔티티를 설계하는게 유지보수에 좋다는 것이다!
@Transactional(readOnly = true)
@Transactional(readOnly = true)
의 기능은 다음과 같다.
트랜잭션 커밋 시점에 플러시를 호출하지 않는다.
dirty checking 같은 부분이 생략되므로 성능 최적화가 발생한다. 또한, 엔티티를 변경해도 변경 내용이 반영되지 않는다.
JPA에서 모든 연관관계는 꼭! LAZY로 설정해야 한다. 실무에서 EAGER는 그냥 없다고 생각하는 것이 더 좋다.
그렇게 해야 member
만 필요해서 조회할 때, member
만 조회가 됩니다.
그런데 때때로 특정 기능에서는 member
와 team
이 함께 필요한 경우가 있다. 이때 그 상황에 맞는 로직에서 fetch join
을 사용하면 n+1 문제를 해결할 수 있다.
⇒ 넓게 보면 N+1 문제는 즉시 로딩 뿐만 아니라 지연 로딩에서도 발생 가능하며, 지연 로딩 시 페치 조인을 통해 N+1 문제를 해결할 수 있다!
em.flush()
다음과 같은 테스트 코드에서, 스프링 데이터 JPA(JPA)에서 제공하는 save()
와 벌크 연산(= JPQL) bulkAgePlus()
에서의 em.flush()
동작에 대해 알아보겠다.
@Test
public void bulkUpdate() {
// given
memberRepository.save(new Member("member1", 10));
memberRepository.save(new Member("member2", 19));
memberRepository.save(new Member("member3", 20));
memberRepository.save(new Member("member4", 21));
memberRepository.save(new Member("member5", 40));
// when
int resultCount = memberRepository.bulkAgePlus(20);
스프링 데이터 JPA(JPA) 에서 제공하는 save()
호출 시
save()
메서드는 주로 엔티티를 영속성 컨텍스트에 저장하는 데에 사용되기 때문에, 이후 트랜잭션이 커밋되거나 명시적으로 flush()
를 호출할 때까지 DB에 반영되지 않는다.
벌크 연산(= JPQL) bulkAgePlus()
호출 시
bulkAgePlus()
메서드 내에서 JPQL을 사용하는 벌크 연산을 실행하면, JPA는 이를 DB에 즉시 반영하기 위해 flush()
를 호출한다. 즉, 영속성 컨텍스트를 무시하고 벌크 연산이 직접 DB에 적용된다.
따라서 벌크 연산 후 clear()
를 호출하는 것은 영속성 컨텍스트와 DB 간의 일관성을 유지하기 위해 중요하다. 벌크 연산은 영속성 컨텍스트에 있는 엔티티 상태를 업데이트하지 않으므로, DB에는 변경 사항이 반영되어 있지만 영속성 컨텍스트는 그 변경 사항을 모르는 상태가 된다. 그러므로 clear()
를 호출하여 영속성 컨텍스트를 초기화하고 필요한 경우 다시 로드하여 일관성을 유지해야 한다.
@MappedSuperclass
강의에서는 createdDate
나 updatedDate
와 같은 변수를 임베디드 타입으로도, @MappedSuperclass
로도 정의할 수 있었다.
그 둘의 차이는 무엇이고, 실무에서는 어떤 것을 사용할까?
구분 | 차이점 |
---|---|
임베디드 타입 | 위임 |
@MappedSuperclass |
상속 |
이런 경우에는 상속을 사용하는 것이 더욱 편리하다.
예를 들어, 임베디드 타입으로 만들게 되면 다음과 같다.
class TraceDate {
TYPE createdDate;
TYPE updatedDate;
}
이러한 경우, JPQL 쿼리를 하려면 항상 임베디드 타입을 한 번 거쳐야 한다.
select m from Member m where m.traceDate.createdDate > ?
하지만 @MappedSuperclass
상속을 사용하면 다음과 같이 간단하다.
select m from Member m where m.createdDate > ?
이러한 편리함과 직관성으로 인해 상속을 주로 사용한다!
updatedAt
글을 조회하면 조회수가 1 올라가는 로직이 존재한다고 가정하자.
이때, JPA Auditing을 사용하고 있다면, 조회수가 오를 때마다 lastModifiedDate
필드도 함께 업데이트 되게 된다.
이러한 경우에는 updateAt
이 변경되어야 할 비즈니스 로직에서만 따로 업데이트 해주어야 한다.
Contents
em.flush()
@MappedSuperclass
updatedAt