영속 컨텍스트는 세션(JPA 의 EntityManager) 단위로 생긴다. 즉, 세션 생성 시점에 영속 컨텍스트가 생성되고 세션 종료 시점에 컨텍스트가 사라진다. 응용 프로그램은 영속 컨텍스트에 직접 접근할 수 없다. 대신 EntityManager를 통해서 영속 ㅋ턴텍스트와 관련된 작업을 수행한다. [참고 - 최범균 님의 JPA]
스프링 부트 에서는 persistence.xml 같은 설정이 필요 없다. 단지, JPA 를 위한 프로퍼티 설정만 추가하면 된다.
spring.jpa.생략
EntityManager 를 사용하기 위해서는
@PersistenceContext
private EntityManager em;
만 정의해주고 사용하면 된다. 예를 들어서,
entityManager.find(User.class, email);
TypedQuery<User> query =
entityManager.createQuery(
"select u from User u order by u.name",
User.class);
List<User> result = query.getResultList();
entityManager 주요 메서드
- find
- merge
- remove
- 등등...
@Transactional 을 선언하면 된다. 단, 스프링은 Unchecked Exception을 rollback 대상으로 지정한다. RuntimeException을 상속한 예외를 Unchecked Exception이라 한다. Checked Exception 발생 시 롤백을 원한다면 다음과 같이 rollbackOn 옵션에 Exception.class 추가하면 된다.
JPA 는 SQL 과 유사한 JPQL 을 제공한다.
TypedQuery<User> query =
entityManager.createQuery(
"select u from User u order by u.name",
User.class);
@Table 어노테이션을 사용하면 테이블 이름으로 직접 지정할 수 있다.
@Id Entity 의 가장 큰 특징은 식별자를 가진다. @ID 어노테이션을 사용한다. @Id 어노테이션을 적용한 필드 값은 EntityManager#find() 메서드에서 엔티티 객체를 찾을 때 식별자로 사용한다.
@Basic 생략 가능, int, long, string 과 같은 기본 타입일 때 매핑
@Temporal 날짜와 시간, DATE, TIME, TIMESTAMP 등 하이버네이트 5.2 이상부터는 LocalhostDateTime 지원
@Enumerated 열거 타입에 대한 매핑
@Column 필드 이름과 테이블의 컬럼 이름이 다를 경우
매핑을 필드가 아니라, getter 에 주고 싶다면? @Column 어노테이션을 필드가 아니라, getter, setter 에 붙여주면 된다. 프로퍼티 접근 타입을 사용하면 객체 지향 관점에서 공개 get/set 메서드는 캡슐화를 약화 시키는 원인이 된다. 물론, get/set 메서드를 protected 나 private 로 범위를 제한할 수 있지만, 프로퍼티 접근 타입보다 필드 접근 타입을 선호한다. [최범균님 JPA 참고]
읽을만한 글
@Taransient Transient 필드, 영속 대상에서 제외
- 기본 생성자를 제공해야 한다.
- 기본 생성자의 접근 범위는 public 또는 protected 이어야 한다. private 일 경우 JPA 의 특징이 올바로 동작하지 않는다.
public <T> T find(Class<T> entityClass, Object primaryKey);
find 와 유사하지만, 데이터가 존재하지 않는 경우 EntityNotFoundExceptino 을 발생시킴
새로운 Entity 를 DB 에 저장
MySQL 의 auto_increment 칼럼을 사용하기 위해서는, @GeneratedValue(st... 어노테이션을 사용하면 매핑이 된다.
@GeneratedValue(strategy = GenerationType.IDENTITY)
트랜잭션 커밋 시점에 실제 삭제를 위한 delete 쿼리를 실행한다.
따로 확인해보자.
EntityManager 를 사용하고, 각 메서드는 @Transactional 이 걸려있음. 즉, 이 의미는 이미 트랜잭션이 중이라면 참여하고 트랜잭션이 없다면 트랜잭션을 새로 시작하라는 의미
https://suhwan.dev/2019/02/24/jpa-vs-hibernate-vs-spring-data-jpa/
최범균님 책 5장,6장 다시 보자. 중요
일단, MyISAM과 InnoDB에서 인덱스 생성 관련하여 구동방식이 다름.. 주의
테스트는 InnoDB 로 셋팅해서 진행
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL5InnoDBDialect
- LAZY 지연 로딩 : Join 걸지 않고 조회
- EAGER 즉시 로딩 : Join 걸고 조회
지연로딩은 연관 객체를 실제 사용하는 시점에서 로딩
select membership0_.card_number as card_num1_0_0_, membership0_.enabled as enabled2_0_0_, membership0_.expiry_date as expiry_d3_0_0_, membership0_.user_email as user_ema4_0_0_ from membership_card membership0_ where membership0_.card_number=?
select membership0_.card_number as card_num1_0_0_, membership0_.enabled as enabled2_0_0_, membership0_.expiry_date as expiry_d3_0_0_, membership0_.user_email as user_ema4_0_0_, user1_.email as email1_1_1_, user1_.create_date as create_d2_1_1_, user1_.name as name3_1_1_ from membership_card membership0_ left outer join user user1_ on membership0_.user_email=user1_.email where membership0_.card_number=?
@OneToOne 어노테이션은 EAGER 를 기본으로 한다. 즉, Fetch 를 따로 지정하지 않으면 엔티티를 로딩할 때 @OneToOne 으로 매핑한 연관 객체도 함께 로딩한다. 연관 객체를 즉시로딩하기 위해서 외부 조인을 사용한다.
JPA 는 1:1 연관에서 물리적으로 존재하지 않는 연관을 처리하기 위해서 mappedBy 를 사용한다.
생략..
차이 나중에 확인해보자. 조금 어려운 듯
@ManyToOne 어노테이션은 FetchType.EAGER 를 기본으로 사용.
설명 생략..