일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 |
- Redis
- 낙관적락 비관적락 차이
- 스프링
- bean
- Transaction isolation level
- 암호화
- component
- Pessimistic Lock
- JPA 비관적락
- JPA 낙관적락
- 개발
- 디자인 패턴
- 캠프
- 안드로이드
- spring
- 서버
- Optimistic Lock
- Android
- JPA
- 스프링 로그
- annotation
- spring security
- 서버개발캠프
- JPA 동시성
- spring security 인증
- JPA Lock
- flask
- Inno DB
- 스프링 log
- 스마일게이트
- Today
- Total
모르는게 많은 개발자
[JPA] 2. 엔티티 매핑 @어노테이션 정리/예제 본문
이번 글에서는 JPA 엔티티 매핑과 관련된 어노테이션 @Entity, @Table, @Id, @Column에 대해 정리한다. 연관관계 매핑 관련 어노테이션은 다음 글에 포스팅하겠다.
1. @Entity
@Entity 어노테이션은 JPA를 사용해 테이블과 매핑할 클래스에 붙여주는 어노테이션이다. 이 어노테이션을 붙임으로써 JPA가 해당 클래스를 관리하게 된다.
속성 | 기능 |
name | JPA에서 사용할 엔티티 이름 지정 name을 쓰지 않을 경우 (default) 클래스이름을 엔티티 이름으로 지정 |
아래의 코드 결과를 보면 name = "user2"로 함으로써 user2 테이블이 생성된 것을 볼 수 있다.
@Entity(name = "user2")
public class User {}
주의 사항
- 기본 생성자가 꼭 필요
- final, enum, interface, innter class에서는 사용 불가
- 필드(변수)를 final로 선언 불가
2. @Table
@Table은 엔티티와 매핑할 테이블을 지정
속성 | 기능 |
name | 매핑할 테이블 이름 생략시 엔티티 이름(@Entity(name="~") 사용 |
catalog | catalog 기능이 있는 DB에서 catalog 매핑 |
schema | schema기능이 있는 DB에서 schema 매핑 |
uniqueContraints | DDL 생성시 유니크 제약조건 생성 ※ 스키마 자동 생성 기능을 사용해 DDL을 만들 때만 사용 |
아래 코드 결과를 보면 @Table에 name을 추가하면 테이블이름이 name값으로 설정이 되고 생략시 Entity이름으로 테이블이 만들어지는 것을 확인할 수 있다.
@Entity
@Table(name = "user3")
@Getter
@Setter
public class User {
@Id
@GeneratedValue
private Long id;
private String name;
}
@Entity(name="user2")
@Table
@Getter
@Setter
public class User {
@Id
@GeneratedValue
private Long id;
private String name;
}
3. @Id
@Id는 특정 속성을 기본키로 설정하는 어노테이션이다.
@Entity
@Table(name = "User")
public class User {
@Id
private Long id;
private String name;
}
@Id 어노테이션만 적게될 경우 기본키값을 직접 부여해줘야 한다.
하지만 보통 DB를 설계할 때는 기본키는 직접 부여하지 않고 Mysql AUTO_INCREMENT처럼 자동 부여되게끔 한다.
@Entity(name="user2")
@Table
@Getter
@Setter
public class User {
@Id
@GeneratedValue
private Long id;
private String name;
}
@GeneratedValue 어노테이션을 사용하면 기본값을 DB에서 자동으로 생성하는 전략을 사용 할 수 있다.
전략에는 IDENEITY, SEQUENCE, TABLE 3가지가 있다.
속성 | 기능 |
@GeneratedValue(startegy = GenerationType.IDENTITY) | 기본 키 생성을 DB에 위임 (Mysql) |
@GeneratedValue(startegy = GenerationType.SEQUENCE) | DB시퀀스를 사용해서 기본 키 할당 (ORACLE) |
@GeneratedValue(startegy = GenerationType.TABLE) | 키 생성 테이블 사용 (모든 DB 사용 가능) |
@GeneratedValue(startegy = GenerationType.AUTO) | 선택된 DB에 따라 자동으로 전략 선택 |
위 처럼 다양한 전략이 있는 이유는 DB마다 지원하는 방식이 다르기 때문이다.
AUTO 같은 경우DB에 따라 전략을 JPA가 자동으로 선택한다. 이로 인해 DB를 변경해도 코드를 수정할 필요 없다는 장점이 있다.
4. @Column
@Column은 객체 필드를 테이블 컬럼과 매핑한다.
@Column
private String name;
속성 | 기능 |
name | 필드와 매핑할 테이블의 컬럼 이름 지정 default는 필드이름으로 대체 |
insertable | true : 엔티티 저장시 필드값 저장 false : 필드값이 저장되지 않음 |
updatable | true : 엔티티 수정시 값이 수정 false : 엔티티 수정시 값이 수정 되지 않음 |
table | 하나의 엔티티를 두 개 이상의 테이블에 매핑할 때 사용 |
nullable | null값 허용 여부 설정 false : not null 제약 조건 |
unique | 컬럼에 유니크 제약조건 부여 |
columnDefinition | 데이터베이스 컬럼 정보를 직접 부여 |
length | 문자 길이 제약조건 String 타입일 때 사용 |
precision, scale | BigDecimal 타입에서 사용 precision : 소수점을 포함한 전체 자릿수 설정 scale : 소수의 자릿수 |
insertable
//Entity
@Entity(name="user2")
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class User {
@Id
@GeneratedValue
private Long id;
@Column(insertable = false)
private String name;
private String age;
}
//Service
@Service
@RequiredArgsConstructor
public class TestService {
private final EntityManager em;
@Transactional
public void test() {
User user = User.builder()
.age("12")
.name("test")
.build();
em.persist(user);
}
}
위의 결과는 User 엔티티에 name 컬럼에 "test"를 입력해도 DB에는 값이 들어가지 않는다.
updatable
//Entity
@Entity(name="user2")
@Builder
@AllArgsConstructor
@NoArgsConstructor
@Setter
public class User {
@Id
@GeneratedValue
private Long id;
@Column(updatable = false)
private String name;
private String age;
}
//Service
@Service
@RequiredArgsConstructor
public class TestService {
private final EntityManager em;
@Transactional
public void test() {
User user = User.builder()
.age("12")
.name("test")
.build();
em.persist(user);
user.setName("change test");
}
}
위의 결과는 User 엔티티 name 컬럼에 "test"를 입력하고 "change test"로 변경해도 변경값이 적용되지 않는다.
nullable
@Entity(name="user2")
@Builder
@AllArgsConstructor
@NoArgsConstructor
@Setter
public class User {
@Id
@GeneratedValue
private Long id;
@Column(nullable = false)
private String name;
@Column(nullable = true)
private String age;
}
위의 결과는 nullable true, false에 따라 not null이 적용되는지 여부이다.
unique
@Entity(name="user2")
@Builder
@AllArgsConstructor
@NoArgsConstructor
@Setter
public class User {
@Id
@GeneratedValue
private Long id;
@Column(unique = true)
private String name;
@Column(unique = false)
private String age;
}
columnDefinition
@Entity(name="user2")
@Builder
@AllArgsConstructor
@NoArgsConstructor
@Setter
public class User {
@Id
@GeneratedValue
private Long id;
@Column(unique = true)
private String name;
@Column(columnDefinition = "VARCHAR(15) NOT NULL")
private String age;
}
length
@Entity(name="user2")
@Builder
@AllArgsConstructor
@NoArgsConstructor
@Setter
public class User {
@Id
@GeneratedValue
private Long id;
@Column(length = 11)
private String name;
@Column(columnDefinition = "VARCHAR(15) NOT NULL")
private String age;
}
5. @Access
@Access는 JPA가 엔티티 데이터에 접근하는 방식을 지정한다.
접근 방식 | 기능 |
AccessType.FILED | 필드에 직접 접근 필드 접근 권한이 private여도 접근 가능 |
AccessType.PROPERTY | getter를 통해 접근 |
@Access를 설정하지 않으면 기본키를 설정하는 @Id의 위치를 기준으로 접근 방식 설정
@Entity(name = "user2")
@Table(name = "user3")
@Getter
@Setter
public class User {
@Id
@GeneratedValue
private Long id;
}
위 코드처럼 @Id가 필드에 설정되면 @Access(AccessType.FILED)로 설정된 아래 코드랑 같다.
@Entity(name = "user2")
@Table(name = "user3")
@Getter
@Setter
@Access(AccessType.FIELD)
public class User {
@Id
@GeneratedValue
private Long id;
}
아래 처럼 getter에 @id를 설정함으로 써 프로퍼티 접근으로 할 수 있다. 아래와 같은 경우 @Id가 getter에 위치하므로 @Access를 생략할 수 있다.
@Entity(name = "user2")
@Table(name = "user3")
@Setter
@Access(AccessType.PROPERTY)
public class User {
@GeneratedValue
private Long id;
@Id
public Long getId() {
return id;
}
}
필드 접근과 프로퍼티 접근을 혼합하여 아래처럼 사용할 수 있다.
//Entity
@Entity(name = "user2")
@Table(name = "user3")
@Getter
@Setter
public class User {
@Id
@GeneratedValue
private Long id;
@Transient
private String name;
@Access(AccessType.PROPERTY)
public String getFullName() {
return name + " hello";
}
protected void setFullName(String firstName) { }
}
//Service
@Service
@RequiredArgsConstructor
public class TestService {
private final EntityManager em;
@Transactional
public void test() {
User user = new User();
user.setName("aaa");
em.persist(user);
}
}
getter에 AccessType.PROPERTY를 함으로써 getter의 fullName을 column으로 지정한다.
6. @Enumerated
@Enumerated는 자바 enum 타입을 매핑할 때 사용한다.
속성 | 기능 |
value | EnumType.ORDINAL : enum 순서를 DB에 저장 EnumType.STRING : enum이름을 DB에 저장 |
//Enum 클래스
public enum RoleType {
ADMIN, USER
}
//Entity
@Entity(name = "user2")
@Table(name = "user3")
@Getter
@Setter
public class User {
@Id
@GeneratedValue
private Long id;
private String name;
@Enumerated(value = EnumType.ORDINAL)
private RoleType ordinal;
@Enumerated(value = EnumType.STRING)
private RoleType string;
}
//Service
@Service
@RequiredArgsConstructor
public class TestService {
private final EntityManager em;
@Transactional
public void test() {
User user = new User();
user.setName("test");
user.setOrdinal(RoleType.ADMIN);
user.setString(RoleType.ADMIN);
em.persist(user);
}
}
결과를 보면 EnumType.ORDINAL은 ADMIN의 순서인 0이 저장되고, EnumType.STRING은 문자열 자체가 저장된다.
사용할 때 주의점은 ADMIN, USER 사이에 enum이 하나 추가되면 USER가 순서상 2번이 된다. 하지만 DB에서는 기존 번호 USER를 1로 저장했기 때문에 문제가 발생할 수 있으므로 되도록이면 EnumType.STRING사용을 권장한다.
7. @Temporal
@Temporal은 날짜 타입을 매핑할 때 사용한다.
속성 | 기능 |
value | TemporalType.DATE : 날짜, DB date 타입과 매핑(예 : 2020-02-12) TemporalType.TIME : 시간, DB time 타입과 매핑(예: 12:12:12) TemporalType.TIMESTAMP : 날짜와 시간 DB timestamp타입과 매핑(예 : 2020-02-12 12:12:12) |
//Entity
@Entity(name = "user2")
@Table(name = "user3")
@Getter
@Setter
public class User {
@Id
@GeneratedValue
private Long id;
private String name;
@Enumerated(value = EnumType.ORDINAL)
private RoleType ordinal;
@Enumerated(value = EnumType.STRING)
private RoleType string;
@Temporal(value = TemporalType.DATE)
private Date date;
@Temporal(value = TemporalType.TIME)
private Date time;
@Temporal(value = TemporalType.TIMESTAMP)
private Date timeStamp;
}
//Service
@Service
@RequiredArgsConstructor
public class TestService {
private final EntityManager em;
@Transactional
public void test() {
User user = new User();
Date date = new Date();
user.setName("test");
user.setOrdinal(RoleType.ADMIN);
user.setString(RoleType.ADMIN);
user.setTime(date);
user.setDate(date);
user.setTimeStamp(date);
em.persist(user);
}
}
8. @Lob
DB BLOB, CLOB 타입과 매핑 @Lob은 정의할 속성이 따로 없다.
대신 필드 타입이 문자열이면 CLOB, 나머지는 BLOB을 매핑
//Entity
@Entity(name = "user2")
@Table(name = "user3")
@Getter
@Setter
public class User {
@Id
@GeneratedValue
private Long id;
private String name;
@Enumerated(value = EnumType.ORDINAL)
private RoleType ordinal;
@Enumerated(value = EnumType.STRING)
private RoleType string;
@Temporal(value = TemporalType.DATE)
private Date date;
@Temporal(value = TemporalType.TIME)
private Date time;
@Temporal(value = TemporalType.TIMESTAMP)
private Date timeStamp;
@Lob
private String stringLob;
@Lob
private Integer integerLob;
}
//Service
@Service
@RequiredArgsConstructor
public class TestService {
private final EntityManager em;
@Transactional
public void test() {
User user = new User();
Date date = new Date();
user.setName("test");
user.setOrdinal(RoleType.ADMIN);
user.setString(RoleType.ADMIN);
user.setTime(date);
user.setDate(date);
user.setTimeStamp(date);
user.setStringLob("hello");
user.setIntegerLob(2);
em.persist(user);
}
}
Mysql에서의 결과를 보면 stringLob은 LONGTEXT, integerLob은 LONGBLOB으로 매핑된 것을 확인할 수 있다.
9. @Transient
@Transient 어노테이션을 붙인 필드는 DB에 저장하지도 조회하지도 않는다. 객체에 임시로 값을 보관하고 싶을 때 사용
@Entity(name = "user2")
@Table(name = "user3")
@Getter
@Setter
public class User {
@Id
@GeneratedValue
private Long id;
private String name;
@Enumerated(value = EnumType.ORDINAL)
private RoleType ordinal;
@Enumerated(value = EnumType.STRING)
private RoleType string;
@Temporal(value = TemporalType.DATE)
private Date date;
@Temporal(value = TemporalType.TIME)
private Date time;
@Temporal(value = TemporalType.TIMESTAMP)
private Date timeStamp;
@Lob
private String stringLob;
@Lob
private Integer integerLob;
//임시 사용
@Transient
private String trans;
}
@Transient를 단 trans필드가 DB컬럼에 추가되지 않는것을 확인할 수 있다.
참고
'스프링' 카테고리의 다른 글
[스프링] Validation 방법(Validator, Bean Validation) 설명/예제 (0) | 2021.08.07 |
---|---|
[JPA] 3. 엔티티 연관 관계 매핑 정리/예제 (7) | 2021.02.28 |
[스프링] Spring Security 인증은 어떻게 이루어질까? (5) | 2021.01.21 |
[SpringBoot] thymeleaf + Mybatis + PageHelper 페이징 간단예제 (0) | 2020.10.31 |
[SpringBoot] spring-security 회원가입/로그인/권한 예제 (2) | 2020.10.02 |