Notice
Recent Posts
Recent Comments
Link
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 |
Tags
- Spring Boot
- Exception
- db
- java
- QueryDSL
- 알고리즘
- 그리디
- Android
- JPQL
- Thymeleaf
- 인프런
- transaction
- pointcut
- 스프링 핵심 기능
- 백준
- Servlet
- springdatajpa
- 스프링 핵심 원리
- Greedy
- 김영한
- SpringBoot
- http
- 자바
- 스프링
- JDBC
- spring
- jpa
- Proxy
- kotlin
- AOP
Archives
- Today
- Total
개발자되기 프로젝트
SpringDataJpa의 구현체 본문
1. SimpleJpaRepository
@Repository
@Transactional(readOnly = true)
public class SimpleJpaRepository<T, ID> ...{
@Transactional
public <S extends T> S save(S entity) {
if (entityInformation.isNew(entity)) {
em.persist(entity);
return entity;
} else {
return em.merge(entity);
}
}
...
}
- SpringDataJpa의 구현체
- @Repository
- 스프링 빈으로 등록
- JDBC/JPA에서 예외가 터지면 Spring에서 사용할 수 있는 예외로 바꿔줌
- 하부 기술을 바꿔도 예외 처리 메커니즘이 동일하게 유지됨.
- @Transaction(readOnly = true)
- SpringDataJpa의 모든 기능은 @Transactional이 걸린 상태에서 진행됨.
- JPA의 모든 변경은 트랜잭션 안에서 동작
- 스프링 데이터 JPA는 변경(등록, 수정, 삭제) 메서드를 트랜잭션 처리
- 서비스 계층에서 트랜잭션을 시작하지 않으면 리파지토리에서 트랜잭션 시작
- 서비스 계층에서 트랜잭션을 시작하면 리파지토리는 해당 트랜잭션을 전파 받아서 사용
- 그래서 스프링 데이터 JPA를 사용할 때 트랜잭션이 없어도 데이터 등록, 변경이 가능했음
(사실은 트랜잭션이 리포지토리 계층에 걸려있는 것임) - 참고로 ReadOnly=true로 지정하면
- flush를 안해서 약간의 성능 향상을 얻을 수 있당
2. 중요
- save() 메서드*
- 새로운 엔티티면 저장( persist )
- 새로운 엔티티가 아니면 병합( merge ) : 준 영속상태 -> 영속상태
- 가급적 merge를 쓰지 말고 변경감지(dirty checking)을 활용하자.
3. 새로운 엔티티를 구분하는 방법
- SimpleJpaRepository
- 새로운 엔티티인지 판단하는 시점은 save()메서드 호출 시점이다.
- 여기서 새로운 엔티티로 판단되면 persit, 아니면 merge
@Transactional
@Override
public <S extends T> S save(S entity) {
Assert.notNull(entity, "Entity must not be null.");
if (entityInformation.isNew(entity)) {
em.persist(entity);
return entity;
} else {
return em.merge(entity);
}
}
- 새로운 엔티티를 판단하는 전략
- 식별자가 객체일 때 null 로 판단-> pk가 null이면 새거!
pk에 값이 들어가는 시점은 persist 시점. - 식별자가 자바 기본 타입(primitive)일 때 0 으로 판단
long은 null이 불가능. Long은 null 가능.
- 식별자가 객체일 때 null 로 판단-> pk가 null이면 새거!
- 문제점: @GeneratedValue를 사용하지 않고 직접 pk를 지정하는 경우
- pk에 값이 있기 때문에 새로운 엔티티로 판단이 안됨
- 따라서 save()호출하면 persist가 아니라 merge가 호출됨
- merge는 DB에서 pk 로 엔티티를 찾은 다음 덮어쓰기함.
- 근데 해당 엔티티는 새로운 엔티티라 DB에 없는데?
- 잉? DB에 없으니 새거구나.. 이 때 insert 쿼리 날라감.
- 어휴 비효율적임.
- Persistable 인터페이스를 구현해서 판단 로직 변경 가능
- Persistable(idType)을 구현해주자.
- isNewI() 메서드를 구현하자.
- @CreatedDate는 persist 직전에 호출되기 때문에, createdDate의 존재 여부를 기준으로
- 새로운 엔티티인지 판단이 가능함.
@Entity
@Getter
@EntityListeners(AuditingEntityListener.class)
public class Item implements Persistable<String> {
@Id
private String id;
/**
* persist 전에 호출됨.@PrePersist
*/
@CreatedDate
private LocalDateTime createdDate;
protected Item() {
}
public Item(String id) {
this.id = id;
}
@Override
public boolean isNew() {
return createdDate == null;
}
}
4. 정리
- JPA 식별자 생성 전략이 @GenerateValue 면 save() 호출 시점에 식별자가 없으므로
- 새로운엔티티로 인식해서 정상 동작한다.
- 그런데 JPA 식별자 생성 전략이 @Id 만 사용해서 직접 할당이면
- 이미 식별자 값이 있는 상태로 save() 를 호출한다.
- 따라서 이 경우 merge() 가 호출된다. merge() 는 우선 DB를 호출해서 값을 확인하고,
- DB에 값이 없으면 새로운 엔티티로 인지하므로 매우 비효율 적이다.
- 따라서 Persistable 를 사용해서 새로운 엔티티 확인 여부를 직접 구현하게는 효과적이다.
- 참고로 등록시간( @CreatedDate )을 조합해서 사용하면
- 이 필드로 새로운 엔티티 여부를 편리하게 확인할수 있다.
- (@CreatedDate에 값이 없으면 새로운 엔티티로 판단)
5. GitHub : 210831 is NEW?
'인프런 > [인프런] Spring Data JPA' 카테고리의 다른 글
Projections (0) | 2021.08.31 |
---|---|
Query By Example (0) | 2021.08.31 |
Web 확장 : 도메인 클래스 컨버터, 페이징 & 정렬 (0) | 2021.08.31 |
Auditing (0) | 2021.08.30 |
사용자 정의 Repository (0) | 2021.08.30 |
Comments