본 글은 https://www.inflearn.com/course/ORM-JPA-Basic/dashboard 강의를 바탕으로 작성한 글입니다.
1. 요구사항에 따른 필드 구성방법
- 회원은 일반 회원과 관리자로 구분해야 한다.
- 회원 가입일과 수정일이 있어야 한다.
- 회원을 설명할 수 있는 필드가 있어야 한다. 이 필드는 길이 제한이 없다.
다음과 같은 요구사항이 있다고 했을 때 엔티티를 어떻게 구성할 수 있을까요?
@Entity
public class 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;
//Getter, Setter…
}
다음과 같이 코드를 구성할 수 있습니다.
- @Id를 통해서 id pk 를 매핑해줍니다.
- @Column(name = “name”)를 통해서 객체명은 username이지만 컬럼명은 name으로 매핑 할 수 있습니다.
- private Integer age 같은 코드를 통해서 다른 타입으로 지정을 할 수도 있습니다.
- JPA에서 객체에서 열거형 타입을 쓰고 싶을 때 입니다. java에는 ENUM 타입이라는 열거형 타입이 있습니다. 하지만 DB에는 ENUM타입이 없습니다. 하지만 @Enumerated라는 어노테이션을 사용하게 되면 열거형 타입을 사용할 수 있게 됩니다.
- 날짜 타입을 사용하여 생성일자, 수정일자를 설정하고 싶으면 @Temporal 이라는 걸 사용해주시면 됩니다. 날짜 설정 같은 경우에는 3가지 타입이 있습니다. 보시면 Date, Time, Time to Temp 이렇게 있습니다. 자바는 Date타입 안에 날짜랑 시간이 다 포함되어 있지만 데이터베이스에서는 구분을 해 놓았습니다. 날짜인 DATE와 시간인 TIME 그리고 날짜와 시간을 모두 포함한 TIMESTAMP 입니다. 여기서는 날짜와 시간을 모두 포함한 TIMESTAMP로 매핑을 해 놓았습니다.
- 데이터베이스에 varchar를 넘어서는 큰 용량의 값을 넣기 위해서 @Lob을 사용할 수 있습니다.
- @Transient를 이용하여 특정 컬럼을 DB랑 매핑하지 않을 수도 있습니다.
2. @Column 어노테이션을 이용한 컬럼 설정
@Column어노테이션을 사용했을 때 컬럼을 설정할 수 있는 속성들을 하나씩 설명드리겠습니다.
name: 필드와 매핑할 테이블의 컬럼 이름을 설정할 수 있습니다. 기본값은 객체의 필드 이름 입니다.
insertable,updatable : 등록, 변경 가능 여부입니다. 만약 컬럼을 변경을 할 때 업데이트 문이 나갈 때 이 컬럼을 반영할려고 설정을 하면 updatable=true로 설정 할 수 있습니다. 반대로 updatable=false로 설정을 하게 된다면 이 컬럼의 값은 절대 변경되지 않습니다.
nullable(DDL) : null 값의 허용 여부를 설정합니다. false로 설정하면 DDL 생성 시에 not null 제약조건이 붙게됩니다.
unique(DDL) : @Table의 uniqueConstraints와 같지만 한 컬럼에 간단히 유니크 제약조건을 걸 때 사용하게 됩니다. 하지만 @Column(unique=true) 이렇게 사용을 하게 되면 유니크 제약 조건을 만들었을 때 이름이 랜덤값으로 나오게 됩니다. 이러면 운영단계에서 exception에 유니크 제약쪽이 걸리면 이름을 보고 바로 알아야 하는데 이렇게 되어있으면 알기 힘들기 때문에 보통은@Table(uniqueConstraints{@UniqueConstraint( name = "NAME_AGE_UNIQUE", columnNames = {"NAME", "AGE"} )})이런 형식을 이용하여 사용하게 됩니다.
columnDefinition(DDL) :데이터베이스 컬럼 정보를 직접 줄 수 있습니다. ex) varchar(100) default ‘EMPTY' 기본값으로는 필드의 자바 타입과 방언 정보를 사용해서 적절한 컬럼 타입을 설정합니다.
length(DDL) : 문자 길이 제약조건, String 타입에만 사용한다. 기본값으로는 255를 사용하게 됩니다.
precision,scale(DDL) : BigDecimal 타입에서 사용합니다. (BigInteger도 사용할 수 있습니다). precision은 소수점을 포함한 전체 자릿수를, scale은 소수의 자릿수입니다. 참고로 double, float 타입에는 적용되지 않는다. 아주 큰 숫자나정 밀한 소수를 다루어야 할 때만 사용합니다. 기본값으로는 precision=19, scale=2로 설정되어있습니다.
3. 필드에 Enum타입 매핑
[ @Enumerated ]
@Enumerated은 Enum타입을 매핑할 때 사용합니다.
Enum 타입을 사용 할 때는 두가지를 선택할 수 있습니다. 바로 ORDINAL과 STRING입니다.
EnumType.ORDINAL로 설정을 하게 되면 enum 순서를 데이터베이스에 저장을 합니다.
EnumType.STRING로 설정을 하면 enum이름을 데이터베이스에 저장을 합니다.
그럼 @Enumerated를 이용하여 enum을 사용한 코드를 짜보겠습니다.
@Entity
public class Member {
@Id
private Long id;
@Column(name = "name")
private String username;
private Integer age;
@Enumerated
private RoleType roleType;
@Temporal(TemporalType.TIMESTAMP)
private Date createdDate;
@Temporal(TemporalType.TIMESTAMP)
private Date lastModifiedDate;
@Lob
private String description;
//Getter, Setter…
}
엔티티는 이전에 설정했던것과 같이 이렇게 설정해주고 메인클래스에서 엔티티에 값을 설정하겠습니다.
try{
Member member = new Member();
member.setId(1L);
member.setUsername("A")
member.setRoleType(RoleType.USER);
em.persist(member)
tx.commit();
} catch (Exception e){
tx.rollback()
}
이렇게 설정하여 실행하게 되면 @Enumerated의 defalt값은 ordinal이기 때문에 ROLETYPE이 0,1,2,3이런 형식으로 데이터베이스에 들어가게 됩니다.
이제 hibernate.hbm2ddl.auto가 create가 아니라 update로 설정을 변경하겠습니다.
그 다음에 다음과 같이 메인 코드를 구성하였습니다.
try{
Member member = new Member();
member.setId(2L);
member.setUsername("B")
member.setRoleType(RoleType.ADMIN);
em.persist(member)
tx.commit();
} catch (Exception e){
tx.rollback()
}
그리고 실행을 하면 DB에는 RoleType가 1로 저장이 되게 됩니다.
그런데 요구사항이 늘어나 GUEST라는게 추가가 되었습니다.
그리고 이제 enum에 순서를 GUEST,USER,ADMIN으로 설정을 하였습니다. 그리고 나서 다시 메인코드를 다음과 같이 구성해서 GUEST를 추가하겠습니다.
try{
Member member = new Member();
member.setId(3L);
member.setUsername("C")
member.setRoleType(RoleType.GUEST);
em.persist(member)
tx.commit();
} catch (Exception e){
tx.rollback()
}
이렇게 되면 다음과 같은 결과 값이 나오게 됩니다.
ID | USERNAME | ROLETYPE |
1 | A | 0 |
2 | B | 1 |
3 | C | 0 |
순서가 바뀌었기 때문에 GUEST인데 ROLETYPE로 데이터 베이스에 0번이 들어가게 되고 이는 기존에 ROLETYPE이 USER인 1번 사용자와 같은 ROLETYPE값을 가지게 됩니다. 이렇게 Exception이 발생하게 됩니다.
이렇게 운영타입에 ORDINAL을 사용하게 되면 다음과 같이 위험한 상황이 발생하게 됩니다. 그래서 필수로 @Enumerated(EnumType.STRING)로 지정을 해주어야만 합니다.
@Enumerated(EnumType.STRING)로 지정을 하게 되면 데이터베이스에 저장을 했을 때 다음과 같이 저장이 되게 됩니다.
ID | USERNAME | ROLETYPE |
1 | A | USER |
2 | B | ADMIN |
3 | C | GUEST |
이렇게 저장을 하게 되면 순서 별도의 타입이 추가가 되어도 순서에 의해 문제가 생길 가능성이 없어집니다.
@TEMPORAL
날짜타입을 매핑할 때 사용합니다.
자바8부터는 LocalDate, LocalDateTime을 사용할 수 있게 변경이 되었습니다. LocalDate와 LocalDateTime을 사용할 때는 생략 가능합니다.
예를 들어 다음과 같이 설정을 할수도 있습니다.
@Entity
public class Member {
@Id
private Long id;
@Column(name = "name")
private String username;
private Integer age;
@Enumerated
private RoleType roleType;
@Temporal(TemporalType.TIMESTAMP)
private Date createdDate;
@Temporal(TemporalType.TIMESTAMP)
private Date lastModifiedDate;
private LocalDate testLocalDate
private LocalDateTime testLocalDateTime
@Lob
private String description;
//Getter, Setter…
}
이렇게 사용을 하게되면 하이버네이트에서는 @Temporal어노테이션이 없어도 타입형식에 맞게 날짜타입이 생성이 됩니다.
4. 대용량 데이터 저장 컬럼 매핑
@LOB
데이터베이스 BLOB, CLOB 타입과 매핑됩니다.
• @Lob에는 지정할 수 있는 속성이 없습니다. • 매핑하는 필드 타입이 문자면 CLOB 매핑, 나머지는 BLOB 매핑합니다. • CLOB: String, char[], java.sql.CLOB
• BLOB: byte[], java.sql. BLOB