@Entity@Column — DDL 옵션 설정기본키 매핑전략(@Id)직접 할당SEQUENCETABLEIDENTITYAUTO기타 컬럼매핑 레퍼런스 어노테이션Entity ListenerEntityListener class 따로 빼기EntityListener 에서 의존성 주입 (@Autowired는 안됨)Spring 에서 제공하는 기본 Listener(=AuditingEntityListener)@MappedSuperClass를 이용한 공통정보 추출
@Entity
@Entity // 기본 생성자가 필요함 -> @NoArgsConstructor 명시 해주어야함 @Table /* constraint나 index 적용하고 싶을 시에 사용. 근데 table이 Hibernate DDL로 만들어질 때만 적용이 됨. DB에 이미 만들어져 있으면 적용 안됨 @Table(name= 'users') 와 같이 테이블이름을 명시할 수 있음*/ // PROTECTED로 선언함으로써 같은 패키지, 그리고 상속하는 클래스에서만 호출이 가능함 @NoArgsConstructor(access = AccessLevel.PROTECTED) public class User{ @Id @GeneratedValue(strategy = ) // strategy 부분이 중요함 private Long id; @Column(updatable=false) // update 불가능한 field로 설정한것. column에 대한 추가정보 줄 수있음 // default, notnull 등등의 옵션 private LocalDateTime createdAt; @Transient // 영속성 처리에서 제외가 됨. db데이터에 반영되지 않고 해당 객체와 생명주기를 같이함 private String testData; // Enum class를 column으로 가질 때, 해당 annnotation에서 value를 // EnumType.STRING 해주지 않으면 DB에 저장되는 값은 0, 1 숫자로(EnumType.ORDINAL) 저장됨 // EnumType.ORDINAL -> 잠재적인 버그. 반드시 EnumType.STRING 달아주기 @Enumerated(value=EnumType.STRING) private Gender gender; }
- @Entity 로 선언하는 것이 클래스를 db 매핑용으로 사용하겠다는 의미
- jpa에서 사용할 엔티티 이름을 지정함
- default : 클래스 이름
- @Id : primary key — 항상 가져야 하는 값임. Entity라면
- 1차 캐쉬에서 이것을 이용하기 때문에
- @GeneratedValue : 자동으로 순차 증가하는 값
- 해당 객체를 생성하고 관리하기 위해서 Repository를 이용
- default constructor와 getter/setter를 통해서 entity가 작동을 함
- @Table(name= ..) 으로 매핑될 테이블의 이름을 명시할 수 있음
- 매핑할 테이블의 이름 지정
- default : 엔티티 이름
- @GeneratedValue
- @Enumerated
- JPA 스펙에 따르면, 모든 엔티티 클래스들은
public
혹은protected
no-arg constructor
가 필요함
@Column — DDL 옵션 설정
@Entity @Table(name = "member") @Getter @Setter public class Member { @Id @GeneratedValue(strategy = GenerationType.SEQUENCE) private Long id; @Column(name = "name", nullable = false, length = 30) // name VARCHAR(30) NOT NULL private String name; @Column(nullable = false, length = 30, unique = true) // unique 제약조건 private String nickName; // name 명시 안하면 필드이름 그대로 만듦 private int age; // age @Column(name = "addres", nullable = false) private String address; @Column(name = "description", nullable = true) private String description; }
drop table if exists member CASCADE drop sequence if exists hibernate_sequence create sequence hibernate_sequence start with 1 increment by 1 create table member ( id bigint not null, address varchar(255) not null, age integer not null, description varchar(255), name varchar(30) not null, nickName varchar(30) not null, primary key (id)) alter table member add constraint UK_1m3ighjll05v7njjxeopp823j unique (nickName)
- 운영환경에서는 안쓰기는 함
- 그러나 개발할 때 해당 코드만 보고도 RDB의 필드가 어떻게 생겼는지를 알 수 있기 때문에 업계에서는 사용하는 것을 권장함
- default 값을 넣기 위해서는 columnDefinition을 이용
@Column(columnDefinition = "integer default 25")
@Column
- insertable, updateable은 잘 안쓰긴 하는데 foreign key에서 좀 쓰는것 같음
기본키 매핑전략(@Id)
직접 할당
- 영속화 전에 애플리케이션에서 직접 값을 할당한다.
@Id @Column(name = "id") private Long id;
SEQUENCE
- 데이터베이스 시퀀스에서 식별자 값을 획득한 후 영속화
- oracle, h2, postgresql DB2 데이터베이스에서 사용가능
- MYSQL에서는 Sequence 기능을 지원하지 않음 [ 참고 ]
In general, I recommend using the SEQUENCE strategy because it allows Hibernate to use JDBC batching and other optimization strategies that require the delayed execution of SQL INSERT statements.
But you can’t use this strategy with a MySQL database. It requires a database sequence, and MySQL doesn’t support this feature.
@Id @GeneratedValue(strategy = GenerationType.SEQUENCE) private Long id; // DDL create sequence hibernate_sequence start with 1 increment by 1
- postgresql (GenerationType.SEQUENCE 일때 id 컬럼 desc 결과)

별다른
@SequenceGenerator
를 설정해 주지 않으면 hibernate_seq
시퀀스가 만들어짐TABLE
- 데이터베이스 시퀀스 생성용 테이블에서 식별자 값을 획득한 후 영속화
- 자주 사용하지는 않기에 이런 전략이 있다 정도
@Entity @Table(name = "test") @TableGenerator(name = "TEST_GENERATOR", table = "MY_SEQUENCE", pkColumnValue = "TEST_SEQ", allocationSize = 1) // pkColumnValue는 해당 테이블의 seqence_name 에 들어가는 값 class TestEntity { @Id @GeneratedValue(strategy = GenerationType.TABLE, generator = "TEST_GENERATOR") var id: Int = 0 var value: Int constructor(value: Int) { this.value = value } }
-- mysql create table my_sequence ( sequence_name varchar(255) not null, next_val bigint, primary key ( sequence_name ) )

IDENTITY
- 데이터베이스 엔티티를 저장해서 식별자 값을 획득한 후 영속화
- 엔티티가 영속화 되려면 식별자 값이 반드시 필요하기 때문에, em.persist() 시점에 INSERT 쿼리가 수행된다.
- 원래는 flush() 할 때 쓰기지연 저장소에서 INSERT 쿼리가 수행되지만 IDENTITY에서는 persistence context에서 관리할 때 id 가 필요하므로 persist() 에서 바로 쿼리를 수행하여 id값을 가져옴
- MySQL (AUTO_INCREMENT)
@Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id;
CREATE TABLE `test` ( `id` int NOT NULL AUTO_INCREMENT, `value` int NOT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
- Postgresql
- IDENTITY 전략일 때, accounts_id_seq 시퀀스가 생성됨

AUTO
- 데이터 베이스 방언(dialect — spring.jpa.properties.hibernate.dialect)에 따라서 자동으로 전략을 선택
- 데이터베이스 엔진에 맞는 쿼리를 만들어 줌
- MYSQL인 경우 IDENTITY
- ORACLE, H2, postgresql일 때는 SEQUENCE로
- postgresql (sequence 이름 설정안해주면
hibernate_sequence
로 자동 생성됨)

- 실무에서는 이걸로 많이 사용한다고 함
@Id @GeneratedValue(strategy = GenerationType.AUTO) private Long id; @Id @GeneratedValue private Long id;
기타 컬럼매핑 레퍼런스 어노테이션
@Entity @Table(name = "orders") public class Order { @Id @Column(name = "id") private String uuid; @Column(name = "order_datetime", columnDefinition = "TIMESTAMP") private LocalDateTime orderDatetime; @Enumerated(EnumType.STRING) private OrderStatus orderStatus; @Lob private String memo; }
@Column
(columnDefinition = “TIMESTAMP”) — LocalDateTime을 TIMESTAMP로 저장- 근데 명시 안해줘도 timestamp로 저장함. 아마 조금 강제하고 싶을 때 저렇게 명시해주나봄
- columnDefinition을 nVarchar(MSSQL의 문자열 타입)으로 지정해주면 length 속성을 추가해주어도 효과 없음. nVarchar(255) 와 같이 적어주어야 함
@Enumerated
— Enum 타입을 String으로 저장하고자 할때 위와 같이 명시- 해당 어노테이션을 붙이면 Mysql 에서는 컬럼타입이 enum 타입이 됨.
- varchar로 이용하기 위해서는 @Column(columnDefinition) 값을 정의해주면 됨
@Lob
— varchar(255)가 넘는 긴 텍스트를 넣고 싶을 때 사용함
@Temporal
— 생략 시, timestamp로 정의됨(ddl로 column 만들어 질 때)
@Transient
— 이 필드는 매핑하지 않는다
@Access
: JPA가 엔티티 접근하는 방식을 지정한다.
Entity Listener
- @PrePersist
- @PreUpdate
- @PreRemove
- @PostPersist
- @PostUpdate
- @PostRemove
- @PostLoad
- PrePersist, PreUpdate 를 가장 많이 현업에서 사용함(Audit(감독,심사)의 용도로)
- PrePersist 같은 경우에 createdAt, updatedAt 같은 필드의 값을 정해주는 용도로 많이 사용함
- setCreatedAt 이런 것을 실수로 적용 안 할수도 있으니
@PrePersist public void prePersist(){ this.createdAt = LocalDateTime.now(); this.updatedAt = LocalDateTime.now(); } @PreUpdate public void preUpdate(){ this.updatedAt = LocalDateTime.now(); }
EntityListener class 따로 빼기
// User.java @NoArgsConstructor @Entity @Data @EntityListeners(value=MyEntityListener.class) public class User implements Auditable{ } // Auditable.java public interface Auditable { LocalDateTime getCreatedAt(); LocalDateTime getUpdatedAt(); void setCreatedAt(LocalDateTime createdAt); void setUpdatedAt(LocalDateTime updatedAt); } // MyEntityListener.java public class MyEntityListener { @PrePersist public void prePersist(Object o){ if (o instanceof Auditable){ ((Auditable) o).setCreatedAt(LocalDateTime.now()); ((Auditable) o).setUpdatedAt(LocalDateTime.now()); } } @PreUpdate public void preUpdate(Object o){ if (o instanceof Auditable){ ((Auditable) o).setUpdatedAt(LocalDateTime.now()); } } }
EntityListener 에서 의존성 주입 (@Autowired는 안됨)
- 이유는 EntityListener가 SpringContainer에서 관리하는 범위에서 벗어나서 그렇다고 함
- ApplicationContext를 이용하여 아래와 같이 주입받을 수 있음
// BeanUtils.java -> ApplicationContextAware 이용 @Component public class BeanUtils implements ApplicationContextAware { private static ApplicationContext applicationContext; @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { BeanUtils.applicationContext = applicationContext; } public static <T> T getBean(Class<T> clazz){ return applicationContext.getBean(clazz); } } // UserEntityListener.java public class UserEntityListener { @PreUpdate @PrePersist public void preUpdateAndPersist(Object o){ UserHistoryRepository userHistoryRepository = BeanUtils.getBean(UserHistoryRepository.class); User user = (User) o; UserHistory userHistory = new UserHistory(); userHistory.setUserId(user.getId()); userHistory.setEmail(user.getEmail()); userHistory.setName(user.getName()); userHistoryRepository.save(userHistory); } }
Spring 에서 제공하는 기본 Listener(=AuditingEntityListener)
@SpringBootApplication @EnableJpaAuditing public class Application{ } @EntityListeners(value=AuditingEntityListener.class) public class User{ @CreatedDate private LocalDateTime createdAt; @LastModifiedDate private LocalDateTime updatedAt; }
- @EnableJpaAuditing
- @CreatedDate, @LastModifiedDate
- @CreatedBy, @LastModifedBy 라는 annotation도 존재하여 이를 통해 누가 변경, 생성했는지도 추적이 가능함
@MappedSuperClass를 이용한 공통정보 추출
//위에서 사용한 User, 또는 다른 클래스에서 공통적인 필드가 존재시 @Data @MappedSuperClass // 이렇게 정의함으로써 BaseEntity를 상속받는 entity들은 // createdAt과 updatedAt를 column으로 포함하게됨 @EntityListeners(value=AuditingEntityListener.class) public class BaseEntity{ @CreatedDate private LocalDateTime createdAt; @LastModifiedDate private LocalDateTime updatedAt; }