FireDrago
[JPA] 엔티티 매핑 본문
JPA 에서 가장 중요한 두가지는 영속성 컨텍스트와 매핑이다.
이번시간에는 JPA에서 엔티티 객체와 데이터베이스를 어떻게 매핑 하는지 알아보자
객체와 테이블 매핑
@Entity
@Table(name = "MEMBER", uniqueConstraints = {
@UniqueConstraint(name = "UK_USERNAME", columnNames = {"USERNAME"}),
@UniqueConstraint(name = "UK_EMAIL", columnNames = {"EMAIL"})
})
public class Member {
private Long id;
// 기본 생성자는 필수
public Member(){}
}
JPA를 사용하기 위해서 클래스에 반드시 @Entity 애노테이션을 붙여줘야한다.
또한 기본생성자가 반드시 필요하다.
@Table 어노테이션은 여러가지 속성을 사용할 수 있다.
| 속성 | 기능 | 설명 |
| name | 매핑할 테이블 명 기본값은 엔티티이름 | |
| schema | 데이터베이스 스키마 매핑 | |
| uniqueConstraint | DDL 생성시 유니크 제약조건 | 예시 코드의 USERNAME, EMAIL 컬럼은 테이블에서 유일한 값이어야 한다. |
DB 스키마 자동생성
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.2" xmlns="http://xmlns.jcp.org/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_2.xsd">
<persistence-unit name="hello">
<properties>
<!-- 필수 속성 -->
<property name="jakarta.persistence.jdbc.driver" value="org.h2.Driver"/>
<property name="jakarta.persistence.jdbc.user" value="sa"/>
<property name="jakarta.persistence.jdbc.password" value=""/>
<property name="jakarta.persistence.jdbc.url" value="jdbc:h2:tcp://localhost/~/test"/>
<property name="hibernate.dialect" value="org.hibernate.dialect.H2Dialect"/>
<!-- 옵션 -->
<property name="hibernate.show_sql" value="true"/>
<property name="hibernate.format_sql" value="true"/>
<property name="hibernate.use_sql_comments" value="true"/>
<!-- 데이터베이스 스키마 자동생성 옵션 -->
<property name="hibernate.hbm2ddl.auto" value="none" />
</properties>
</persistence-unit>
</persistence>
<!-- 데이터베이스 스키마 자동생성 옵션 -->
<property name="hibernate.hbm2ddl.auto" value="none" />
JPA는 애플리케이션 실행시 엔티티의 설정대로 DDL을 생성하는 기능이 있다.
DB 스키마를 자동생성 기능은 실제 운영서버에서는 사용할 수 없다.
애플리케이션이 실행될때마다 DB 데이터가 초기화 되기 때문이다.
hibernate.hbm2ddl.auto 값을 통해 설정 할 수 있다.
| 옵션 | 설명 |
| create | 기존 테이블 삭제 후 다시 생성 (DROP + CREATE) |
| create-drop | create 와 같으나 종료시점에 테이블 DROP |
| validate | 엔티티와 테이블이 정상 매핑되었는지만 확인 (테스트서버, 운영서버) |
| update | 변경분만 반영 (테스트 서버) |
| none | 사용하지 않음 (운영서버) |
필드와 컬럼 매핑
@Entity
public class Member {
@Id @GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
@Column(name = "name")
private String username;
private Integer age;
@Enumerated(EnumType.STRING)
private RoleType roleType;
@Temporal(TemporalType.TIMESTAMP)
private Date createdDate;
@Temporal(TemporalType.TIMESTAMP)
private Date lastModifiedDate;
@Lob
private String description;
public Member() {}
}
@Column : 컬럼매핑
| 속성 | 설명 | 기본값 |
| name | 필드와 매핑할 테이블의 컬럼이름 | 객체의 필드이름 |
| insertable, updatable |
등록, 변경 가능 여부 | TRUE |
| nullable(DDL) | DDL 생성시 not null 제약조건 | |
| unique(DDL) | @Table의 uniqueConstaints 와 동일 | |
| columnDefinition (DDL) |
데이터베이스에 컬럼 정보를 직접 줄 수 있다. ex) varchar(100) default 'EMPTY' |
필드의 자바타입과 방언 정보 사용 |
| length(DDL) | String 타입에 길이 제약조건 varchar(숫자) 입력된다. | 255 |
| preicision, scale(DDL) |
BigDecimal 타입에서 사용 precision은 소수점 포함한 전체 자리수 scale은 소수의 자릿수 |
precision = 19, scale = 2 |
@Enumerated : Enum 타입에 사용
EnumType.STRING : enum 이름을 데이터베이스에 저장
LocalDate, LocalDateTime : date, timestamp 와 매핑된다. 별도의 어노테이션 없어도 됨
@Lob : BLOB, CLOB 타입과 매핑, 문자열이면 CLOB, 나머지 BLOB
@Transient : 매핑 안함
기본키 매핑
@Id @GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
PK 컬럼과 매핑할때는 @Id 어노테이션을 붙인다. JPA는 @Id 어노테이션이 붙은 필드를 바탕으로 기본키로 생성한다
엔티티 클래스에서 PK 컬럼을 지정하려면 반드시 @Id 어노테이션을 사용해야 한다
기본키 생성은 웬만하면 식별자 용 id를 생성하고, Long 타입으로 최대값 초과를 방지하자,
@GeneratedValue 어노테이션은 기본키의 자동생성전략을 설정할 수 있다.
| 속성 | 설명 |
| IDENTITY | MySql 등에서 사용된다. 특징적으로 트랜잭션 커밋 시점이 아니라 em.persist() 시점에 SQL을 실행한다. 왜냐하면 1차 캐시를 사용하기 위해서는 키값으로 pk 값이 필요한데, MySql은 Insert 쿼리가 실행되어야 pk값을 생성하기 때문이다. INSERT 실행하고 식별자를 조회하여 1차 캐시에 등록한다. |
| SEQUENCE | Oracle 에서 사용된다. 기본키 생성만을 위한 특수 데이터베이스 오브젝트를 생성한다. (시퀀스) em.persist() 시점에 시퀀스에 다음 pk 값을 조회하여 가져온다. 1차 캐시 등록하고, 트랜잭션 커밋 시점에 INSERT 쿼리를 실행한다. allocationSize 값을 50 정도로 설정하면 한번에 50개씩 pk 값을 조회하여 매 INSERT 마다 pk를 조회하지 않도록 성능최적화를 할 수 있다. |
| AUTO | 사용하는 데이터베이스에 맞게 자동으로 JPA가 pk 값 전략을 설정한다. |
@Entity
// 시퀀스 전략
@SequenceGenerator(
name = “MEMBER_SEQ_GENERATOR",
sequenceName = “MEMBER_SEQ", //매핑할 데이터베이스 시퀀스 이름
initialValue = 1, allocationSize = 1) // 1부터 1씩 증가하는 pk
public class Member {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE,
generator = "MEMBER_SEQ_GENERATOR")
private Long id;
@SequenceGenerator 속성
| 속성 | 설명 | 기본값 |
| name | 식별자 생성기 이름 | 필수 |
| sequenceName | 뎅이터베이스에 등록되어 있는 시퀀스 이름 | hibernate_sequence |
| initialValue | DDL 생성시 시퀀스 처음 시작하는 수를 지정한다. | 1 |
| allocationSize | 시퀀스 한 번 호출에 증가하는 수 (성능최적화에 사용) | 50 |
| catalo, schema | 데이터베이스 catalog, schema 이름 |
데이터 중심의 엔티티 설계의 문제
-> 객체 타입을 사용하지 못하고, 관계있는 PK 값을 사용해야함
자바 코드에서 객체를 바로 사용하지 못하고, id 값으로 다시 조회해야함
JPA는 이 문제를 해결하기 위한 연관관계 매핑 기능을 제공한다.
// 객체지향적이지만 이렇게 설계불가 -> DB 테이블이랑 안 맞기 때문
@Column(name = "MEMBER_ID")
private Member orderMember;
// 어쩔 수 없이 이렇게 설계해야 한다.
@Column(name = "MEMBER_ID")
private Long memberId;
... 실행코드 ....
Order order = em.find(id);
// 여기서 Long 타입 키 조회
Long memberId = order.getMemberId();
// 다시 DB에 조회해야 함 (객체지향적이지 못함)
Member orderMember = em.find(memberId);
'프로그래밍 > JPA' 카테고리의 다른 글
| [JPA] @PersistenceContext 사용해야 하는 이유 (0) | 2024.04.08 |
|---|---|
| [JPA] JPQL 기본문법 2 (0) | 2024.04.06 |
| [JPA] JPQL 기본문법 1 (0) | 2024.04.04 |
| [JPA] 연관관계 관리 (0) | 2024.04.01 |
| [JPA] 영속성 관리 (0) | 2024.03.26 |
