@Entity
- JPA를 사용해서 테이블과 매핑할 클래스는 @Entity 어노테이션을 필수로 붙여야하며
@Entity가 붙은 클래스는 JPA가 관리하며, 엔티티라고 부름 - @Entity 속성
1) name : JPA에서 사용할 엔티티 이름을 지정하며, 기본값은 클래스 이름을 그대로 사용하므로 충돌되지 않도록 주의 - @Entity 적용 시 주의 사항
1) 기본 생성자는 필수이며 하나도 없다면 자동으로 생성 (파라미터가 없는 public 또는 protected 생성자)
2) final, enum, interface, inner 클래스에는 사용 불가
3) 저장할 필드에 final을 사용하면 안 됨
public Member() {} // 자동으로 생성된 기본 생성자
// ================= //
public Member() {} // 직접 만든 기본 생성자
// 임의의 생성자를 만들면 기본 생성자는 자동으로 만들어지지 않으므로 위처럼 직접 만들어야 함
public Member(String name) {
this.name = name;
}
@Table
- 엔티티와 매핑할 테이블을 지정하며, 생략 시 매핑한 엔티티 이름을 테이블 이름으로 사용
- @Table 속성
1) name : 매핑할 테이블 이름을 지정하며, 기본값은 엔티티 이름을 사용
2) catalog : catalog 기능이 있는 데이터베이스에서 catalog를 매핑
3) schema : schema 기능이 있는 데이터베이스에서 schema를 매핑
4) uniqueConstraints (DDL) : DDL 생성 시 유니크 제약 조건을 만들며, 스키마 자동 생성 기능을 사용해 DDL을 만들 때 사용
다양한 매핑 사용
- 데이터베이스 스키마 자동 생성을 사용해서 엔티티만 만들고 테이블은 자동 생성하기 위해 엔티티를 작성
// Member.java
import javax.persistence.*;
import java.util.Date;
@Entity
@Table(name="MEMBER")
public class Member {
@Id
@Column(name = "ID")
private String id;
@Column(name = "NAME", nullable = false, length = 10)
private String username;
private Integer age;
//=== 추가 ===
/* 자바의 enum을 사용해서 회원 타입을 구분
일반 회원은 USER, 관리자는 ADMIN
enum을 사용하려면 @Enumerated 어노테이션으로 매핑 */
@Enumerated(EnumType.STRING)
private RoleType roleType; // 일반회원과 관리자로 구분
/* 자바의 날짜 타입은 @Temporal을 사용해서 매핑 */
@Temporal(TemporalType.TIMESTAMP)
private Date createdDate; // 회원 가입일
@Temporal(TemporalType.TIMESTAMP)
private Date lastModifiedDate; // 회원 수정일
/* 길이 제한이 없도록 하기 위해서는 VARCHAR 대신 CLOB 타입으로 저장해야 함
그러므로 @Lob를 사용해 CLOB, BLOB 타입을 매핑할 수 있도록 함 */
@Lob
private String description; // 길이 제한이 없는 회원 설명
//Getter, Setter
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public RoleType getRoleType() {
return roleType;
}
public void setRoleType(RoleType roleType) {
this.roleType = roleType;
}
public Date getCreatedDate() {
return createdDate;
}
public void setCreatedDate(Date createdDate) {
this.createdDate = createdDate;
}
public Date getLastModifiedDate() {
return lastModifiedDate;
}
public void setLastModifiedDate(Date lastModifiedDate) {
this.lastModifiedDate = lastModifiedDate;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
}
// RoleType.java
public enum RoleType {
ADMIN, USER
}
데이터베이스 스키마 자동 생성
- JPA는 데이터베이스 스키마를 자동으로 생성하는 기능을 제공
JPA는 클래스의 매핑정보와 데이터베이스 방언을 사용해서 데이터베이스 스키마를 생성 - 스키마 자동 생성 기능을 사용하기 위해 persistence.xml에 속성 추가
// persistence.xml
// 애플리케이션 실행 시점에 데이터베이스 테이블을 자동으로 생성하도록 하는 속성
<property name="hibernate.hbm2ddl.auto" value="create" />
// 콘솔에 실행되는 테이블 생성 DDL을 출력하도록 하는 속성
<property name="hibernate.show_sql" value="true" />
- hibernate.hbm2ddl.auto 속성
1) create : 기존 테이블을 삭제하고 새로 생성 (DROP + CREATE)
2) create-drop : create에 추가호 애플리케이션을 종료할 때 생성한 DDL를 제거 (DROP + CREATE + DROP)
3) update : 데이터베이스 테이블과 엔티티 매핑 정보를 비교해서 변경 사항만 수정
4) validate : 데이터베이스 테이블과 엔티티 매핑 정보를 비교해서 차이가 있으면 경고를 남기고 애플리케이션 종료
5) none : 자동 생성 기능을 사용하지 않으려면 hibernate.hbm2ddl.auto 속성 자체를 지우거나 유효하지 않는 옵션값을 줌 - 애플리케이션 실행 시 콘솔에 DDL이 출력되면서 실제 테이블이 생성되며
자동 생성되는 DDL은 지정한 데이터베이스 방언에 따라 달라짐
- 스키마 자동 생성 기능은 애플리케이션 실행 시점에 데이터베이스 테이블이 자동으로 생성되어 개발자의 수고를 덜 수 있으나
운영 환경에서 사용할 만큼 완벽하지는 않으므로 객체와 테이블 매핑을 위해 참고하는 정도로만 사용하는 것이 좋음
DDL 생성 기능
- 스키마 자동 생성하기를 통해 만들어지는 DDL에 not null과 문자 크기 제약 조건을 추가
// Member.java
@Entity
@Table(name="MEMBER")
public class Member {
...
/* 회원 이름 필수, 10자를 초과하면 안 되는 제약조건 추가
nullable = false 속성을 통해 DDL에 not null 제약조건 추가
length 속성을 통해 DDL에 문자의 크기를 지정
*/
@Column(name = "NAME", nullable = false, length = 10)
private String username;
}
- 스키마 자동 생성하기를 통해 만들어지는 DDL에 유니크 제약 조건을 추가
// Member.java
@Entity
@Table(name="MEMBER", uniqueConstraints = {@UniqueConstraint(
name = "NAME_AGE_UNIQUE",
columnNames = {"NAME", "AGE"})})
public class Member {
기본 키 매핑
- 지금까지는 @Id 어노테이션만 사용해서 회원의 기본 키를 애플리케이션에 직접 할당했으나
기본 키를 직접 할당하는 대신 데이터베이스가 생성해주는 값을 사용하려면 어떻게 매핑해야 할까 - 기본 키 직접 할당 전략 : 기본 키를 애플리케이션에서 직접 할당 (@Id를 사용하는 이전의 방식)
/* @Id 적용 가능 자바 타입 :
자바 기본형, 자바 래퍼형, String, java.util.Date, java.sql.Date,
java.math.BigDecimal, java.math.BigInteger */
@Id
@Column(name = "id")
private String id;
// em.persist()로 엔티티를 저장하기 전에 애플리케이션에서 기본 키를 직접 할당
Board board = new Board();
board.setId("id1"); // 기본 키 직접 할당
em.persist(board);
- 기본 키 자동 생성 전략 : 대리 키 사용 방식으로 @Id에 @GeneratedValue를 추가하고 원하는 키 생성 전략을 선택
1. IDENTITY 전략 : 기본 키 생성을 데이터베이스에 위임 (데이터베이스에 의존하며 지원하는 방식에 따라 사용 가능)
주로 MySQL, PostgreSQL, SQL Server, DB2에서 사용
예) MySQL의 AUTO_INCREMENT 기능은 데이터베이스가 기본 키를 자동으로 생성해줌
// IDENTITY DDL
CREATE TABLE BOARD (
ID INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
DATA VARCHAR(255)
);
// 데이터베이스에 값을 저장할 때 ID 컬럼을 비워두면 데이터베이스가 순서대로 값을 채워줌
INSERT INTO BOARD(DATA) VALUES('A'); // ID : 1
INSERT INTO BOARD(DATA) VALUES('B'); // ID : 2
IDENTITY 전략은 위처럼 데이터베이스에 값을 저장하고 나서야 기본 키 값을 구할 수 있을 때 사용하며
이 경우 먼저 엔티티를 데이터베이스에 저장한 후 JPA는 기본 키 값을 얻어오기 위해 식별자를 조회해서 엔티티의 식별자에 할당
즉, 데이터베이스에 엔티티를 저장해서 식별자 값을 획득한 후 영속성 컨텍스트에 저장
// IDENETITY 매핑 코드
@Entity
public class Board {
@Id
// JPA는 기본 키 값을 얻어오기 위해 데이터베이스를 추가로 조회
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
...
}
// IDENTITY 사용 코드
private static void logic(EntityManager em) {
Board board = new Board();
em.persist(board);
System.out.println("board.id = " + board.getId()); // board.id = 1
}
2. SEQUENCE 전략 : 데이터베이스 시퀀스를 사용해서 기본키를 할당 (데이터베이스에 의존하며 지원하는 방식에 따라 사용 가능)
데이터베이스 시퀀스는 유일한 값을 순서대로 생성하는 특별한 데이터베이스 오브젝트
시퀀스를 지원하는 오라클, PostgreSQL, DB2, H2 데이터베이스에서 사용
// 시퀀스 DDL
CREATE TABLE BOARD (
ID BIGINT NOT NULL PRIMARY KEY,
DATA VARCHAR(255)
)
// 시퀀스 생성
CREATE SEQUENCE BOARD_SEQ START WITH 1 INCREMENT BY 1;
시퀀스를 생성한 후 생성한 데이터베이스 시퀀스를 매핑하여 사용
SEQUENCE 전략은 em.persist()를 호출할 때 먼저 데이터베이스 시퀀스를 사용해서 식별자를 조회하여 엔티티에 할당한 후에
엔티티를 영속성 컨텍스트에 저장하고 트랜잭션을 커밋해서 플러시가 일어나면 데이터베이스에 저장
// 시퀀스 매핑 코드 (사용할 데이터베이스 시퀀스 매핑)
@Entity
@SequenceGenerator( // 시퀀스 생성기 등록
name = "BOARD_SEQ_GENERATOR",
sequenceName = "BOARD_SEQ" // 매핑할 데이터베이스 시퀀스 이름. 즉, BOARD_SEQ 시퀀스와 매핑
initialValue = 1, allocationSize = 1)
public class Board { // 키 생성 전략으로 시퀀스 생성기 선택
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "BOARD_SEQ_GENERATOR")
private Long id; // id 식별값으로 BOARD_SEQ_GENERATOR 시퀀스 생성기가 할당
...
}
// SEQUENCE 사용 코드
private static void logic(EntityManager em) {
Board board = new Board();
em.persist(board);
System.out.println("board.id = " + board.getId()); // board.id = 1
}
@SequenceGenerator 속성
1) name : 식별자 생성기 (시퀀스 생성기) 이름으로 등록에 있어서 필수적
2) sequenceName : 데이터베이스에 등록되어 있는 시퀀스 이름 (매핑할 데이터베이스 시퀀스 이름)
3) initialValue : 시퀀스 DDL을 생성할 때 처음 시작하는 수를 지정. DDL 생성시에만 사용하며 기본값은 1
4) allocationSize : 시퀀스 한 번 호출에 증가하는 수. 성능 최적화에 사용되며 기본값은 50
5) catalog, schema : 데이터베이스 catalog, schema 이름
// 매핑할 DDL
create squence [sequenceName]
start with [initialValue] increment by [allocationSize]
3. TABLE 전략 : 키 생성 테이블을 사용 (모든 데이터베이스에서 사용 가능)
키 생성 전용 테이블을 하나 만들고 여기에 이름과 값으로 사용할 컬럼을 만들어 데이터베이스 시퀀스를 흉내내는 전략으로
테이블을 사용하므로 모든 데이터베이스에 적용할 수 있음
키 생성 용도로 사용할 테이블을 만들어서 사용
// TABLE 전략 키 생성 DDL
create table MY_SEQUENCES (
sequence_name varchar(255) not null, // 시퀀스 이름
next_val bigint, // 시퀀스 값
primary key (sequence_name)
)
// TABLE 전략 매핑 코드
@Entity
@TableGenerator( // 테이블 생성기 등록
name = "BOARD_SEQ_GENERATOR",
table = "MY_SEQUENCES" // MY_SEQUENCES 테이블을 키 생성용 테이블로 매핑
pkColumnValue = "BOARD_SEQ", allocationSize = 1)
public class Board {
@Id
@GeneratedValue(strategy = GenerationType.TABLE, generator = "BOARD_SEQ_GENERATOR")
private Long id; // id 식별자 값으로 BOARD_SEQ_GENERATOR 테이블 키 생성기가 할당
...
}
// TABLE 전략 매핑 사용 코드
private static void logic(EntityManager em) {
Board board = new Board();
em.persist(board);
System.out.println("board.id = " + board.getId()); // board.id = 1
}
@TableGenerator.pkColumnValue에서 지정한 "BOARD_SEQ"가 컬럼명으로 추가되었으며,
키 생성기를 사용할 때마다 next_val 컬럼 값이 증가하여 id 식별자 값을 할당
sequece_name | next_val |
BOARD_SEQ | 1 → 2 → ... |
@TableGenerator 속성
1) name : 식별자 생성기 (시퀀스 생성기) 이름으로 등록에 있어서 필수적
2) table : 키생성 테이블명
3) pkColumnName : 시퀀스 컬럼명으로 기본값은 sequece_name
4) valueColumnName : 시퀀스 값 컬럼명으로 기본값은 next_val
5) pkColumnValue : 키로사용할 값 이름으로 기본값은 엔티티 이름
6) initialValue : 초기 가뵤, 마지막으로 생성된 값이 기준으로 기본값은 0
7) allocationSize : 시퀀스 한 번 호출에 증가하는 수. 성능 최적화에 사용되며 기본값은 50
8) catalog, schema : 데이터베이스 catalog, schema 이름
9) uniqueConstraints (DDL) : 유니크 제약 조건을 지정
{pkColumnName} | {valueColumnName} |
{pkColumnValue} | {initialValue} |
4. AUTO 전략
@GeneratedValue.strategy의 기본값은 AUTO이며, 선택한 데이터베이스 방언에 따라 전략 중 하나를 자동으로 선택함
키 생성 전략이 아직 확정되지 않은 개발 초기 단계나 프로토타입 개발 시 편리하게 사용
AUTO를 사용할 때 SEQUENCE나 TABLE 전략이 선택되면 미리 시퀀스나 키 생성 테이블을 만들어 두어야 함
// AUTO 전략 매핑 코드
@Entity
public class Board {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
...
}
// 또는
@Id
@GeneratedValue
private Long id;
필드와 컬럼 매핑 : 레퍼런스
- JPA가 제공하는 필드와 컬럼 매핑용 어노테이션
1. @Column : 컬럼을 매핑
- name : 필드와 매핑할 테이블의 컬럼 이름으로 기본값은 객체의 필드 이름
- insertable : 엔티티 저장 시 이 필드도 함께 저장하며 기본값은 true이며 읽기 전용일 때 false로 사용
- updatable : 엔티티 수정 시 이 필드도 함께 수정하며 기본값은 true이며 읽기 전용일 때 false로 사용
- table : 하나의 엔티티를 두 개 이상의 테이블에 매핑할 때 사용
- nullable (DDL) : null 값의 허용 여부를 설정
- unique (DDL) : 한 컬럼에 간단히 유니크 제약조건을 걸 때 사용
- columnDefinition (DDL) : 데이터베이스에 컬럼 정보를 직접 줌
- length (DDL) : 문자 길이 제약 조건으로 String 타입에만 사용
- precision, scale (DDL) : BigDecimal 타입에 사용. precision은 소수점을 포함한 전체 자릿수, scale은 소수의 자릿수
@Column(nullable = false)
private string data;
// 생성된 DDL
data varchar(255) not null
@Column(unique = true)
private string username;
// 생성된 DDL
alter table Tablename
add constraint UK_Xxx unique (username)
@Column(columnDefinition = "varchar(100) default 'EMPTY'")
private string data;
// 생성된 DDL
data varchar(100) default 'EMPTY'
@Column(length = 400)
private string data;
// 생성된 DDL
data varchar(400)
@Column(percision = 10, scale = 20)
private BigDecimal cal;
// 생성된 DDL
cal numeric(10, 2) // H2, PostgreSQL
cal number(10, 2) // 오라클
cal decimal(10, 2) // MySQL
2. @Enumerated : 자바의 enum 타입을 매핑
- EnumType.ORIDINAL : enum 순서를 데이터베이스에 저장하며 기본값
- EnumType.STRING : enum 이름을 데이터베이스에 저장
// enum 클래스
enum RoleType {
ADMIN, USER
}
// enum 이름으로 매핑 (문자로 데이터베이스에 저장하므로 ADMIN은 ADMIN, USER는 USER로 저장)
@Enumerated(EnumType.STRING)
private RoleType roleType;
// enum의 사용
member.setRoleType(RoleType.ADMIN); // DB에 문자 ADMIN으로 저장됨
// enum 순서로 매핑 (순서로 데이터베이스에 저장하므로 ADMIN은 0, USER는 1으로 저장)
@Enumerated(EnumType.ORDINAL)
private RoleType roleType;
// enum의 사용
member.setRoleType(RoleType.ADMIN); // DB에 0으로 저장됨
3. @Temporal : 날짜 타입 (java.util.Date, java.util.Calendar) 을 매핑
- TemporalType.DATE : 날짜, 데이터베이스 date 타입과 매핑 (예 : 2013-10-11)
- TemporalType.TIME : 시간, 데이터베이스 time 타입과 매핑 (예 : 11:11:11)
- TemporalType.TIMESTAMP : 날짜와 시간, 데이터베이스 timestamp 타입과 매핑 (예 : 2013-10-11 11:11:11)
(H2, 오라클, PostgreSQL의 경우 timestamp, MySQL의 경우 datetime)
자바의 Date 타입에는 년월일 시분초가 있지만 데이터베이스에는 date, time, timestamp 세 가지 타입이 존재하며
@Temporal을 생략하면 자바의 Date와 가장 유사한 timestamp로 정의됨
@Temporal(TemporalType.Date)
private Date date; // 날짜
@Temporal(TemporalType.TIME)
private Date time; // 시간
@Temporal(TemporalType.TIEMSTAMP)
private Date timestamp; // 날짜와 시간
// 생성된 DDL
date date,
time time,
timestamp timestamp,
4. @Lob : BLOB, CLOB 타입을 매핑
- CLOB : String, char[], java.sql.CLOB
- BLOB : byte[], java.sql.BLOB
@Lob에는 지정할 수 있는 속성이 없어 매핑하는 필드 타입이 문자면 CLOB으로 매핑하고 나머지는 BLOB로 매핑
@Lob
private string lobString;
@Lob
private byte[] lobByte;
// 오라클
lobString clob,
lobByte blob,
// MySQL
lobString longtext,
lobByte logblob,
// PostgreSQL
lobString text,
lobByte oid,
5. @Transient : 특정 필드를 데이터베이스에 매핑하지 않으며 데이터베이스에 저장하지 않고 조회하지도 않음
@Transient
private Integer temp; // 객체에 임시로 어떤 값을 보관하고 싶을 때 사용
6. @Access : JPA가 엔티티에 접근하는 방식을 지정
- AccessType.FIELD : 필드 접근으로 필드에 직접 접근
- AccessType.PROPERTY : 프로퍼티 접근으로 접근자(Getter)를 사용
// 필드 접근
@Entity
@Access(AccessType.FIELD)
public class Member {
@Id // @Id가 필드에 있으므로 @Access(AccessType.FIELD)로 설정한 것과 같으므로 @Access 생략 가능
private String id;
private String data1;
private String data2;
...
}
// 프로퍼티 접근
@Entity
@Access(AccessType.PROPERTY)
public class Member {
private String id;
private String data1;
private String data2;
@Id // @Id가 프로퍼티에 있으므로 @Access(AccessType.PROPERTY)로 설정한 것과 같으므로 @Access 생략 가능
public String getId() {
return id;
}
@Column
public String getData1() {
return data1;
}
public String getData2() {
return data2;
}
}
@Access를 설정하지 않으면 @Id의 위치를 기준으로 접근 방식이 설정되며
필드 접근 방식과 프로퍼티 접근 방식을 함께 사용할 수도 있음
// 필드, 프로퍼티 접근 함께 사용
@Entity
public class Member {
@Id // @Id가 필드에 있으므로 기본은 필드 접근 방식을 사용
private String id;
@Transient
private String firstName;
@Transient
private String lastName;
@Access(AccessType.PROPERTY);
public String getFullName() { // getFullName()만 프로퍼티 접근 방식 사용
return firstName + lastName;
}
...
}
'Java-Spring > 자바 ORM 표준 JPA 프로그래밍' 카테고리의 다른 글
[자바 ORM 표준 JPA 프로그래밍] 연관관계 매핑 기초 (0) | 2022.03.29 |
---|---|
[자바 ORM 표준 JPA 프로그래밍] 엔티티 매핑 - 실전 예제 (0) | 2022.03.26 |
[자바 ORM 표준 JPA 프로그래밍] 영속성 관리 (0) | 2022.03.20 |
[자바 ORM 표준 JPA 프로그래밍] JPA 시작 (0) | 2022.03.15 |
[자바 ORM 표준 JPA 프로그래밍] JPA 소개 (0) | 2022.03.13 |