[JPA] Spring Data JPA

Simple·2022년 3월 22일
0

JPA

목록 보기
4/6
  • JPA표준 스펙에 디폴트 생성자 있어야함
    • 이 때 protected하는 이유는 프록싱 개체가 생성할 때 접근하는데, private으로 막아 놓으면 생성이 안되기 때문이다.
    • protected Member(){} 이렇게 만들 수도 있겠지만, @NoArgsConstrucotr(access = AccessLevel.PROTECTED) 어노테이션을 이용하자!
  • JPA @Transactional 필수! Test코드도!
  • 테스트 코드 전체에 트랜잭션 먹였기 때문에 롤백되고 DB에 반영은 안한다.
  • p6spy → 사용할경우 성능 테스트 해봐야한다.(성능 좀 갉아먹는듯) 공부할 때나 테스트할 때 사용하면 좋을 것같다.
  • changeTeam에서 team.add(member)넣어도 그럼 기존의 팀이였던 곳에서 해당 멤버객체를 빼야하는거 아닌가??(궁금)
    • 원칙적으로 구 list에 있는 team을 제거하도록 코드를 작성하는 것이 맞습니다.

      다만 이 관계에서 list는 연관관계의 주인이 아니므로 실제 데이터베이스에 영향을 주지는 않습니다.

      객체까지 고려하면 list에 있는 team을 제거하는 것이 맞지만, 실용적인 관점에서 그냥 두어도 DB에서 삭제되지 않으므로 크게 상관은 없습니다^^

      → Team내의 members 객체는 우리가 만든거다. DB관점에서 보면 team테이블 안에 members컬럼이 있는게 아니므로 지장이 없다!!

  • 프록시를 통해 인터페이스 구현체를 Spring Data JPA가 한다.
  • spring.io > project > spring data spring data jpa의 Reference docs을 보면 제공하는 쿼리 메소드 기능이 있다. → 네이밍 만으로 쿼리를 생성할 수 있다!
  • 코드가독성과 유지보수를 위해 이름 기반 파라미터 바인딩을 사용하자
  • in절에 여러명의 이름을 넣어서 파라미터 바인딩도 가능!
    • 예시

      @Query("select m from Member m where m.username in :names")
      List<Member> findByNames(@Param("names")Collection<String> names);
  • 조회 결과가 많거나 없으면?

    • 컬렉션
      • 결과 없음: 빈 컬렉션 반환
    • 단건 조회
      • 결과 없음: null 반환
        결과가 2건 이상: javax.persistence.NonUniqueResultException 예외 발생
  • 있는지 없는지 모르면 → Optional 사용

  • List 비어있으면

List<Member> result = memberRepository.findListByUsername(”asdfasdf”);
if (result != null) 
//이렇게 List가 비어있는지 확인하는 거는 실무에서 안좋은 코드다.
//List는 절대 null이 아니니까 받아라, 없으면 empty collection이 반환된다. 
  • 모바일에서 더보기 기능으로 페이징 구현하는거는 Slice로 이용, 추가 count쿼리 없이 다음 페이지만 확인가능(내부적으로 limit + 1 조회)

    • 원하는 컨텐츠 개수에 ‘더보기'라는 부분이 +1이다.
    • count 쿼리 안나가고, size 3으로 걸었을 때 쿼리에서 limit 4가 날라간다.
  • 반환타입 Page냐,Slice냐에 따라서 totalCount를 날릴지 안날릴지를 결정

  • 페이징처리 성능이 안나오면? countQuery 분리해서만들기

    • 예시코드

      @Query(value = "select m from Member m left join m.team t",
              countQuery = "select count(m) from Member m")
      Page<Member> findByAge(intage,Pageablepageable);
    • 카운트 쿼리 분리(이건 복잡한 sql에서 사용, 데이터는 left join, 카운트는 left join 안해도 됨)

      실무에서 매우 중요!!!

  • query result에 findTop3by 해서 가져온 것 중 상위 3개 이렇게 뽑을 수도 있다.

  • 다시 강조 Entity를 그대로 반환하면 안된다.!!

    • Dto로 반환해라

      Page<MemberDto> toMap = page.map(member -> new MemberDto(member.getId(), member.getUsername(), null));
  • SpringDataJPA에서 벌크연산할 때 @Modifying넣어줘야 executeUpdate가 실행된다.! 중요

    • Modifying에 (clearAutomatically = true)를 추가해줘야 영속성 컨텍스트가 초기화 된다.
    • JPQL을 실행하면(쿼리를 날리면) DB에 플러쉬해준다. 다만, 여기서 중요한건 DB의 반영된 값과 영속성 컨텍스트에 있는 값이 다른게 문제가 되는 것이다.
    • (복습) em.flush() 영속성 컨텍스트의 내용을 DB에 반영(이 때 영속성 컨텍스트에 내용들이 사라지지 않는다.) em.clear() 해주면 영속성 컨텍스트를 비운다.
  • 간단할 때는 →EntityGraph, 복잡할 때는 JPQL + Fetch Join

    • why? 이미 복잡해진 시점은 findBy~~ 간단한 메소드로 못만들정도가 되었다는 뜻,, 그러므로 JPQL 이용
  • JPA Hint → JPA 쿼리 힌트(SQL 힌트가 아니라 JPA 구현체에게 날리는 힌트)

  • "org.hibernate.readOnly" 를 쿼리 힌트로 주면 읽기만 해서 변경 감지를 체크 안한다.(스냅샷 생성x)

  • 실시간 서비스가 많은 곳에는 Lock을 걸면 안된다. 실제로는 versioning이나 메커니즘으로 해결

  • 사용자 정의 인터페이스 구현 클래스명을 [class 명 + Impl]로 구현해야 Spring Data JPA가 인터페이스로 상속한 메소드를 찾아준다.

  • (참고) 화면상 API Repository / 핵심 비즈니스로직 or 복잡한 통계결과값이 나와야하는 API Repository는 분리하는 경우도 있다.

  • (개인 복습) API 응답값으로 나갈 때는 []처럼 Array형식이 아니라 {}형식으로 나가야 한다.(이유: Array형식은 유연성이 떨어진다. fetch조인 해도 service단에서 dto로 .stream.map.collect하기

  • 테이블 만들 때 등록일,수정일 필수로 넣으면 좋다!(모든 테이블에 적용)

  • 등록일,수정일 처럼 속성만 가져오고싶으면 @MappedSuperclass 를 속성만든 곳에 선언해라

  • 등록자 수정자는 필수가 아니므로, 필수인 BaseTimeEntity를 상속받게 해서 필요할 때만 BaseEntity 구현하게끔 하면 된다.

  • 도메인 클래스 컨버터는 “조회용"이다. 엔티티를 변경해도 DB에 반영되지 않는다.(간단하게 사용할 때만 가능하다고 생각하자)

  • localhost:8080/members?page=1&size=3 Pageable 인터페이스를 통해 값을 이렇게 넣을 수 있다.

  • 페이징 정보가 둘 이상일 때 → 접두사 이용

  • Entity 는 Dto를 보면 안되는데 , Dto는 Entity를 봐도 된다.

  • 서비스 계층에서 트랜잭션을 시작하지 않으면 리파지토리에서 트랜잭션 시작
    서비스 계층에서 트랜잭션을 시작하면 리파지토리는 해당 트랜잭션을 전파 받아서 사용

  • 병합(merge)는 쓰면 안된다 →변경감지 (기존의 것은 set으로 변경하는거)

  • save()는 값이 있으면 자동으로 merge가 나간다

    • @GeneratedValue가 있을경우→ pk가 자동생성해서 걱정 x
    • @GeneratedValue가 없을경우 → Persistable구현해서 isNew에 createdDate가 null인지 여부 판단으로 기존에 있던 데이터인지 아닌지를 확인한다
  • 엔티티 대신에 DTO에서 멤버 이름만 조회하고 싶을 때(=select절의 내용을 정하는 것) → Projection

  • projection의 한계 entity가 1개가 넘어가면 쓰기가 애매해진다.

  • 네이티브 SQL은 보통

    • QueryDSL로 해결, 안되면 JPQL

    • 둘 다 안되면 JdbcTemplate,mybatis 사용하자

      → 하지만 네이티브를 사용할 일은 거의 없다.

profile
몰입하는 개발자

0개의 댓글