엔티티 매핑
4. 엔티티 매핑
- 엔티티와 테이블을 매핑하기 위해서 매핑 어노테이션을 사용한다.
- 객체와 테이블 매핑: @Entity, @Table
- 기본 키 매핑: @Id
- 필드와 컬럼 매핑: @Column
- 연관관계 매핑: @ManyToOne, @JoinColumn
- 어노테이션 또는 XML을 사용해서 매핑 가능하다. 책에서는 어노테이션만 설명한다.
4.1 @Entity
- 테이블과 매핑할 클래스에는 @Entity 어노테이션을 필수로 붙여야한다.
- 기본 생성자가 반드시 있어야 한다.(기본 생성자는 파라미터가 없는 public 또는 protected 생성자이다.)
- 생성자를 만들지 않으면 기본 생성자가 자동으로 생성된다.
- 생성자를 하나 이상 만들면 기본 생성자가 자동으로 생성되지 않으므로, 직접 기본 생성자를 만들어야 한다.
- final 클래스, enum, interface, inner 클래스에는 사용할 수 없다.
- 저장할 필드에 final을 사용하면 안 된다.
-
속성 기능 기본값 name 엔티티 이름을 지정한다. 다른 패키지에 이름이 같은 엔티티 클래스가 있다면 이름을 지정해서 충돌하지 않도록 해야 한다. 클래스 이름
4.2 @Table
- @Table은 엔티티와 매핑할 테이블을 지정한다. 생략하면 매핑한 엔티티 이름을 테이블 이름으로 사용한다.
-
속성 기능 기본값 name 매핑할 테이블 이름이다. 엔티티 이름을 사용한다. catalog catalog를 매핑한다. schema schema를 매핑한다. uniqueConstraints 유니크 제약조건을 만든다. indexes 인덱스를 생성한다.
4.3 다양판 매핑 사용
@Entity
@Table(name="MEMBER_TABLE")
public class Member {
@Id @GeneratedValue
@Column(name = "MEMBER_ID")
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;
}
create table MEMBER_TABLE (
MEMBER_ID bigint not null,
age integer,
createdDate timestamp,
description clob,
lastModifiedDate timestamp,
roleType varchar(255),
NAME varchar(255),
primary key (MEMBER_ID)
)
4.4 데이터베이스 스키마 자동 생성
- JPA는 클래스의 매핑정보와 데이터베이스 방언을 사용해서 데이터베이스 스키마 자동생성 기능을 지원한다.
- persistence.xml에 hibernate.hbm2ddl.auto 속성을 추가하면 애플리케이션 실행 시점에 데이터베이스 테이블을 자동으로 생성한다.
-
<property name="hibernate.hbm2ddl.auto" value="create"/>
- persistence.xml에 hibernate.show_sql 속성을 추가하면 콘솔에 실행되는 테이블 생성 DDL을 출력한다.
-
<property name="hibernate.show_sql" value="true"/>
- 자동 생성되는 DDL은 지정한 데이터베이스 방언에 따라 달라진다.
- Oracle은 VARCHAR2, MySQL은 VARCHAR
- Oracle은 NUMBER, MySQL은 INT
-
옵션 설명 create-only 테이블을 새로 생성한다. drop 테이블을 삭제한다. create 기존 테이블을 삭제하고 새로 생성한다. DROP + CREATE create-drop create 속성에 추가로 애플리케이션을 종료할 때 생성한 DDL을 제거한다. DROP + CREATE + DROP update 데이터베이스 테이블과 엔티티 매핑정보를 비교해서 변경 사항만 수정한다. validate 데이터베이스 테이블과 엔티티 매핑정보를 비교해서 차이가 있으면 경고를 남기고 애플리케이션을 실행하지 않는다. 이 설정은 DDL을 수정하지 않는다. none 자동 생성 기능을 사용하지 않으려면 hibernate.hbm2ddl.auto 속성 자체를 삭제하거나 유효하지 않은 옵션 값을 주면 된다.(none은 유효하지 않은 옵션 값이다.) - 운영 서버에서 create, create-drop, update처럼 DDL을 수정하는 옵션은 절대 사용하면 안 된다.
- 이름 매핑 전략 hibernate.ejb.naming_strategy 속성은 hibernate.implicit_naming_strategy 와 hibernate.physical_naming_strategy 로 변경되었다.
4.5 DDL 생성 기능
- @Column 어노테이션의 nullable 속성을 false로 지정하면 자동 생성되는 DDL에 not null 제약조건이 추가된다.
- @Column 어노테이션의 length 속성 값을 설정하면 자동 생성되는 DDL에 문자의 크기를 지정할 수 있다.
- @Table 어노테이션의 uniqueConstraints 속성을 지정하면 유니크 제약조건이 추가된다.
- @Column의 length, nullable 속성, @Table의 uniqueConstraints 속성 등은 DDL 자동 생성시에만 사용되고 JPA의 실행 로직에는 영향을 주지 않는다.
@Entity
@Table(name = "member", uniqueConstraints = {@UniqueConstraint(
name = "NAME_AGE_UNIQUE",
columnNames = {"name", "age"})})
public class Member {
@Id
@Column(name="id")
private String id;
@Column(name = "name", nullable = false, length = 10)
private String username;
private Integer age;
}
create table member (
id varchar(255) not null,
age integer,
name varchar(10) not null,
primary key (id)
)
add constraint NAME_AGE_UNIQUE unique (name, age)
4.6 기본 키 매핑
- JPA가 제공하는 데이터베이스 기본 키 생성 전략은 다음과 같다.
- 직접 할당: 기본 키를 애플리케이션에서 직접 할당한다.
- 자동 생성: 대리 키 사용방식
- IDENTITY: 기본 키 생성을 데이터베이스에 위임한다.
- SEQUENCE: 데이터베이스 시퀀스를 사용해서 기본 키를 할당한다.
- TABLE: 키 생성 테이블을 사용한다.
- 오라클 데이터베이스는 시퀀스를 제공하지만 MySQL은 시퀀스를 제공하지 않는다.
- MySQL은 기본 키 값을 자동으로 채워주는 AUTO_INCREMENT 기능을 제공한다.
- SEQUENCE나 IDENTITY 전략은 데이터베이스에 의존한다.
- TABLE 전략은 키 생성용 테이블을 만들어 시퀀스처럼 사용하는 방식으로, 테이블을 이용하므로 모든 데이터베이스에서 사용가능하다.
- hibernate.id.new_generator_mappings 속성은 하이버네이트5 부터 true가 디폴트로 설정된다. https://docs.jboss.org/hibernate/orm/5.5/userguide/html_single/Hibernate_User_Guide.html#identifiers-generators
4.6.1 기본 키 직접 할당 전략
- @Id로 매핑하면 된다.
-
@Id private String id;
- @Id 적용 가능 자바 타입은 다음과 같다.
- 자바 기본형
- 자바 래퍼형
- String
- java.util.Date
- java.sql.Date
- java.math.BigDecimal
- java.math.BigInteger
- 기본 키 직접 할당 전략은 em.persist()로 엔티티를 저장하기 전에 애플리케이션에서 기본 키를 직접 할당하는 방법이다.
-
Member member = new Member(); member.setId(1L); em.persist(member);
4.6.2 IDENTITY 전략
- 기본 키 생성을 데이터베이스에 위임하는 전략이다.
- 주로 MySQL, PostgreSQL, SQL Server, DB2에서 사용한다. 예를 들어 MySQL의 AUTO_INCREMENT 기능은 데이터베이스가 기본 키를 자동으로 생성해준다.
-
@Entity public class Member { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; }
- 엔티티가 영속 상태가 되려면 식별자가 반드시 필요한데, IDENTITY 전략은 엔티티를 데이터베이스에 저장해야 식별자를 얻을 수 있다. 따라서 em.persist()를 호출하는 즉시 INSERT SQL이 데이터베이스에 전달된다.
- IDENTITY 전략은 트랜잭션을 지원하는 쓰기 지연이 동작하지 않는다.
4.6.3 SEQUENCE 전략
- 데이터베이스 시퀀스는 유일한 값을 순서대로 생성하는 데이터베이스 오브젝트이다.
- SEQUENCE 전략은 시퀀스를 사용해서 기본 키를 생성한다.
- SEQUENCE 전략은 em.persist()를 호출할 때 먼저 데이터베이스 시퀀스를 사용해서 식별자를 조회한다. 그리고 식별자를 엔티티에 할당한 후에 영속성 컨텍스트에 저장한다.
-
속성 기능 기본값 name 식별자 생성기 이름 필수 sequenceName 데이터베이스에 등록되어 있는 시퀀스 이름 hibernate_sequence initialValue 시퀀스 DDL을 생성할 때 처음 시작하는 수 1 allocationSize 시퀀스 한 번 호출에 증가하는 수 50 catalog 데이터베이스 catalog 이름 schema 데이터 베이스 schema 이름
create sequence member_sequence start with 1 increment by 1;
@Entity
@SequenceGenerator(
name = "member_sequence_generator",
sequenceName = "member_sequence", // 데이터베이스 시퀀스 이름
initialValue = 1,
allocationSize = 1
)
public class Member {
@Id @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "member_sequence_generator")
private Long id;
private String username;
private Integer age;
}
4.6.4 TABLE 전략
- TABLE 전략은 키 생성 전용 테이블을 하나 만들고 이름과 값으로 사용할 컬럼을 만들어 데이터베이스 시퀀스처럼 동작하는 전략이다.
- 이 전략은 테이블을 사용하므로 모든 데이터베이스에서 사용할 수 있다.
-
속성 기능 기본값 name 식별자 생성기 이름 필수 table 키생성 테이블명 hibernate_sequences catalog 데이터베이스 catalog 이름 schema 데이터베이스 schema 이름 pkColumnName 시퀀스 컬럼명 sequence_name valueColumnName 시퀀스 값 컬럼명 next_val pkColumnValue 키로 사용할 값 이름 엔티티 이름 initialValue 초기 값 0 allocationSize 시퀀스 한 번 호출에 증가하는 수 50 uniqueConstraints 유니크 제약조건을 지정할 수 있다. indexes 인덱스를 생성한다.
create table my_sequences (
sequence_name varchar(255) not null,
next_val bigint,
primary key (sequence_name)
)
@Entity
@TableGenerator(
name = "member_sequence_generator",
table = "my_sequences", // 데이터베이스 테이블
pkColumnValue = "member_sequence",
allocationSize = 1
)
public class Member {
@Id @GeneratedValue(strategy = GenerationType.TABLE, generator = "member_sequence_generator")
private Long id;
private String username;
private Integer age;
}
4.6.5 AUTO 전략
- AUTO 전략은 선택한 데이터베이스 방언에 따라 IDENTITY, SEQUENCE, TABLE 전략 중 하나를 자동으로 선택한다.
- AUTO 전략의 장점은 데이터베이스를 변경해도 코드를 수정할 필요가 없다.
- SEQUENCE 나 TABLE 전략이 선택되면 시퀀스나 키 생성용 테이블을 미리 만들어 두어야 한다. 스키마 자동 생성 기능 사용시 자동으로 만들어준다.
@Entity
public class Member {
@Id @GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String username;
private Integer age;
}
@Entity
public class Member {
@Id @GeneratedValue
private Long id;
private String username;
private Integer age;
}
4.6.6 기본 키 매핑 정리
- 영속성 컨텍스트는 엔티티를 식별자 값으로 구분한다.
- 엔티티를 영속 상태로 만들려면 식별자 값이 반드시 있어야 한다.
- 직접 할당: em.persist()를 호출하기 전에 애플리케이션에서 직접 식별자 값을 할당해야 한다.
- SEQUENCE: 데이터베이스 시퀀스에서 식별자 값을 획득한 후 영속성 컨텍스트에 저장한다.
- TABLE: 데이터베이스 시퀀스 생성용 테이블에서 식별자 값을 획득한 후 영속성 컨텍스트에 저장한다.
- IDENTITY: 데이터베이스에 엔티티를 저장해서 식별자 값을 획득한 후 영속성 컨텍스트에 저장한다.
- 테이블에 데이터를 저장해야 식별자 값을 획득할 수 있으므로, em.persist() 호출 시점에 INSERT SQL이 데이터베이스에 전달되므로 쓰기 지연 기능이 불가능하다.
4.7 필드와 컬럼 매핑: 레퍼런스
4.7.1 @Column
- 객체 필드를 테이블 컬럼에 매핑한다.
- 자바 기본 타입에는 @Column을 사용하면 nullable = false로 지정하는 것이 안전하다.
- 기본 타입에는 null 값을 입력할 수 없다.
- DDL 자동 생성시 기본 타입에 not null 제약조건을 자동으로 추가한다.
- 기본 타입에 @Column 사용시 nullable 기본 값이 true로 되어있어 not null 제약조건이 설정되지 않는다.
- 객체 타입에는 null 값을 입력할 수 있다.
- 기본 타입에는 null 값을 입력할 수 없다.
-
속성 기능 기본값 name 필드와 매핑할 테이블의 컬럼 이름 객체의 필드 이름 insertable 엔티티 저장 시 이 필드도 같이 저장한다. false 옵션은 읽기 전용일 때 사용한다. true updatable 엔티티 수정 시 이 필드도 같이 수정한다. false 옵션은 읽기 전용일 때 사용한다. true table 하나의 엔티티를 두 개 이상의 테이블에 매핑할 때 사용한다. 지정한 필드를 다른 테이블에 매핑할 수 있다. 현재 클래스가 매핑된 테이블 nullable(DDL) null 값의 허용 여부를 설정한다. true unique(DDL) @Table의 uniqueConstraints와 같지만 한 컬럼에 간단히 유니크 제약조건을 걸 때 사용한다. 만약 두 컬럼 이상을 사용해서 유니크 제약조건을 사용하려면 클래스 레벨에서 @Table.uniqueConstraints를 사용해야 한다. columnDefinition(DDL) 데이터베이스 컬럼 정보를 직접 줄 수 있다. length(DDL) 문자 길이 제약조건, String 타입에만 사용한다. 255 precision(DDL) BigDecimal, BigInteger 타입에서 사용한다. 소수점을 포함한 전체 자릿수를 나태난다. scale(DDL) BigDecimal, BigInteger 타입에서 사용한다. 소스의 자릿수를 나타낸다.
4.7.2 @Enumerated
- enum 타입을 매핑할 때 사용한다.
- EnumType.ORDINAL
- enum 순서를 데이터베이스에 저장
- EnumType.STRING
- enum 이름을 데이터베이스에 저장
- EnumType.STRING 타입을 무조건 쓰도록 권장한다. ORDINAL은 enum 순서가 바뀌면 문제가 발생하기 때문이다.
@Enumerated(EnumType.ORDINAL)
private RoleType roleType;
@Enumerated(EnumType.STRING)
private RoleType roleType;
4.7.3 @Temporal
- 날짜 타입(java.uitl.Date, java.util.Calendar)을 매핑할 때 사용한다.
- TemporalType.DATE
- 데이터베이스의 date 타입과 매핑
- TemporalType.TIME
- 데이터베이스의 time 타입과 매핑
- TemporalType.TIMESTAMP
- 데이터베이스의 timestamp 타입과 매핑
@Temporal(TemporalType.DATE)
private Date date;
@Temporal(TemporalType.TIME)
private Date time;
@Temporal(TemporalType.TIMESTAMP)
private Date timestamp;
private LocalDate localDate;
private LocalDateTime localDateTime;
create table Member (
id bigint not null,
age integer,
date date,
localDate date,
localDateTime timestamp,
time time,
timestamp timestamp,
username varchar(255),
primary key (id)
)
4.7.4 @Lob
- 데이터베이스 BLOB, CLOB 타입과 매핑한다.
- 매핑하는 필드 타입이 문자면 CLOB으로 매핑하고 나머지는 BLOB으로 매핑한다.
- CLOB: String, char[], java.sql.CLOB
- BLOB: byte[], java.sql.BLOB
4.7.5 @Transient
- 이 필드는 매핑하지 않는다.
- 객체에 임시로 어떤 값을 보관하고 싶을 때 사용한다.
@Transient
private int temp;
4.7.6 @Access
- JPA가 엔티티 데이터에 접근하는 방식을 지정한다.
- 필드 접근
- AccessType.FIELD
- 필드에 직접 접근한다. private이어도 접근할 수 있다.
- 프로퍼티 접근
- AccessType.PROPERTY
- 접근자 Getter를 사용한다.
- @Access를 설정하지 않으면 @Id의 위치를 기준으로 접근방식이 설정된다.
@Entity
//@Access(AccessType.FIELD)
public class Member {
@Id @GeneratedValue
private Long id;
}
@Entity
//@Access(AccessType.PROPERTY)
public class Member {
private Long id;
private String username;
private Integer age;
@Id @GeneratedValue
public Long getId() {
return id;
}
}
Leave a comment