@Entity
@Table(name = "user", uniqueConstraints = {@UniqueConstraint(
name = "NAME_AGE_UNIQUE",
columnNames = {"name", "age"}
)})
@Getter
@Setter
public class User {
@Id
private Long id;
@Column(name = "name", nullable = false, length = 10)
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;
}
@Enumrated
: 자바의 enum 매핑시
@Temporal
: 자바의 java.util.Date, java.util.Calendar 매핑시
@Lob
: DB의 Lob
계열의 컬럼을 매핑시
@UniqueConstraint
: 유니크 제약조건
@Transient
: 특정 필드는 매핑을 하지 않습니다.
@Access
: JPA가 엔티티에 접근하는 방식을 지정합니다.
nullable
: not null 제약조건
length
: 컬럼 길이
애플리케이션에서 직접 PK를 지목 하는 수동 생성 방법과 DB에서 생성해주는 PK 값을 사용하는 자동 생성 방법이 있습니다.
JPA는 자동 생성시 여러 전략을 사용합니다. DB 마다 PK를 생성 해주는 방법이 다르기 때문입니다.
@Id
를 이용해 직접 매핑합니다.
@Id
private Long id;
--
Member member = new Member();
member.setId(1); //직접 매핑
em.persist(member);
@GeneratedValue(strategy = GeneratedType.IDENTITY)
를 이용해 PK 생성을 DB에게 맡깁니다.
MySQL, PostgreSQL, SQL Server 에서 사용합니다.
이 전략은 DB에 INSERT를 한 후에 PK를 조회할 수 있습니다. 따라서 엔티티에 식별자 값을 할당하려면 JPA는 추가로 DB를 조회 해야만 합니다. 즉, 레코드를 INSERT한 후에 그 INSERT된 레코드의 기본 키 값을 얻기 위해 다시 데이터베이스를 조회해야 합니다.
엔티티가 영속 상태가 되려면 식별자가 반드시 필요합니다. 그러므로 이 전략은 em.persist()
호출시 DB에 SQL이 날아갑니다.
따라서 이 전략은 트랜잭션을 지원하는 쓰기 지연이 동작하지 않습니다.
@Id
@GeneratedValue(strategy = GeneratedType.IDENTITY)
private Long id;
--
Member member = new Member();
member.setName("cho");
member.setAge(10);
em.persist(member);
오라클, PostgreSQL 과 같은 DB에서만 사용 가능합니다.
@SequenceGenerator
를 이용해 시퀀스 생성기를 등록 해야합니다.
@GeneratedValue( strategy = GeneratedType.SEQUENCE
로 시퀀스 생성기를 선택합니다.
@Entity
@SequenceGenerator(
name = "USER_SEQ_GENERATOR",
sequenceName = "USER_SEQ",
initialValue = 1, allocationSize = 1
)
public class SequenceUser {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "USER_SEQ_GENERATOR")
private Long id;
}
Hibernate:
select
nextval ('SEQUENCE_USER_SEQ')
Hibernate:
insert
into
sequence_user
(age, createdDate, description, lastModifiedDate, roleType, name, id)
values
(?, ?, ?, ?, ?, ?, ?)
INDETITY는 INSERT 후 ID를 조회, SEQUENCE는 ID 조회 후 INSERT를 합니다.
데이터베이스 시퀀스를 통해 식별자를 조회하는 추가 작업 때문에 DB와 2번 통신하게 됩니다.
JPA는 시퀀스에 접근하는 횟수를 줄이기 위해 @SequenceGenerator.allocationSize
를 사용합니다.
설정한 값만큼 한번에 시퀀스를 증가 시키고 해당 값들을 메모리로 가지고 있는 방법입니다.
@SequenceGenerator(
name = "USER_SEQ_GENERATOR",
sequenceName = "SEQUENCE_USER_SEQ",
initialValue = 1, allocationSize = 1
)
키 전용 테이블을 만들어 SEQUENCE를 따라한 전략입니다.
Hibernate:
create table MY_SEQUENCES (
sequence_name varchar(255) not null,
next_val int8,
primary key (sequence_name)
)
Hibernate:
insert into MY_SEQUENCES(sequence_name, next_val) values ('USER_SEQ',0)
Hibernate:
select
tbl.next_val
from
MY_SEQUENCES tbl
where
tbl.sequence_name=? for update
of tbl
Hibernate:
update
MY_SEQUENCES
set
next_val=?
where
next_val=?
and sequence_name=?
Hibernate:
insert
into
TableUser
(age, description, roleType, username, id)
values
(?, ?, ?, ?, ?)
GenerationType.AUTO
는 DB의 종류에 따라 위 3개 전략중 하나를 택합니다.
GeneratedValue.strategy
의 기본값이 AUTO
입니다.
@Enumerated.ORDINAL
: DB에 enum에 정의된 순서가 저장됩니다. 저장되는 크기는 작지만 enum의 순서를 바꾸면 안됩니다.
@Enumerated.STRING
: DB에 문자 그대로 저장됩니다. enum의 순서와 상관 없이 저장되지만 String으로 저장 되므로 ORDINAL 보단 크기가 큽니다.
@TemporalType.DATE
@TemporalType.TIME
@TemporalType.TIMESTAMP
JPA가 엔티티 데이터에 접근하는 방식을 지정합니다.
AccessType.FIELD
: 필드에 직접 접근합니다.
AccessType.PROPERTY
: 접근자(getter/setter) 를 사용합니다.
@Entity @Getter @Setter
@Access(AccessType.FIELD)
class AccessTypeEntity {
@Id
private Long id;
private String data1;
private String data2;
}
@Entity
@NoArgsConstructor
@Access(AccessType.PROPERTY)
class AccessTypePropertyEntity {
private Long id;
private String data1;
private String data2;
public AccessTypePropertyEntity(String data1, String data2) {
this.data1 = data1;
this.data2 = data2;
}
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
@Column
public String getData1() {
return data1;
}
public void setData1(String data1) {
this.data1 = data1;
}
public String getData2() {
return data2;
}
public void setData2(String data2) {
this.data2 = data2;
}
}
세밀하게 접근 제어가 필요한 경우 사용하지만 일관되게 사용하는 것이 좋을듯 싶습니다.
코드가 너무 복잡해지기도 하고 entity에서 로직이 있다는 점이 힘들 것 같습니다.