JPA
Java 진영에서 사용하는 ORM 기술의 표준 사양
영속성 컨텍스트
JPA에서는 테이블과 매핑되는 엔티티 객체 정보를 영속성 컨텍스트라는 곳에 보관해서 애플리케이션 내에서 오래 지속 되도록 함
JAP API 사용하기
JAP API를 사용하기 위한 사전준비
build.gradle 설정
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-data-jpa' // (1)
compileOnly 'org.projectlombok:lombok'
runtimeOnly 'com.h2database:h2'
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
}
JPA 설정 (application.yml)
spring:
h2:
console:
enabled: true
path: /h2
datasource:
url: jdbc:h2:mem:test
jpa:
hibernate:
ddl-auto: create # (1) 스키마 자동 생성
show-sql: true # (2) SQL 쿼리 출력
configuration 클래스 생성 (샘플코드 실행)
// (1) Spring에서 Bean 검색 대상인 Configuration 클래스로 간주
@Configuration
public class JpaBasicConfig {
private EntityManager em;
private EntityTransaction tx;
// (2) 리턴하는 객체를 Spring Bean으로 추가
@Bean
public CommandLineRunner testJpaBasicRunner(EntityManagerFactory emFactory) {
this.em = emFactory.createEntityManager();
this.tx = em.getTransaction();
return args -> {
// (3) 이 곳에 학습할 코드를 타이핑합니다.
};
}
}
영속성 컨텍스트에 엔티티 저장
import lombok.Getter;
import javax.persistence.*;
@Getter
@Setter
@NoArgsConstructor
@Entity // (1)
public class Member {
@Id // (2) JPA에서 해당 클래스를 엔티티 클래스로 인식하게 만듦
@GeneratedValue // (3) 데이터베이스 테이블에서 기본키가 되는 식별자를 자동으로 설정
private Long memberId;
private String email;
public Member(String email) {
this.email = email;
}
}
영속성 컨텍스트와 테이블에 엔티티 저장
@Configuration
public class JpaBasicConfig {
private EntityManager em;
private EntityTransaction tx;
@Bean
// EntityManagerFactory 객체를 Spring으로부터 DI 받음
public CommandLineRunner testJpaBasicRunner(EntityManagerFactory emFactory) {
// EntityManager 클래스의 객체를 얻어옴
this.em = emFactory.createEntityManager();
// Transaction 객체를 얻음
this.tx = em.getTransaction();
return args -> {
example02();
};
}
private void example02() {
// Transaction을 시작하기 위해서 tx.begin() 메서드를 먼저 호출
tx.begin();
Member member = new Member("hgd@gmail.com");
// 영속성 컨텍스트에 member 객체의 정보들을 저장
em.persist(member);
//영속성 컨텍스트에 저장되어 있는 객체를 데이터베이스의 테이블에 저장
tx.commit();
// 객체가 잘 저장되었는지 find(Member.class, 1L) 메서드로 조회
Member resultMember1 = em.find(Member.class, 1L);
System.out.println("Id: " + resultMember1.getMemberId() + ", email: " + resultMember1.getEmail());
}
}
엔티티 매핑
@Table(name = "USERS") // 테이블 이름을 설정
public class Member {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long memberId;
@Column(nullable = false, updatable = false, unique = true)
private String email;
@Column(nullable = false)
private LocalDateTime createdAt = LocalDateTime.now(); // 시간 및 날짜를 매핑하기 위한 필드
@Enumerated(EnumType.STRING) // enum 타입과 매핑할 때 사용하는 애너테이션
private OrderStatus orderStatus = OrderStatus.ORDER_REQUEST;
}
Id
- 기본키 직접 할당: 애플리케이션 코드 상에서 기본키를 직접 할당
- IDENTITY: 기본키 생성을 데이터베이스에 위임
- SEQUENCE: 데이터베이스에서 제공하는 시퀀스를 사용해서 기본키를 생성
- TABLE: 별도의 키 생성 테이블을 사용
Column
- nullable: 열에 null 값을 허용할지 여부
- updatable: 열 데이터를 수정할 수 있는지 여부
- unique: 하나의 열에 unique 유니크 제약 조건을 설정
엔티티간 연관 관계 매핑
단방향 연관 관계 (일대다)
단방향 연관 관계 (다대일)
양방향 연관 관계
다대일 연관관계 매핑 코드
public class Order {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long orderId;
@Enumerated(EnumType.STRING)
private OrderStatus orderStatus = OrderStatus.ORDER_REQUEST;
@ManyToOne
@JoinColumn(name = "MEMBER_ID")
private Member member;
public enum OrderStatus {
ORDER_REQUEST(1, "주문 요청"),
ORDER_CONFIRM(2, "주문 확정"),
@Getter
private int stepNumber;
@Getter
private String stepDescription;
OrderStatus(int stepNumber, String stepDescription) {
this.stepNumber = stepNumber;
this.stepDescription = stepDescription;
}
}
}
다대일 매핑에 일대다 매핑 추가
public class Member {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long memberId;
@Column(nullable = false, updatable = false, unique = true)
private String email;
@OneToMany(mappedBy = "member")
private List<Order> orders = new ArrayList<>();
}