public interface MemberRepositoryCustom {
List<Member> findMemberCustom();
}
@RequiredArgsConstructor
public class MemberRepositoryImpl implements MemberRepositoryCustom {
private final EntityManager em;
@Override
public List<Member> findMemberCustom() {
return em.createQuery("select m from Member m")
.getResultList();
}
}
사용자 정의 구현 클래스
public interface MemberRepository extends JpaRepository<Member, Long>, MemberRepositoryCustom {
...
}
List<Member> result = memberRepository.findMemberCustom();
이렇게 직접 커스텀한 코드를 작성하고 다음과 같이 호출을 하게 되면 구현 클래스의 코드가 실행된다.
실무에서는 주로 QueryDSL이나 SpringJdbcTemplate을 함께 사용할 때 사용자 정의
리포지토리 기능 자주 사용
핵심 비즈니스 로직에 관련되면 사용자 정의 인터페이스를 만들어서 상속하면 되지만 핵심 비즈니스 로직에 관련이 없고 특정 화면에서 사용되는 경우라면
인터페이스가 아닌 클래스로 만들어서 스프링 빈으로 등록해 사용하는 것이 좋다. 위에서 예시로 든 사용자 정의 인터페이스가 아닌 클래스로 만들면 다음과 같다.
@MappedSuperclass
@Getter
public class JpaBaseEntity {
@Column(updatable = false)
private LocalDateTime createdDate;
private LocalDateTime updatedDate;
@PrePersist
public void prePersist() {
LocalDateTime now = LocalDateTime.now();
createdDate = now;
updatedDate = now;
}
@PreUpdate
public void preUpdate() {
updatedDate = LocalDateTime.now();
}
}
--------------------------------------------------
public class Member extends JpaBaseEntity {} //Member 엔티티에 추가
@Test
public void JpaEventBaseEntity() throws Exception {
//given
Member member = new Member("member1");
memberRepository.save(member); // @PrePersist
Thread.sleep(100);
member.setUsername("member2");
em.flush(); //@PreUpdate
em.clear();
//when
Member findMember = memberRepository.findById(member.getId()).get();
//then
System.out.println("findMember.getCreatedDate = " + findMember.getCreatedDate());
System.out.println("findMember.getUpdatedDate = " + findMember.getUpdatedDate());
}
스프링 데이터 JPA 를 사용하면 이것보다 더 편리하게 이용가능하다.
@EnableJpaAuditing // 추가
@SpringBootApplication
public class DataJpaApplication {
@EntityListeners(AuditingEntityListener.class) //이벤트를 기반으로 동작하기 때문에 넣어줘야 한다.
@MappedSuperclass
@Getter
public class BaseEntity {
@CreatedDate
@Column(updatable = false)
private LocalDateTime createdDate;
@LastModifiedDate
private LocalDateTime lastModifiedDate;
}
@EntityListeners(AuditingEntityListener.class)
@MappedSuperclass
public class BaseEntity {
....
@CreatedBy
@Column(updatable = false)
private String createdBy;
@LastModifiedBy
private String lastModifiedBy;
@EnableJpaAuditing // 추가
@SpringBootApplication
public class DataJpaApplication {
...
@Bean
public AuditorAware<String> auditorProvider() {
return () -> Optional.of(UUID.randomUUID().toString());
}
참고 : 저장시점에 등록일, 등록자는 물론이고, 수정일, 수정자도 같은 데이터가 저장된다. 데이터가 중복 저장되는 것 같지만, 이렇게 해두면 변경 컬럼만 확인해도 마지막에 업데이트한 유저를 확인 할 수 있으므로 유지보수 관점에서 편리하다.
시간에 대한 데이터는 대부분의 테이블이 다 사용하지만 등록자,수정자는 필요 없는 경우가 있다
따라서 이를 BaseTimeEntity(시간)와 그것을 상속받는 BaseEntity 로 분리해주면 유용하다.
@EntityListeners(AuditingEntityListener.class) //이벤트를 기반으로 동작하기 때문에 넣어줘야 한다.
@MappedSuperclass
@Getter
public class BaseTimeEntity {
@CreatedDate
@Column(updatable = false)
private LocalDateTime createdDate;
@LastModifiedDate
private LocalDateTime lastModifiedDate;
}
@EntityListeners(AuditingEntityListener.class) //이벤트를 기반으로 동작하기 때문에 넣어줘야 한다.
@MappedSuperclass
@Getter
public class BaseEntity extends BaseTimeEntity {
@CreatedBy
@Column(updatable = false)
private String createdBy;
@LastModifiedBy
private String lastModifiedBy;
}
하루에 해나갈수 있는 할당량을 점점 늘려 나가자.