본문 바로가기

JPA

Spring jpa의 사실과 오해 우연히.. youtube를 보던 도중 NHN에서 올린 Spring JPA의 사실과 오해라는 알고리즘이 내 유튜브에 떳고 해당 영상을 시청하고 새롭게 알게된 지식을 다시한번 기록하기위해 작성하려고 합니다.ㅎㅎ 흔히 JPA를 통해 entity설계 및 로직을 구현해본사람이면 N+1문제는 알 것이빈다. 한번의 쿼리가 나갈 것을 예상하고 쿼리를 작성했는데 연관관계의 의해 N번의 쿼리가 추가적으로 나가는 것을 N+1이라고하는데 , 이것이 EAGER Fetch 타입뿐만아니라 lazy로딩에서도 발생될 수 있고, findAll과 같이 jpql이 먼저 수행되는 로직이 작성됐을 때도 발생할 수 있다는 이야기를 해주셨다. 위에 설명을 읽으니까 너무 이해가 잘 됐다. findAll같은 경우 단일 레코드 조회가 아닌경우, 해당 .. 더보기
Spring Data JPA 에서 Page와 Slice Spring Data JPA를 쓰면서 동적 페이징처리가 매우 쉬워졌다. 바로 PageRequest 객체가 자동으로 limit, offset으로 페이징하던 이전 쿼리를 자동으로 생성해주기 때문이다. Page와 Slice PageRequest 객체를 통해 페이징을 할때 반환형으로 Page와 Slice를 사용한다. 두 객체의 결과물과 성능은 어떤 차이가 있는지 확인해보자 Repository Interface //MemberRepository ... Page findPageBy(Pageable pageable); Slice findSliceBy(Pageable pageable); ... 위와같이 Page와 Slice를 반환해주는 리포지토리를 만들고 @Test public void paging() throws E.. 더보기
DTO에 Noargs 사용하는이유와 aceessLevel Protected을 사용하는이유 @Getter @NoArgsConstructor(access= AccessLevel.PROTECTED) public class SignUpRequestDto { private String email; private String password; } 👉 이러한 DTO에서 습관적으로 @NoArgsConstructor 를 붙여서 사용하곤 했는데, 사용하는 이유를 제대로 알지 못해서 이번 기회에 제대로 알아보고자 한다. 우선, 기본 생성자가 필요한 이유를 알기 위해서는 스프링이 어떻게 Dto를 JSON으로 맵핑하는지 그 원리를 알아야 한다. 스프링은 바로 Jackson 라이브러리의 ObjectMapper를 사용하여 JSON으로 맵핑한다! ObjectMapper는 직렬화(serialize), 역직렬화(deseri.. 더보기
JSON parse error: Cannot deserialize value of type `enum package` 이게 무슨오류일까 ?? enum을 validationCheck해주는 어노테이션을 만들다가 오류가 발생했다. 기본적으로 json body를 받을때, json to object 과정에서 deserialize 는Jackson라이브러리에서 실행됩니다. 이 경우 enum value의 name과 완전 동일한경우, 기본 deserialize가 있기에 문제는 없으나, 변수가 조금이라도 틀리게되면 바로 이런 에러를 맞이하게됩니다. JSON parse error: Cannot deserialize value of type `enum package` 이경우 해당 에러를 맞이하지않고, 내가 지정한 setter를 사용하게끔 지원하는 @JsonCreator라는 어노테이션이 필요합니다. youth,adult,oldMan; @Jso.. 더보기
컬렉션 fetch join 및 페이징 applying in memory 오류 이번에 프로젝트를 하는 도중에 2023-08-01 23:49:22.440 WARN 2334 --- [nio-8080-exec-7] o.h.h.internal.ast.QueryTranslatorImpl : HHH000104: firstResult/maxResults specified with collection fetch; applying in memory! 2023-08-01 23:49:22.441 DEBUG 2334 --- [nio-8080-exec-7] org.hibernate.SQL 이런 경고로그가 발생했다 어 뭐지 ..? 코드를 다시보니 JPAQuery contentQuery = selectFrom(board) .leftJoin(board.comments, comment).fetchJoin() ... 더보기
JPA에서 Entity에 protected 생성자를 만드는 이유. 엔티티에서 setter를 쓰는 것보다 생성자를 통해 파라미터를 넘기는 게 좋다. Setter를 무분별하게 남용하다 보면 여기저기서 객체(엔티티)의 값을 변경할 수 있으므로 객체의 일관성을 보장할 수 없기 때문. 그리고 setter는 그 의도를 알기 힘들기에 setter 사용을 자제해야한다. 그러므로 protect 생성자를 생성해서 아무데나 생성되는 걸 막는 게 좋다. (다른 사람이 쓰지 못하도록 제약한다.) 예시) Member member = new member (); member .setname("민우"); member .setname("하진"); 그러나 JPA 표준 스펙에 디폴트 생성자가 있어야하기에 private 생성자를 사용할 수 없다. 왜냐하면 jpa가 프록시 기술을 쓰는데 거기서 프록시 기술을.. 더보기
Entity를 Dto로 쉽게 변환 및 Entity노출을 최대한 자제해라. 1. 엔티티 내부 구현을 캡슐화할 수 있다. 엔티티가 getter와 setter를 가지고 있다면 충분히 데이터 전달 역할도 할 수 있지 않을까? 여기서 엔티티란 도메인의 핵심 로직과 속성을 가지고 있고, 실제 DB의 테이블과 매칭되는 클래스이다. 그렇기 때문에 엔티티가 getter와 setter를 갖게 된다면, controller와 같은 비즈니스 로직과 크게 상관없는 곳에서 자원의 속성이 실수로라도 변경될 수 있다. 또한 엔티티를 UI계층에 노출하는 것은 테이블 설계를 화면에 공개하는 것이나 다름없기 때문에 보안상으로도 바람직하지 못한 구조가 된다. 따라서 엔티티의 내부 구현을 캡슐화하고 UI계층에 노출시키지 않아야하는 것은 충분히 데이터 전달 역할로 DTO를 사용해야 할 이유로 볼 수 있다. 2. 화면.. 더보기
qlrm 라이브러리와 mariaDB 사용시 주의사항 Subscribe.Service @Transactional(readOnly = true) public List subscribeList(long principalId, long pageUserId) { StringBuffer sb = new StringBuffer(); sb.append("select u.id userId, u.username, u.profileImageUrl, "); sb.append("if( (select true from subscribe where fromUserId = ? and toUserId = u.id), true, false) subscribeState, "); // principalDetails.user.id sb.append("if(u.id = ?, true, fals.. 더보기