Spring/JPA

    JPA와 컬렉션

    컬렉션 JPA는 자바에서 기본으로 제공하는 Collection, List, Set, Map 컬렉션을 지원한다. 컬렉션은 일대다나 다대다 엔티티 관계를 매핑하거나, @ElementCollection을 사용해서 값 타입을 하나 이상 보관할 때 사용된다. 래퍼 컬렉션 하이버네이트는 엔티티를 영속 상태로 만들 때 컬렉션 필드를 하이버네이트에서 준비한 컬렉션으로 감싸서 사용한다. 이는 컬렉션을 효율적으로 관리하기 위한 것으로, 원본 컬렉션을 감싸는 내장 컬렉션을 생성해서 이 내장 컬렉션을 사용하도록 참조를 변경한다. 컬렉션 래퍼 컬렉션 초기화 Collection, List PersistenceBag ArrayList Set PersistenceSet HashSet 하이버네이트의 이러한 특징 때문에 컬렉션을 사용할..

    프록시와 연관관계 관리

    프록시 지연로딩 기능을 사용하려면 실제 엔티티 객체 대신에 데이터베이스 조회를 지연시킬 수 있는 가짜 객체가 필요한데, 이 가짜 객체를 프록시라고 한다. 따라서 프록시를 사용하면 지연로딩을 통해 연관된 객체를 처음부터 데이터베이스에서 조회하는 것이 아니라, 실제 사용하는 시점에 데이터베이스에서 조회할 수 있다. em.getReference() em.find()로 엔티티를 조회하면 조회한 엔티티의 실제 사용여부와 상관없이 데이터베이스에서 조회하게 된다. 만약 엔티티를 실제 사용하는 시점까지 데이터베이스 조회를 미루고 싶은 경우 em.getReference()를 사용한다. em.getReference()를 호출하면 JPA는 실제 엔티티 객체를 생성하지 않는 대신, 데이터베이스 접근을 위임한 프록시 객체를 반..

    엔티티 매핑

    객체와 테이블 매핑 @Entity JPA를 사용해서 테이블과 매핑할 클래스는 @Entity 어노테이션을 필수로 붙여야 하며, @Entity가 붙은 클래스를 엔티티라고 한다. JPA는 엔티티 객체를 생성할 때 기본 생성자를 사용하기 때문에, 파라미터가 없는 public 또는 protected 생성자를 필수로 지정해주어야 한다. @Entity 어노테이션은 final, enum, interface, inner 클래스에는 사용할 수 없으며, 저장할 필드에도 final을 사용하면 안 된다. @Table @Table은 엔티티와 매핑할 테이블을 지정하며, 생략하면 매핑한 엔티티 이름을 테이블 이름으로 사용한다. @Table 어노테이션에 uniqueConstraint 속성을 지정하면 DDL 생성 시에 유니크 제약조건을..

    스프링에서의 영속성 관리

    트랜잭션 범위와 영속성 컨텍스트 스프링이나 J2EE 컨테이너 환경에서 JPA를 사용하면 컨테이너가 제공하는 전략을 따라야 한다. 스프링 컨테이너는 트랜잭션 범위의 영속성 컨텍스트 전략을 기본으로 사용한다. 트랜잭션을 시작할 때 영속성 컨텍스트를 생성하고 트랜잭션이 끝날 때 영속성 컨텍스트를 종료한다. 같은 트랜잭션 안에서는 항상 같은 영속성 컨텍스트에 접근한다. 스프링 트랜잭션 AOP @Transactional 어노테이션을 선언하면 호출한 메서드를 실행하기 직전에 스프링 트랜잭션 AOP가 동작한다. 스프링 트랜잭션 AOP는 대상 메서드를 호출하기 직전에 트랜잭션을 시작하고, 대상 메서드가 정상 종료되면 트랜잭션을 커밋하면서 종료된다. 이때 트랜잭션을 커밋하면 JPA는 먼저 영속성 컨텍스트를 플러시해서 변..

    영속성 컨텍스트와 연속성 관리

    엔티티 매니저 JPA가 제공하는 기능은 크게 엔티티와 테이블을 매핑하는 설계 부분과 매핑한 엔티티를 실제 사용하는 부분으로 나눌 수 있다. 엔티티 매니저는 엔티티를 저장, 수정, 삭제, 조회하는 등 엔티티와 관련된 모든 일을 처리한다. 엔티티 매니저 팩토리 엔티티 매니저 팩토리는 엔티티 매니저를 만드는 공장으로, 만드는 비용이 크기 때문에 데이터베이스 당 한 개만 만들어서 애플리케이션 전체에서 공유한다. 이후 필요할 때마다 엔티티 매니저 팩토리에서 엔티티 매니저를 생성해서 사용한다. 하이버네이트를 포함한 JPA 구현체들은 엔티티 매니저 팩토리를 생성할 때 커넥션 풀도 생성한다. 엔티티 매니저는 데이터베이스 연결이 꼭 필요한 시점(트랜잭션을 시작할 때)까지 커넥션을 얻지 않다가, 트랜잭션을 시작할 때 커넥..

    N+1 문제와 해결 방법

    즉시 로딩 회원 @Entity public class Member { @Id @GeneratedValue private Long id; @OneToMany(mappedBy = "member", fetch = FetchType.EAGER) private List orders = new ArrayList(); ... } 주문정보 @Entity @Table(name = "ORDERS") public class Order { @Id @GeneratedValue private Long id; @ManyToOne private Member member; ... } 예를 들어 회원과 주문정보가 1:N 양방향 연관관계이고, 회원이 참조하는 주문정보인 Member.orders를 즉시 로딩하는 경우 특정 회원 하나를 em..

    JPA와 ORM

    ORM 하이버네이트 ORM은 객체와 관계형 데이터베이스를 매핑하는 기술로, 객체와 테이블의 매핑관계만 설정해주면 ORM 프레임워크가 SQL을 만들어서 데이터베이스와 관련된 처리를 해준다. 매핑 방법만 알려주면 ORM 프레임워크가 객체와 테이블을 매핑해서 패러다임의 불일치 문제를 처리해주기 때문에, 개발자는 객체지향 애플리케이션 개발에 집중할 수 있다. 자바의 ORM 기술에 대한 API 표준이 JPA이고, JPA를 구현한 ORM 프레임워크가 하이버네이트이다. MyBatis MyBatis나 스프링 JdbcTemplate은 ORM이 아니라 SQL 매퍼로, SQL 매퍼는 객체와 SQL을 매핑한다. SQL과 매핑할 객체만 지정하면 반복되는 JDBC API 사용과 응답 결과를 객체로 매핑하는 일을 SQL 매퍼가 대..

    JPQL과 페치 조인

    JPQL SQL이 데이터베이스 테이블을 대상으로 하는 데이터 중심의 쿼리라면, JPQL은 엔티티 객체를 대상으로 하는 객체지향 쿼리이다. JPQL을 사용하면 JPA는 JPQL을 분석한 다음 적절한 SQL을 만들어서 데이터베이스를 조회하고, 조회한 결과로 엔티티 객체를 생성해서 반환한다. 문법은 SQL과 비슷하지만, SQL을 추상화했기 때문에 특정 데이터베이스 SQL에 의존하지 않는다. SELECT List members = em.createQuery("SELECT m FROM Member m WHERE m.username = :username", Member.class) .setParameter("username", username) .getResultList(); JPQL 키워드는 대소문자를 구분하지 ..

    QueryDSL

    QueryDSL JPQL을 편하게 작성하도록 도와주는 빌더 클래스 모음으로, 문자가 아닌 코드 기반이기 때문에 문법 오류를 컴파일 단계에서 잡아낼 수 있다. 비표준 오픈소스 프로젝트로, JPA 뿐만 아니라 JDO, 몽고DB, Java 컬렉션 등도 거의 같은 문법으로 지원한다. 특징 쿼리 DSL의 핵심은 타입 안정성으로, 도메인 타입의 프로퍼티를 반영해서 생성한 쿼리 타입(Q)을 이용해서 쿼리를 작성한다. 또한 Query 인터페이스는 공통의 상위 인터페이스를 가지므로, 기반 기술에 상관없이 쿼리 경로와 오퍼레이션 모두 일관성있게 사용할 수 있다. JPA 쿼리 의존성 추가 // Spring Data JPA implementation 'org.springframework.boot:spring-boot-star..