JPA part 4
Entity Mapping
엔티티 정보와 이에 대응되는 클래스를 매핑 annotation을 이용해서 매핑 시켜줘야한다.
@Entity
JPA에서 사용하는 테이블과 매핑되는 클래스를 맵핑한다.
@Entity
public class Member{
public Member(){
...
}
}
- 기본 생성자는 필수. (파라미터가 없는 public 또는 protected 생성자)
- final 클래스, enum, interface, inner 클래스에는 사용할 수 없다.
- 저장할 필드에 final을 사용하면 안된다.
왜 기본생성자가 필수 일까?
JPA에서는 Reflection API를 통해 빈 객체를 생성하게 되는데, 이때 인자 정보에 대해서는 접근할 수 없다. 따라서 인자를 필요로 하지 않은 기본 생성자가 필요한 것이다.
//Music Class
public class Music {
private String singer;
private String title;
public Music(String singer, String title) {
this.singer = singer;
this.title = title;
}
public Music(){
}
public String getTitle() {
return title;
}
public String getSinger() {
return singer;
}
}
public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
Object music = new Music("IU", "YOU AND ME");
Class clazz = Music.class;
Method getTitle = clazz.getMethod("getTitle");
String title = (String)getTitle.invoke(music,null);
System.out.println(title);
}
@Table
엔티티와 매핑되는 테이블을 지정한다.
@Entity
@Table(name="MEMBER")
public class Member{
public Member(){
...
}
}
DB Schema Auto Creation
JPA에서는 데이터베이스의 스키마를 자동 생성할 수 있는 기능을 제공한다. 클래스에 설정된 annotation 매핑 정보를 토대로, Table, Field 값을 분석해서 이에 대한 ddl을 자동 생성해서 DB에 적용한다.
persistence.xml
persistence.xml 에 아래의 2가지 설정을 추가한다.
<!-->DDL을 자동 생산<!-->
<property name="hibernate.hbm2ddl.auto" value="create" />
<!-->콘솔에 생선된 SQL문 출력<!-->
<property name="hibernate.show_sql" value="true" />
위와 같이 설정되며 Table 정보를 참고해서 DDL문을 자동으로 생성해주게 된다. 하지만, 운용환경에서 사용할 만큼 완벽한 DDL은 아니어서 개발환경에서만 사용하거나 참고용으로만 활용한다.
hibernate.hbm2ddl.auto 관련 설정 정보
option | description |
---|---|
create | 기존 테이블을 삭제하고 새로 생성, DROP+CREATE |
create-drop | 애플리케이션 종료시 생성한 DDL 제거,DROP + CREATE + DROP |
update | 데이터베이스의 테이블과 엔티티 매핑벙보를 비교해서 변경사항만 적용 |
validate | update와 유사하게 작동하지만, 차이점에 대해 경고만 하고 실제 적용 안함 |
create,create-drop,update 같이 schema를 변경시키는 sql문들은 개발환경에서만 사용될 수 있도록 한다.
이름 매핑 전략
<property name="hibernate.ejb.naming_strategy" value="org.hibernate.cfg.ImprovedNamingStrategy" />
위와 같이 설정하게 되면 DB에서 SQL문 생성시 컬럼명을 자동적으로 언더바 형식으로 생성한다
보통, JAVA에서는 CamelCase를 활용하고, DB에서는 언더바 방식을 활용한다.
DDL 생성 기능
@Entity
@Table(name="MEMBER")
public class Member {
@Id
@Column(name = "ID")
private String id;
@Column(name = "NAME", nullable = false, length = 10) //추가
private String username;
...
}
위의 매핑정보를 토대로 아래의 sql문이 생성된다.
create table MEMBER (
ID VARCHAR() PRIMARY KEY,
NAME varchar(10) not null
)
column annotation을 이용해서 제약조건을 추가할 수 있다.
nullable=false를 설정하면 컬럼에 not null 속성 추가
length를 이용해서 최대 길이 지정
다음과 같이 유니크 제약조건을 만들어주는 것도 가능하다.
@Table(name="MEMBER", uniqueConstraints = {@UniqueConstraint( //추가 //**
name = "NAME_AGE_UNIQUE",
columnNames = {"NAME", "AGE"} )})
위와 같이 설정하면 아래의 sql문이 생성되고
ALTER TABLE MEMBER
ADD CONSTRAINTS NAME_AGE_UNIQUE UNIQUE(NAME,AGE);
와 같이 유니크 제약조건이 추가된다. 제약조건은 테이블 단위로 적용되므로 @Table annotation에 포함되게 된다.
Primary Key Mapping
기본키에 대해서 사용자가 직접 기본키값을 입력하는 방식과, DB에서 자동으로 생성한 값을 부여하는 방식이 존재한다.
직접 할당 방식
아래와 같이, @Id annotation만 추가하면 이를 기본키로 매핑하게 된다.
@Id
@Column(name = "ID")
private String id;
기본키를 적용할 수 있는 자료형 종류
- 자바 기본형
- 자바 래퍼형
- String
- java.util.Date
- java.sql.Date
- java.math.BigDecimal
- java.math.BigInteger
반드시, DB에 저장되기 전에 식별자(기본키)에 값을 할당해야된다. 그렇지 않으면 에러가 발생하게 된다.
자동 할당 방식
DB마다 자동 할당을 지원하는 방식이 다르므로 사용하는 DB에 맞게 방식 설정을 달리 해야한다.
우선 자동으로 기본키 값을 할당하는 방식을 사용하려면 아래의 property를 추가해줘야한다.
persistece.xml
<property name="hibernate.id.new_generator_mappings" value="true" />
추가로, 자동 할당 방식을 사용하기 위해서는 @GeneratedValue annotation을 추가하고, Generation Type를 각각의 방식에 맞게 설정해줘한다.
@GeneratedValue(strategy=GenerationType.IDENTITY)
IDENTITY 방식
IDENTITY 방식은 기본 키 생산을 DB에 위임하는 방식으로, MySQL,PostgreSQL, SQL Server, DB2에서 사용되는 방식이다.
MySQL의 auto_increment을 사용하는도 이를 활용하는 방식 중에 하나이다.
IDENTITY 방식의 자동할당을 활용하려면 아래와 같이 설정한다.
@Entity
public class Board {
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
private int id;
}
Board board=new Board();
em.persist(board);
System.out.println("board.id=" + board.getId());
기본키를 DB에서 자동으로 할당 해주기 때문에, JPA는 ID값을 알아내기 위해 DB에 쿼리를 추가로 날리게 된다. 또한, em.persist() 호출 과정에서 식별자값이 할당되지 않은 상태이므로 이 INSERT 쿼리는 영속성 컨텍스트에 저장되기 전에 바로 DB에 쿼리가 날려진다. –> 지연 쓰기가 적용되지 않는다.
SEQUENCE 방식
유일한 값을 순서대로 생성하는 DB 오브젝트를 이용하는 방식으로 Oracle ,PostgreSQL, SQL Server, DB2, H2 에서 사용할 수 있다.
아래와 같은 SEQUENCE가 있다고 하자
CREATE SEQUENCE BOARD_SEQ START WITH 1 INCREMENT BY 1;
SEQUENCE 매핑은 아래와 같이 수행한다.
@Entity
@SequenceGenerator(
//SequenceGenerator의 이름 설정
name = "BOARD_SEQ_GENERATOR",
//DB Sequence의 이름
sequenceName = "BOARD_SEQ",
//초기값
initialValue = 1,
//증가값
allocationSize = 1)
public class Board {
@Id
//SEQUENCE 방식으로 설정하고, 위에서 생성한 generator 값을 부여
@GeneraedValue(strategy = GenerationType.SEQUENCE,
generator = "BOARD_SEQ_GENERATOR")
private Long id;
}
DB 시퀀스를 이용해서 기본키 값을 받아와서 entity 식별자에 값을 할당하고, 이를 영속성 컨텍스트에 저장하게 된다.
IDENTITY 방식과는 달리, INSERT를 바로 실행하는 것이 아니라, 기본키만 먼저 조회 한다음, 이를 엔티티에 부여해서 영속성 컨텍스트에 저장하게 된다. 이후에 플러시 될때 DB에 적용된다. –> 지연 쓰기 동작
TABLE 방식
키 전용 테이블을 만들어, 데이터 베이스 시퀀스 인것 처럼 작동하게 하는 것이다. 일반 테이블을 만들어서 사용하는 것이므로 모든 DB에서 활용가능하다.
키 자동 생성용 테이블
create table MY_SEQUENCES (
-- sequence의 이름을 저장하는 필드
sequence_name varchar(255) not null,
-- 해당 sequence의 다음 값을 저장하는 필드
next_val bigint,
primary key (sequence_name)
)
매핑 수행
@Entity
@TableGenerator(
name = "BOARD_SEQ_GENERATOR",
table = "MY_SEQUENCES",
pkColumnValue = "BOARD_SEQ", allocationSize = 1)
public class Board {
@Id
@GeneratedValue(strategy = GenerationType.TABLE,
generator = "BOARD_SEQ_GENERATOR")
private Long id;
}
다음과 같이 설정하게 되면
MY_SEQUENCES 테이블에 아래와 같이 값이 생성되게 된다.
sequence_name | next_val |
---|---|
BOARD_SEQ | 1 |
여러개의 sequence를 관리하는 테이블 처럼 작동하게 된다.
SEQUENCE와 비슷하게, 영속성 컨텍스트에 저장하기 전에 MY_SEQUENCES 테이블로의 SELECT, UPDATE 문을 수행하기 위해 DB로의 접근이 필요하다
AUTO 방식
AUTO는 DB에 맞춰서 자동으로 방식을 선택해서 적용하는 것이다.
Oracle –> SEQUENCE MySQL –> IDENTITY
@Entity
public class Board {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
}
DDL 자동 생성을 활용하면 DB가 알아서 Sequence나 키 자동생성용 테이블을 만들게 된다.
Field Column Mapping
@Column
컬럼과 관련된 설정들을 할 수 있다.
Options | Description |
---|---|
name | 컬럼의 이름을 설정 |
nullable | null 허용가능 여부 설정 |
unique | 컬럼에 유일값 제약조건 적용 여부 |
length | 길이 제한 |
요구조건에 맞춰서 옵션을 설정해주면된다.
Example
not null 이면서 unique 한 컬럼 설정
@Column(nullable=false, unique=true)
private String username;
위의 매핑 정보가 아래의 SQL문을 생성하게 된다.
USERNAME VARCHAR() NOT NULL, UNIQUE
@Enumerated
java의 enum 타입을 맵핑할 수 있다.
EnumType 값 종류
- EnumType.ORDINAL을 이용하게 되면 enum 순서대로 값을 저장
- ADMIN은 1, USER는 2
- enum의 순서를 바꾸게 되면 기존의 DB에 저장되어 있는 정보에 의미가 바뀌게 된다.
- EnumType.String 이용하게 되면 enum 이름을 저장
- ADMIN은 “ADMIN”, USER는 “USER”
- DB에 저장되는 값의 크기가 ORDINAL 방식보다는 크다.
목적에 맞춰서 사용하면된다. 일반적으로 EnumType.String의 사용을 권한다.
Example
enum RoleType{
ADMIN,USER
}
@Enumerated(EnumType.String)
private RoleType roleType;
@Temporal
날짜 타입을 매핑할 때 사용한다.
Options | Description |
---|---|
TemporalType.DATE | 날짜 타입, DB의 Date 타입과 매핑 |
TemporalType.TIME | 시간 타입, DB의 Time 타입과 매핑 |
TemporalType.TIMESTAMP | 날짜_시간 타입, DB의 TIMESTAMP 타입과 매핑 |
Example
@Temporal(TemporalType.DATE)
private Date date;
@Temporal(TemporalType.TIME)
private Date time;
@Temporal(TemporalType.TIMESTAMP)
private Date timestamp;
위와 같은 매핑 정보는 아래의 SQL로 변환된다.
date DATE,
time TIME,
timestamp TIMESTAMP
@Lob
DB의 BLOB, CLOB와 매핑된다.
해당 annotation은 따로 설정할 수 있는 옵션은 없는데, java의 자료형을 보고 CLOB 또는 BLOB으로 할당한다.
String,char[] 와 같은 타입은 CLOB으로
byte[] 형태는 BLOB으로 변환한다.
Example
@Lob
private String lobString
@Lob
private byte[] lobByte;
이를 sql로 표현하게 되면 아래와 같다.
lobString CLOB,
lobByte BLOB
@Transient
DB에 저장하지 않는 컬럼을 지정하기 위한 필드, 해당 annotation 설정 시 해당 변수는 컬럼으로 생성되지 않는다.
References
book: 자바 ORM 표준 JPA 프로그래밍 -김영한 저
댓글남기기