사용자 정의 Repository 구현Spring Data JPA가 제공하는 인터페이스를 직접 구현하면 구현해야 하는 기능이 너무 많기 때문에 Spring Data JPA 리포지토리를 사용하여 인터페이스만 정의하고 구현체는 스프링이 자동 생성하도록 한다.만약, 다양한 이유로 인터페이스의 메서드를 직접 구현하고 싶다면(JPA 직접 사용, 스프링 JDBC Template 사용, MyBatis 사용, QueryDSL 사용, DB 커넥션 직접 사용 등) 사용자 정의 인터페이스를 사용한다.사용 방법1. 사용자 정의 Interface 생성public interface MemberRepositoryCustom { // 이름은 자유 List findMemberCustom();}2. 사용자 정의 Interface의..
@EntityGraphFetch Join아래 코드의 상황은 member1이 teamA를 참조하고, member2가 teamB를 참조하고 있으며, Member와 Team의 관계는 @ManyToOne 지연로딩 관계이다. @Testpublic void findMemberLazy() { // given // member1 -> teamA // member2 -> teamB Team teamA = new Team("teamA"); Team teamB = new Team("teamB"); teamRepository.save(teamA); teamRepository.save(teamB); Member member1 = new Member("member1", 10, teamA..
JPA 페이징과 정렬Spring Data JPA는 페이징 및 정렬 기능을 지원한다.순수 JPA 페이징과 정렬아래의 조건으로 페이징과 정렬을 사용해 보겠다검색 조건 : 나이가 10살정렬 조건 : 이름으로 내림차순페이징 조건 : 첫 번째 페이지, 페이지당 보여줄 데이터는 3건public List findByPage(int age, int offset, int limit) { return em.createQuery("select m from Member m where m.age = :age order by m.username desc ") .setParameter("age", age) .setFirstResult(offset) // 어디서부터 가져올 것인지 ..
메소드 이름으로 쿼리 생성순수 JPA를 사용하여 메소드를 작성하면 아래와 같으며 이 메소드는 입력된 사용자의 이름과 나이를 기준으로 Member 엔티티를 검색한다. public List findByUsernameAndAgeGreaterThen(String username, int age) { return em.createQuery("select m from Member m where m.username = :username and m.age > :age") .setParameter("username", username) .setParameter("age", age) .getResultList();}이번에는 Spring Data JPA를 사..
순수 JPA 기반 리포지토리 만들기JPA는 Java 개발자가 관계형 데이터베이스에서 자바 객체를 저장, 수정, 삭제, 조회할 수 있게 도와주는 인터페이스이다.리포지토리는 기본적으로 CRUD(Create, Read, Update, Delete)를 수행할 수 있어야 한다. 우선 공통 인터페이스를 적용하기 전에 순수 JPA기반 리포지토리 기반으로 api를 만들어 보겠다.@Repositorypublic class MemberJpaRepository { @PersistenceContext private EntityManager em; // 저장 public Member save(Member member) { em.persist(member); return member..
OSIV와 성능 최적화OSIV(Open Session in View)는 하이버네이트 영속성 컨텍스트로 데이터베이스 커넥션 범위 유지 전략이다.spring.jpa.open-in-view: true가 기본값으로 되어 있다. 이는 기본적으로 하이버네이트는 OSIV를 ON 한다는 의미이다. 애플리케이션을 실행하면 아래와 같은 WARN 로그를 남기는데 이를 고려하지 않으면 나중에 장애로 이어질 수 있다.WARN 4381 --- [ restartedMain] JpaBaseConfiguration$JpaWebConfiguration : spring.jpa.open-in-view is enabled by default. Therefore, database queries may be performed during v..
이번에는 주문내역에서 추가로 주문한 상품 정보를 추가로 조회하는 부분에 대하여 진행한다. 앞에서는 XtoOne(OneToOne, ManyToOne) 관계만 있었지만 이번에는 컬렉션인 일대다 관계(OneToMany)를 조회하고, 최적화하는 방법에 대해 정리한다.주문 조회V1 : 엔티티를 직접 노출@GetMapping("/api/v1/orders")public List ordersV1() { List all = orderRepository.findAllByString(new OrderSearch()); for (Order order : all) { order.getMember().getName(); // 객체 LAZY 강제 초기화 order.getDelivery().ge..
주문 + 배송 정보 + 회원을 조회하는 API를 만드는 부분으로 지연 로딩 때문에 발생하는 성능 문제를 해결해가며 진행한다.참고로 이번 부분은 정말 중요하며 실무에서 JPA를 사용하려면 100% 이해해야 한다!!!간단한 주문 조회V1 : 엔티티를 직접 노출이전 글에서 작성했던 것처럼 엔티티를 직접 노출하는 것은 좋지 않다.order -> member와 order -> delivery는 지연 로딩이다. 따라서 실제 엔티티 대신에 프록시가 존재한다.jackson 라이브러리는 기본적으로 이 프록시 객체를 json으로 어떻게 생성해야 하는지 모르기 때문에 예외가 발생한다.이는 Hibernate5JakartaModule을 스프링 빈으로 등록하여 해결할 수 있다.@GetMapping("/api/v1/simple-o..
JPA 활용 1편에서는 JPA로 도메인을 설계하고, Repository와 Service에 로직을 구현했으며, Thymeleaf로 화면단을 구현했다. JPA 활용 2편에서는 이전에 작성한 로직을 기반으로 API를 개발하여 다양한 방법으로 최적화와 DTO 활용을 적용한다.API 개발 - 회원 등록 APIV1 : 엔티티를 RequestBody에 직접 매핑한다.@PostMapping("/api/v1/members")public CreateMemberResponse saveMemberV1(@RequestBody @Valid Member member) { // 이렇게 Member를 외부에 노출시키면 안되며 DTO를 파라미터로 받아야 함 Long id = memberService.join(member); ..
상속 관계 매핑: 객체의 상속과 구조와 DB의 슈퍼-서브 타입 관계를 매핑해준다.(관계형 DB는 상속 관계X, 슈퍼-서브 타입 관계라는 모델링 기법이 객체 상속과 유사)JOINED : 조인 전략SINGLE_TABLE : 단일 테이블 전략TABLE_PER_CLASS : 구현 클래스마다 테이블 전략1. 조인 전략: 가장 정규화된 모델링으로 엔티티들을 각각 테이블로 만들어서 부모의 기본 키를 기본 키 + 외래 키로 사용한다.(슈퍼 타입에 조인)슈퍼 타입 엔티티(부모 엔티티)에 @Inheritance(strategy = InheritanceType.JOINED)를 추가한다. 다만, 객체는 타입이 있는데 테이블은 타입에 개념이 없다. 따라서 슈퍼 타입의 속성으로 DTYPE을 두어서, 슈퍼 타입 테이블을 보고 어떤..