스프링 데이터 JPA - 3

김강현·2023년 4월 27일
0

스프링 데이터 JPA

목록 보기
4/4

Spring Data JPA 구현체 분석

우리가 평소에 만드는 Repository 처럼,
@Repository, @Transactional(readOnly=true) 어노테이션을 가진다!

save 같은 친구들에겐 따로 @Transactional 을 달아주면서 처리!

save 함수

새로운 엔티티면 저장 (persist)
새로운 엔티티가 아니면 병합 (merge) <- 그냥 바꿔치기 해버리기!!
(잘 활용해야함!)

새로운 엔티티 인지 어떻게 판단?

@GeneratedValue 를 사용한다면, id 값이 존재하지 않는 것으로 확인을 할 수 있으나

생성자로 id 를 받아오는 경우에는, id 유무로 isNew 를 판단해야하는데, DB 에 올라가있지도 않은 엔티티에 merge 가 일어남 -> 사이드 이펙트 느낌 물씬

이런 방식으로해서 isNew를 덮어씌워주면 됨.

@Entity
@EntityListeners(AuditingEntityListener.class)
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Item implements Persistable<String> {
    @Id
    private String id;

    @CreatedDate
    private LocalDateTime createdDate;

    public Item(String id){
        this.id = id;
    }
    @Override
    public String getId(){
        return id;
    }

    @Override
    public boolean isNew(){
        return createdDate == null;
    }
}

기타 쿼리 기능들

Specifications

Jpa Criteria 를 활용해서 하는데, 김영한 개발자님 비추...

Query By Example

ExampleMatcher 와 Prove 로 Example 을 생성하여, 쿼리 조건 만들기!
(QueryDSL 로 해결 가능)

Projections

Spring Data Jpa 를 사용해서 조회를 하는데, Entity 말고 특정 값들만 가지고 오고 싶을때!

  • 엔티티 대신에 DTO를 바로 조회할때
public interface UsernameOnly {
    String getUsername();
}

이렇게 만들어 두고,

<MemberRepository.java> - interface
    ...
	List<Member> findByUsername(@Param("username") String username);
    List<UsernameOnly> findProjectionsByUsername(@Param("username") String username);

분명 interface 인데, 알아서 proxy 객체로 생성을 해줌!

이런식으로 지정해주는 것도 가능함!

  • class 기반의 Projections
public class UsernameOnlyDto {
    private final String username;

    public UsernameOnlyDto(String username) {
        this.username = username;
    }

    public String getUsername() {
        return username;
    }
}

생성자에서 받아오는 인자의 이름이 중요!! 이름 기반으로 엔티티 필드 찾아감

<MemberRepository.java> interface 에 이와 같이 구현
List<UsernameOnlyDto> findClassProjectionsByUsername(@Param("username") String username);

이래도 똑같이 작동함!! class 가 명시되어 있기에, proxy 객체가 아님

애초에 findByUsername 함수를 만들때, class type 을 지정하도록 할 수 있음

<T> List<T> findCustomByUsername(@Param("username") String username, Class<T> type);

사용 예시

List<UsernameOnlyDto> result = memberRepository.findCustomByUsername("m1", UsernameOnlyDto.class);

덧) join 된 엔티티의 필드들도 같이 가져올 수 있음!!
실무에서는 단순할 때만 사용하고, 복잡해지면 QueryDSL을 사용하자!!

Native Query

JPQL 문법이 아닌, 실제 SQL 문법으로 적어서 쓸 수 있도록 지원해줌!!
위에서 이야기한 join 된 복합 DTO를 가져오는 것이 좀더 편리하게 가능함!

public interface MemberProjection {
    Long getId();
    String getUsername();
    String getTeamName();
}
    @Query(value = "select m.member_id as id, m.username, t.name as teamName " +
        "from member m left join team t",
            countQuery = "select count(*) from member",
            nativeQuery = true)
    Page<MemberProjection> findByNativeProjection(Pageable pageable);

역시나 가급적 안씀! JPQL, QueryDSL 로 왠만하면 다 해결하려고 함!!

profile
this too shall pass

0개의 댓글