[JPA] 엔티티 매핑(@Entity, @Table)
자바 ORM 표준 JPA 프로그래밍 - 기본편 - 인프런 | 강의
이 글은 인프런에서 제공하는 자바 ORM 표준 JPA 프로그래밍 - 기본편 강의를 참고했고
강의 내용을 다시 복습하면서 정리하려는 목적으로 작성합니다.
JPA
에서 가장 중요한 일은 엔티티와 테이블을 정확하게 매핑하는 것 입니다.
따라서 매핑 애노테이션은 반드시 숙지해야 합니다.
(매핑(mapping)이란 하나의 값을 다른 값으로 대응시키는 것을 말합니다.)
매핑 애노테이션은 크게 4가지로 분류할 수 있습니다.
- 객체와 테이블 매핑
@Entity
,@Table
- 필드와 컬럼 매핑
@Column
- 기본 키 매핑
@Id
- 연관관계 매핑
@ManyToOne
,@JoinColumn
연관관계 매핑은 내용이 조금 많아서 나중에 따로 정리하고
해당 포스팅에는 연관관계 매핑을 제외한 3가지에 대해서 설명하도록 하겠습니다.
@Entity
@Entity
가 붙은 클래스는 JPA가 관리합니다.
흔히 엔티티라고 부릅니다.
Team
@Entity(name = "Team") // JPA가 관리하는 객체
@Table(name = "TM") // TM 테이블에 매핑
public class Team {
@Id
private Long id;
private String name;
// 기본 생성자 필수
public Team() {
}
public Team(Long id, String name) {
this.id = id;
this.name = name;
}
}
특징
- 기본 생성자가 필수 입니다.(
public
,protected
)JPA
를 사용하는 라이브러리들이 리플렉셔, 프록시 같은 기술을 사용하려면 필요하기 때문
final
클래스,enum
,interface
,inner
클래스 사용 불가- 저장할 필드에
final
사용 불가
속성
name
JPA
에서 사용할 엔티티 이름을 지정- 기본값: 클래스 이름을 그대로 사용(E.g: User)
- 같은 클래스 이름이 없으면 가급적 기본값 사용 권장
@Table
엔티티와 매핑할 테이블을 지정합니다.
속성
- name
- 매핑할 테이블 이름
- 기본값: 엔티티 이름을 사용
- catalog
DB catalog
매핑
- schema
DB schema
매핑
- uniqueConstraints(DDL)
DDL
생성시 유니크 제약 조건 생성
데이터 베이스 스키마 자동 생성 기능
JPA
에서는 애플리케이션 로딩 시점에 DB 테이블
도 생성하는 기능도 제공합니다.
- 테이블 중심 -> 객체 중심
DB 방언
을 활용해서DB
에 맞는 적절한DDL
생성
이렇게 생성된 DDL
은 개발 장비에서만 사용하며,
운영서버에서는 사용하지 않거나, 적절히 다듬은 후에 사용하는 것을 권장 합니다.
hibernate.hbm2ddl.auto 속성
- create
- 기존 테이블 삭제 후 다시 생성
- 운영 DB 사용 X
- create-drop
create
와 같으나, 종료시점에 테이블drop
- 운영 DB 사용 X
- update
- 변경분만 반영
- 운영 DB 사용 X
- 테스트 DB 권장
- validate
- 엔티티 테이블이 정상 매핑되었는지만 확인
- 테스트 DB 권장
- none
- 사용하지 않음
- 운영 DB 권장
DDL 생성 기능
DDL
생성 기능은 DDL
을 자동 생성할 때만 사용되고 JPA
의 실행 로직에는 영향을 주지 않습니다.
유니크 제약조건 추가
@Entity
@Getter @Setter
// 유니크 제약 조건 추가
@Table(uniqueConstraints =
{@UniqueConstraint(
name = "NAME_AGE_UNIQUE",
columnNames = {"NAME", "AGE"}
)})
public class Team {
@Id
private Long id;
private String name;
private Integer age;
// 유니크 제약 조건 추가
@Column(unique = true)
private String nickName;
}
필드와 컬럼 매핑
모든 속성을 외우는 것보다는 어느정도 숙지하신 뒤 엔티티 설계시 필요할때마다 찾아보는것을 추천 드립니다.^^
@Column
컬럼 매핑 애노테이션
속성
name
- 필드와 매핑할 테이블의 컬럼 이름
insertable
,updatable
- 등록, 변경 가능 여부
- 기본값
TRUE
nullable
null
값의 허용 여부 설정flase
이면DDL
생성시not null
제약조건 추가
unique
@Table
의uniqueConstraints
와 같지만 한 컬럼에 간단히 유니크 제약조건을 걸 때 사용- 하지만, 잘 사용하지 않음
- 랜덤한 값으로 이름을 설정해서 개발자가 인식에 어려움이 있음(아래 코드 참고 [
UK_syftr7gx86fwf7ox7bgvnnta7
])
- 랜덤한 값으로 이름을 설정해서 개발자가 인식에 어려움이 있음(아래 코드 참고 [
- 하지만, 잘 사용하지 않음
Hibernate:
alter table User
add constraint UK_syftr7gx86fwf7ox7bgvnnta7 unique (name)
columnDefinition
- 데이터베이스 컬럼 정보를 직접 줄 수 있음
@Column(name = "name",
columnDefinition = "varchar(100) default 'EMPTY'")
private String username;
Hibernate:
create table User (
id bigint not null,
name varchar(100) default 'EMPTY',
primary key (id)
)
length
- 문자 길이 제약조건,
String
타입에만 사용
- 문자 길이 제약조건,
precision
,scale
BigDecimal
또는BigInteger
타입에서 사용precision
은 소수점을 포함한 전체 자릿수scale
은 소수의 자릿수double
,float
타입에는 적용되지 않음- 아주 큰 숫자나 정밀한 소수를 다룰때만 사용
@Column(precision = 20, scale = 6)
private BigDecimal age;
Hibernate:
create table User (
id bigint not null,
age decimal(20,6),
primary key (id)
)
@Temporal
날짜 타입 매핑 애노테이션
속성
value
TemporalType.DATE
DB
의date
타입과 매핑yyyy-MM-dd
- 2023-02-14
TemporalType.TIME
DB
의time
타입과 매핑hh:mm:ss
- 11:11:11
TemporalType.TIMESTAMP
DB
의timestamp
타입과 매핑yyyy-MM-dd hh:mm:ss
- 2023-02-14 11:11:11
참고LocalDate
, LocalDateTime
을 사용할 때는 @Temporal
생략 가능!
@Enumerated
enum
타입 매핑 애노테이션ORDINAL
사용을 권장하지 않습니다.
속성
value
EnumType.ORDINAL
enum
순서를 데이터베이스에 저장
EnumType.STRING
enum
이름을 데이터베이스에 저장
ORDINAL
사용을 권장하지 않는 이유는 다음과 같습니다.
만약 처음 User
엔티티 설계시 USER
, ADMIN
이라는 enum
타입으로 RoleType
을 설정했다고 합시다.
RoleType
public enum RoleType {
USER, ADMIN
}
User
@Entity
@Getter @Setter
public class User {
@Id
@GeneratedValue(strategy = GenerationType.TABLE,
generator = "USER_SEQ_GENERATOR")
private Long id;
private String name;
@Enumerated(EnumType.ORDINAL)
private RoleType roleType;
}
JpaMainV3
public class JpaMainV3 {
public static void main(String[] args) {
EntityManagerFactory emf = Persistence.createEntityManagerFactory("pureJpa");
EntityManager em = emf.createEntityManager();
EntityTransaction tx = em.getTransaction();
tx.begin();
try {
User user1 = new User();
user1.setId(1L);
user1.setName("노홍철");
user1.setRoleType(RoleType.USER);
User user2 = new User();
user2.setId(2L);
user2.setName("유재석");
user2.setRoleType(RoleType.ADMIN);
em.persist(user1);
em.persist(user2);
tx.commit(); // 커밋
} catch (Exception e) {
tx.rollback(); // 롤백
} finally {
em.close(); // DB Connection을 사용하여 작업함
}
emf.close(); // 애플리케이션이 종료되면 닫아야함
}
}
결과
ADMIN
은 1
USER
는 0
으로 저장된것을 확인 할 수 있습니다.
그런데, RoleType
에 GUEST
라는 권한이 추가되면….?
public enum RoleType {
GUEST, USER, ADMIN
}
이렇게 되면 유재석은 ADMIN
이기 때문에 2
, 노홍철은 USER
이기 때문에 1
로 변경되어야 합ㄴ디ㅏ.
그런데 이미 DB
에 저장된 회원이 500만명이라고 해봅시다…. 다 수정하기가 어렵겠죠? 😿
따라서 EnumType.STRING
을 사용하는 것을 권장 합니다.enum
이름을 데이터베이스에 저장할수 있기 때문이죠!
@Enumerated(EnumType.STRING)
private RoleType roleType;
EnumType.STRING
으로 변경한뒤 유재석과 노홍철을 저장하면 다음과 같은 결과를 얻습니다.
EnumType.STRING
을 사용하는 것을 권장 합니다.
@Lob
BLOB
, CLOB
매핑 애노테이션
(LOB
은 TEXT, 그래픽, 이미지, 비디오, 사운드 등 구조화되지 않은 대형 데이터를 저장하는 목적으로 개발됨)@Lob
에는 지정할 수 있는 속성이 없음
- 매핑하는 필드 타입이 문자이면
CLOB
매핑, 나머지는BLOB
매핑CLOB
: String, char[], java.sql.CLOB- 문자 대형 객체
- 문자 기반 데이터 보관용
BLOB
: bytep[], java.sql.BLOB- 2진 대형 객체, 이미지, 동영상, MP3 등
- 비 전통적인 데이터 보관용
@Transient
특정 필드를 컬럼에 매핑하지 않음(매핑 무시)
메모리상에서만 임시로 어떤 값을 보관하고 싶을 때 사용합니다.
(@Transient
에는 지정할 수 있는 속성이 없음!)
기본 키 매핑
기본키 매핑에 들어가기에 앞서 권장드리는 기본키(식별자) 전략을 말씀 드립니다.
기본키 제약조건은 null
이면 안되고, 유일해야하며, 불변해야 합니다.
그런데, 먼 미래까지 이 조건을 만족하는 자연키는 찾기가 어렵습니다.(특히 불변이 어려워요)😥
그래서 대리키(대체키)를 사용하는것을 권장 드립니다.😊
(대리 키(alternate key
)는 관계형 데이터베이스의 관계 모델 에서 관계의 ‘후보 키’ 중 기본 키로 선정되지 않은 키를 말한다.)
예를 들어 주민등록번호도 기본키로 적절하지 않습니다.Long
형 auto_increment
(10억 넘어도 동작 가능), UUID
같은 대체키 같은 것을 조합해서 사용하는 것을 권장합니다.
기본키를 매핑하는 방법은 2가지가 존재합니다.
직접 할당과 자동 할당 입니다.
직접 할당
@Id
만 사용
자동 할당
@GeneratedValue
사용
@GeneratedValue
기본 키 생성을 DB
에 위임 합니다.
속성
* IDENTITY
* 기본키 생성을 DB
에 위임
* 주로 MySQL
, PostgreSQL
, SQL Server
, DB2
에서 사용
* MySQL
의 AUTO_INCREMENT
* JPA
는 보통 트랜잭션 커밋 시점에 INSERT SQL
실행
* AUTO_INCREMENT
는 DB
에 INSERT SQL
을 실행한 이후에 ID
값을 알 수 있음
* IDENTITY
전략은 em.persist()
시점 즉시 INSERT SQL
실행하고 DB
에서 식별자를 조회함
* 영속성 컨텍스트에 관리되려면 PK
가 무조건 있어야 하기 때문에 commit
에 SQL
이 날아가지 않음
User
@Entity
@Getter @Setter
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
@Enumerated(EnumType.STRING)
private RoleType roleType;
}
JpaMainV3
public class JpaMainV3 {
public static void main(String[] args) {
EntityManagerFactory emf = Persistence.createEntityManagerFactory("pureJpa");
EntityManager em = emf.createEntityManager();
EntityTransaction tx = em.getTransaction();
tx.begin();
try {
User user1 = new User();
user1.setName("노홍철");
user1.setRoleType(RoleType.USER);
System.out.println("persist 전");
em.persist(user1);
System.out.println("persist 후");
tx.commit(); // 커밋
} catch (Exception e) {
tx.rollback(); // 롤백
} finally {
em.close(); // DB Connection을 사용하여 작업함
}
emf.close(); // 애플리케이션이 종료되면 닫아야함
}
}
결과
persist 전
Hibernate:
/* insert com.study.purejpa.entity.User
*/ insert
into
User
(id, name, roleType)
values
(default, ?, ?)
persist 후
정말로 commit
이전인 em.persist()
에 SQL
를 날린 것을 알수 있습니다.
따라서 em.persist()
시점에 즉시 INSERT SQL
실행하고 DB
에서 식별자(PK
)를 조회합니다.
결국에 쓰기 지연 SQL
저장소를 이용하여 SQL
을 날리는 것은 IDENTITY
전략에서는 불가능 합니다.
SEQUENCE
- 유일한 값을 순서대로 생성하는 특별한
DB
오브젝트 DB
시퀀스 오브젝트 사용- 오라클,
PostgreSQL
,H2
,DB2
에서 사용 @SequenceGenerator
필요
- 유일한 값을 순서대로 생성하는 특별한
@SequenceGenerator
@Entity
@SequenceGenerator(
name = "USER_SEQ_GENERATOR", // 시퀀스 제너레이터 이름
sequenceName = "MEMBER_SEQ", // 매핑할 DB 시퀀스 이름
initialValue = 1, // 시작 값
allocationSize = 1 // 시퀀스 한 번 호출에 증가하는 수
)
public class User {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE,
generator = "USER_SEQ_GENERATOR")
private Long id;
}
allocationSize
가 50이면 애플리케이션에서 한번에 시퀀스 값을 50개를 갖고오는 것입니다.
쉽게 이야기 해서 50을 메모리상에 띄우고 하나씩 증가시키며 사용하다가 50이 넘어가게 되면 그때 다시 DB에서 100까지 갖고오는 것 입니다.
이러한 방법을 사용하면 시퀀스가 증가할 때마다 DB와 통신하지 않기 때문에 네트워크를 이용하지 않아도 됩니다.
TABLE
- 키 생성 전용 테이블을 하나 만들어서
DB
시퀀스를 흉내내는 전략 - 모든
DB
에서 사용@TableGenerator
필요
- 성능 이슈가 있음
- 키 생성 전용 테이블을 하나 만들어서
@TableGenerator
@Entity
@TableGenerator(
name = "USER_SEQ_GENERATOR", // 식별자 생성기 이름
table = "MY_SEQUENCES", // 키생성 테이블명
pkColumnName = "USER_SEQ", // 키로 사용할 값 이름
allocationSize = 1 // 시퀀스 한 번 호출에 증가하는 수
)
public class User {
@Id
@GeneratedValue(strategy = GenerationType.TABLE,
generator = "USER_SEQ_GENERATOR")
private Long id;
}
AUTO
- 방언에 따라 자동 지정(기본값)
@Entity
@Getter @Setter
public class User {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String name;
}
'0+ 스프링 > 0 + 스프링 ORM(JPA)' 카테고리의 다른 글
[JPA] 연관관계 매핑(@ManyToOne, @OneToMany, @OneToOne, @ManyToMany) (0) | 2023.02.20 |
---|---|
[JPA] 연관관계 매핑 개념(패러다임 불일치 해결) (0) | 2023.02.18 |
[JPA] JPA 영속성 컨텍스트(+ flush, 준영속 상태) (0) | 2023.02.09 |
[JPA] JPA 구동 방식과 간단 실습(CRUD) (0) | 2023.02.09 |
[JPA] JPA 기초 (0) | 2023.02.08 |