-
Notifications
You must be signed in to change notification settings - Fork 0
Open
Description
끄적끄적
-
이 책을 읽지 않고 직접 부딪혔다면 시간이 많이 들었을 법한 깊은 내용이 많이 나와서 좋았다
- 예외 처리
- JPA 예외는 크게 두가지로 나뉜다
- 트랜잭션 롤백을 표시하는 예외
- 무조건 롤백됨 (심지어 강제로 커밋하려고 해도)
- EntityExixstException, EntityNotFoundExcetpion, RollbackException, ...
- 트랜잭션 롤백을 표시하지 않는 예외
- 개발자가 롤백할지 커밋할지 선택
- NoResultException, NonUniqueResultException, QueryTimeoutException, ...
- 트랜잭션 롤백을 표시하는 예외
- Data-Access 계층의 Exception에 의존하지 않기 위해, 추상화된 Spring 예외로 변경된다
-
다른 것들은 이해가 되는데, JPA 어쩌고 Exception들은 여전히 의존성 갖는거 아닌가..? (e.g. JpaSystemException)
- PersistentExceptionTranslationPostProcessor를 Bean으로 등록해야 AOP를 적용해서 변환해준다
-
- JPA 예외는 크게 두가지로 나뉜다
- 트랜잭션 롤백
- 롤백하면 DB의 데이터는 롤백되지만, 영속성 컨텍스트의 객체는 수정 상태로 남아있음
- 기본적으론 트랜랙션 AOP 종료 시점에 트랜잭션을 롤백하면서 영속성 컨텍스트도 같이 종료되기 때문에 문제 발생하지 않음
- 하지만 OSIV처럼 영속성 컨텍스트의 범위를 트랜잭션 범위보다 넓어서 여러 트랜잭션이 하나의 영속성 컨텍스트를 사용하면 문제 발생
- Spring Framework에서는 이런 경우 트랜잭션 롤백 시 영속성 컨텍스트를 초기화해서 문제를 예방함
- JpaTransactionManager::doRollback()
- Spring Framework에서는 이런 경우 트랜잭션 롤백 시 영속성 컨텍스트를 초기화해서 문제를 예방함
- Entity 비교
- 1차 캐시는 영속성 컨텍스트와 생명주기를 같이 함
- 같은 영속성 컨텍스트에서 Entity를 조회하면 항상 같은 Entity를 반환함
- 동등성 비교(equals) 수준이 아니라 정말 주소값이 같은(==) 인스턴스 반환
- 다른 영속성 컨텍스트에서 Entity를 비교하면 동일성 비교(==)는 실패함
- 이 때문에 비즈니스 키를 이용한 동등성 비교(equals)를 권장함
- 프록시 심화
- 같은 영속성 컨텍스트 안에서는 아래를 통해 동일성 비교를 보장한다
- Proxy로 먼저 조회된 Entity가 있으면, 다음 조회에서도 Proxy를 반환함
- 원본 Entity로 먼저 조회된 Entity가 있으면, 다음 조회에서도 원본 Entity 반환 (Proxy 반환할 필요 없음)
- Proxy 동등성 비교(equals) 시 아래 같은 경우 문제 발생 가능
if (this.getClass() != obj.getClass()) return false;- Proxy는 원본 Class를 상속받아서 만들어지므로 실패함
instanceof로 변경해야 함
if(name != null ? !name.equals(member.name) : member.name != null) return false;- equals의 경우 보통 필드에 직접 접근하는데, Proxy 객체의 멤버 변수는 null이므로 실패함
- getter를 사용해야 원본 객체의 값을 프록싱함
- 다형성을 프록시 조회와 함께 사용하면 instanceof도 실패하거나, 캐스팅 문제 발생 가능
- Item 타입의 프록시는 실제 원본 클래스가 Book이더라도 Item 타입의 프록시로 생성됨 (당연)
- 여러 해결 방법이 있는데, 별도 인터페이스 제공하는게 그나마 나을 듯
- 비지터 패턴 사용방법이 확실해보이긴 하지만, 이해하기 어렵고 유지보수가 힘들어져 현실성이 떨어질 듯
- 같은 영속성 컨텍스트 안에서는 아래를 통해 동일성 비교를 보장한다
- N+1 문제
- 즉시로딩으로 하면 상위 객체 호출 시 즉시 문제 발생
- 지연로딩으로 하면 상위 객체 호출 시 문제 발생하지 않지만, 하위 객체 foreach 시 뒤늦게 문제 발생
- 여튼 둘 다 문제 발생. 해결방법은 보통 Fetch Join
- Hibernate BatchSize를 통해 어느정도 해소는 할 수 있지만
N+1 문제를(N/batch size)+1 문제정도로 줄여줄 뿐 - 추천하는 방법: 지연로딩만 사용하고, 최적화 필요한 곳에서 JPQL + Fetch Join 사용
- 참고
@**ToOne매핑은 기본적으로 즉시로딩@**ToMany매핑은 기본적으로 지연로딩
- 참고
- 읽기 전용 쿼리의 성능 최적화
- 메모리 최적화
- 영속성 컨텍스트는 변경 감지를 위해 Snapshot을 조회해서 메모리를 소모함
스칼라 타입으로 조회하거나read-only query hint를 사용하면, Snapshot 생성하지 않아 메모리 최적화 가능
- 속도 최적화
read-only transaction을 사용하면, flush 호출을 막아서 속도 최적화 가능
- 메모리 최적화
- 배치 처리
- 성능이 많이 중요하면 그냥 JDBC 쓰자..!
- JPA를 쓰면 flush 호출(속도), 1차 캐시(메모리) 등 성능을 저하시키는 요인이 많아 신경써야 하는게 많다.
- Query Hint
- 이것도 그냥 native query or JDBC 쓰는게 나아보임
- 트랜잭션을 지원하는 쓰기 지연과 성능 최적화
- INSERT SQL 모아서 한 번에 DB로 전송
- JDBC만 사용하면 코드가 지저분해짐
- JPA를 쓰면 쉽게 적용 가능 (쓰기 지연 & 변경 감지 덕분에 성능과 개발 편의성 둘 다 잡을 수 있음)
- DB 테이블 Row에 Lock이 걸리는 시간을 최소화
- flush 전까지는 DB의 데이터를 실제로 변경하지 않으므로
- DB 테이블 Row에 Lock이 걸리는 시간을 최소화
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
No labels