JPA

스프링 데이터 JPA - 다양한 기능2

Rudtjs 2022. 7. 5. 01:06

JPA Hint & Lock

 

Hint

JAP 구현체에게 제공하는 힌트 (SQL 힌트랑 다름)

 

Read Only 

Hint를 사용하면 영속성 콘텍스트에 저장되는 것을 막고 변경사항이 업데이트되지 않도록 막을 수 있습니다. 말 그대로 읽을 수만 있게 해주는 기능입니다.

 

실습

Repository에 사용자 이름으로 조회하는 메서드를 추가합니다.

@QueryHints(value = @QueryHint(name = "org.hibernate.readOnly",value = "true"))
Member findReadOnlyByUsername(String username);

@QueryHints 어노테이션을 추가해줌으로써 Hint를 사용할 수 있고 hibernate의 readOnly 속성을 true로 지정해야 합니다.

테스트 클래스로 실행을 해보겠습니다.

@Test
public void queryHint() {
    Member member1 = new Member("member1", 10);
    memberRepository.save(member1);
    entityManager.flush();
    entityManager.clear();

    Member findMember = memberRepository.findReadOnlyByUsername("member1");
    findMember.setUsername("member2");

    entityManager.flush();
}

Hint를 넣지 않고 동작을 해보면 insert, select , update 순으로 실행이 됩니다. 여기서 hint를 넣고 실행을 하면?

hint 사용시 결과

 

select 쿼리만 실행되는 걸 확인할 수 있습니다. 이렇게 되면 영속성 컨텍스트를 여러 번 거칠 필요가 없기 때문에 메모리를 절약할 수가 있습니다. 

 

Hint를 사용한다고 성능이 극적으로 좋아지지는 않습니다. 단순 조회나 부하가 심한 API에만 적용을 해보고, 성능 튜닝에 관한 부분은 성능 테스트 후, 판단하도록 합시다.

 

Lock

  • 낙관적 잠금 : 낙관적 잠금은 데이터 갱신 시 경합이 발생하지 않을 것이라고 낙관적으로 보고 잠금을 거는 기법 
  • 비관적 잠금 : 동일한 데이터를 동시에 수정할 가능성이 높다는 비관적인 전제로 잠금을 거는 방식
  • 암시적 잠금 : 프로그램 코드상에 명시적으로 지정하지 않아도 잠금이 발생하는 것 
  • 명시적 장금 : 프로그램을 통해 의도적으로 잠금을 실행하는 명시적 잠금 

Lock에는 이렇게 다양한 종류가 있지만 위의 상황에 필요한 비관적 잠금을 사용해보겠습니다. 

 

@Lock(LockModeType.PESSIMISTIC_WRITE)
List<Member> findLockByUsername(String username);

 

Test 코드로 만들어 

    @Test
    void lock() {
        memberRepository.save(new Member("member1", 10));
        entityManager.flush();
        entityManager.clear();

        List<Member> member = memberRepository.findLockByUsername("member1");
    }

실행을 해보면

2022-07-04 11:44:22.562 DEBUG 38193 --- [           main] org.hibernate.SQL                        : 
    select
        member0_.member_id as member_i1_0_,
        member0_.age as age2_0_,
        member0_.team_id as team_id4_0_,
        member0_.username as username3_0_ 
    from
        member member0_ 
    where
        member0_.username=? for update

아까와 다르게 마지막에 for update가 추가된걸 볼 수 있습니다. 이 구문이 있기 때문에 동시 다발적으로 업데이트가 될 때도 정합성이 보장됩니다.

 

JPA 확장 기능

 

사용자 정의 리포지토리 구현

 

스프링 데이터 JPA가 구현체를 자동으로 생성해주지만 인터페이스를 직접 구현해야 하는 경우가 생길 수 있다.  직접 구현하는 방법을 알아보자

  • JPA 직접 사용
  • 스프링 JDBC Template 사용
  • Mybatis 사용
  • 데이터베이스 커넥션 사용
  • Querydsl 사용

1. 사용자 정의 인터페이스

public interface MemberRepositoryCustom {
    List<Member> findMemberCustom();
}

 

2. 인터페이스 구현 클래스

@RequiredArgsConstructor
public class MemberRepositoryImpl implements MemberRepositoryCustom {

    private final EntityManager em;

    @Override
    public List<Member> findMemberCustom() {
        return em.createQuery("select m from Member m")
                .getResultList();
    }
}

여기서 중요한점은 이름 규칙이다. 리포지토리 인터페이스 이름 + Impl

 

3. 사용자 정의 인터페이스 상속

public interface MemberRepository extends JpaRepository<Member, Long>, MemberRepositoryCustom { ... }

이렇게 사용 하면, 여러 인터페이스를 분리해서 사용하는 것이 가능해지고 유지보수가 더 쉬워진다.

 

 

Auditing

 

데이터베이스에서는 시간(생성일, 수정일)이 거의 필수적으로 들어갑니다. 즉 시간 컬럼은 공통적으로 사용한다는 말인데 JPA에서는 시간에 대해서 자동으로 값을 넣어주는 Auditing으로 처리하고 있습니다.

 

Auditing을 사용하기 위해서는 설정이 먼저 필요합니다. 

1. 설정

  • @EnableJpaAuditing → 스프링 부트 클래스에 적용
  • @EntityListeners(AuditingEntityListener.class) → 엔티티에 적용

2. 적용

public class BaseTimeEntity {
//  데이터 생성 날짜 자동 저장 어노테이션
 @CreatedDate
 @Column(updatable = false)
 private LocalDateTime createdDate;
 
 // 데이터 수정 날짜 자동 저장 어노테이션
 @LastModifiedDate
 private LocalDateTime lastModifiedDate;
}

public class BaseEntity extends BaseTimeEntity {
// 데이터 생성자 자동 저장 어노테이션
 @CreatedBy
 @Column(updatable = false)
 private String createdBy;
 
 // 데이터 수정자 자동 저장 어노테이션
 @LastModifiedBy
 private String lastModifiedBy;
}
  • 저장시점에 등록일, 등록자는 물론이고, 수정일, 수정자도 같은 데이터가 저장된다. 데이터가 중복 저장되는 것 같지만, 이렇게 해두면 변경 컬럼만 확인해도 마지막에 업데이트한 유저를 확인할 수 있으므로 유지보수 관점에서 편리하다.

 

이렇게 스프링 데이터 JPA에 대해 알아봤는데 아직 어디에서 어떻게 써야 효율적인지  모르는 부분이 많습니다. 그래서 토이 프로젝트를 하나 만들고 스프링 데이터 JPA를 적용시킴으로써 효울적으로 다룰 수 있게 노력해보겠습니다.  

 

 

reference

김영한 선생님 JPA 강의

https://reiphiel.tistory.com/entry/understanding-jpa-lock

 

'JPA' 카테고리의 다른 글

스프링 데이터 JPA - 다양한 기능들  (0) 2022.06.26
스프링 데이터 JPA - 다양한 쿼리 기능  (0) 2022.06.21
JPA 영속성 컨텍스트란?  (0) 2022.06.15
JPA를 사용해야 하는 이유  (0) 2022.06.14