2019. 12. 30. 15:16ㆍSpring/Basic
@MappedSuperclass로 중복 컬럼 상속화
- Entity별 공통 요소 상속의 필요성
- @MappedSuperclass를 이용하여 공통요소를 Super Class에 정의
- 매우 간단해진 기존 Entity
- 공통 컬럼명을 override 하고싶을 경우엔 @AttributeOverride로 재정의
1. Entity별 공통 요소 상속의 필요성
@NoArgsConstructor(access = AccessLevel.PROTECTED) @Getter @Setter @Entity @Table(name = "user") @DynamicUpdate @DynamicInsert public class User implements Serializable { private static final long serialVersionUID = -563329217866858622L; @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(updatable = false, nullable = false, columnDefinition = "INT(11)") private Long id; @Column(nullable = false, length = 1, columnDefinition = "CHAR(1) DEFAULT '0'") private String type; @Column(nullable = false, unique = true, length = 100) private String email; @Column(nullable = false, length = 50) private String name; @Column(nullable = false, length = 1, columnDefinition = "CHAR(1) DEFAULT '1'") private String sex; @Column(nullable = false, length = 6) private String birthDate; @Column(nullable = false, length = 20) private String phoneNumber; @Column(nullable = false, length = 150) private String password; @Column(nullable = false, columnDefinition = "TINYINT(1) DEFAULT 0", length = 1) private boolean del; @Temporal(TemporalType.TIMESTAMP) @Column(updatable = false, columnDefinition = "TIMESTAMP DEFAULT CURRENT_TIMESTAMP") private Date createTimestamp; @Temporal(TemporalType.TIMESTAMP) @Column(nullable = true, columnDefinition = "TIMESTAMP DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP") protected Date updateTimestamp; @Builder public User(String type, String name, String email, String sex, String birthDate, String phoneNumber, String password) { this.type = type; this.name = name; this.email = email; this.sex = sex; this.birthDate = birthDate; this.phoneNumber = phoneNumber; this.password = password; } @PrePersist protected void onCreate() { createTimestamp = Timestamp.valueOf(LocalDateTime.now()); } @PreUpdate protected void onUpdate() { updateTimestamp = Timestamp.valueOf(LocalDateTime.now()); } }
@NoArgsConstructor(access = AccessLevel.PROTECTED) @Getter @Setter @Entity(name = "store") @DynamicUpdate public class Store implements Serializable{ private static final long serialVersionUID = 3321044622977739271L; @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(updatable = false, nullable = false, columnDefinition = "INT(11)") private Long id; @JsonBackReference @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "user_id", foreignKey = @ForeignKey(name = "FK_STORE_USER")) private User user; @Column(nullable=false, length = 100) private String name; @Column(length = 30) private String storeBusiness; @Column(nullable = false, columnDefinition = "TINYINT(1) DEFAULT 0", length = 1) private boolean del; @Temporal(TemporalType.TIMESTAMP) @Column(updatable = false, columnDefinition = "TIMESTAMP DEFAULT CURRENT_TIMESTAMP") private Date createTimestamp; @Temporal(TemporalType.TIMESTAMP) @Column(nullable = true, columnDefinition = "TIMESTAMP DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP") protected Date updateTimestamp; @Builder private Store(User user, String name, String storeBusiness) { this.user = user; this.name = name; this.storeBusiness = storeBusiness; } @PrePersist protected void onCreate() { createTimestamp = Timestamp.valueOf(LocalDateTime.now()); } @PreUpdate protected void onUpdate() { updateTimestamp = Timestamp.valueOf(LocalDateTime.now()); } }
demo 프로젝트의 엔티티인 User와 Store에 중복적으로 이용되는 id, del, create_timestamp, update_timestamp
컬럼이 있다.
이 컬럼들은 User, Store 뿐만 아니라 이 후에 추가될 엔티티들에게도 기본적으로 필요한 컬럼들입니다.
이런 컬럼들은 따로 SuperClass로 정의하여 하위 엔티티들이 상속을 받는다면 중복 코드를 줄여 한눈에 보기에도 편할 뿐 아니라, 개발 중 실수로 누락하여 에러를 발생하는 상황을 방지할 수 있어 좋습니다.
저장 또는 수정 전에 자동으로 발생되는 create_timestamp 설정 및 update_timestamp 설정에 관한 기본 메서드 역시 Super Class에 정의하면 좋을 메서드들입니다.
2. @MappedSuperclass를 이용하여 공통요소를 Super Class에 정의
@Getter @Setter @MappedSuperclass public abstract class BaseEntity implements Serializable { private static final long serialVersionUID = 1146360965411496820L; @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(updatable = false, nullable = false, columnDefinition = "INT(11)") private Long id; @Temporal(TemporalType.TIMESTAMP) @Column(updatable = false, columnDefinition = "TIMESTAMP DEFAULT CURRENT_TIMESTAMP") private Date createTimestamp; @Temporal(TemporalType.TIMESTAMP) @Column(nullable = true, columnDefinition = "TIMESTAMP DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP") protected Date updateTimestamp; @Column(nullable = false, columnDefinition = "TINYINT(1) DEFAULT 0", length = 1) private boolean del; @PrePersist protected void onCreate() { createTimestamp = Timestamp.valueOf(LocalDateTime.now()); } @PreUpdate protected void onUpdate() { updateTimestamp = Timestamp.valueOf(LocalDateTime.now()); } }
기본적으로 Entity에서 공통적으로 이용될 id, create_timestamp, update_timestamp, del
컬럼과 onCreate, onUpdate 함수를 BaseEntity에 작성했습니다.
이때, BaseEntity는 상속될 용도로만 이용될 뿐이며 단독적으로 생성자 생성 될 상황은 없기 때문에 추상 클래스로 정의했습니다.
javax.persistence.MappedSuperclass 애너테이션을 추상클래스 위에 붙이면 됩니다.
3. 매우 간단해진 기존 Entity
@NoArgsConstructor(access = AccessLevel.PROTECTED) @Getter @Setter @Entity @Table(name = "user") @DynamicUpdate @DynamicInsert public class User extends BaseEntity implements Serializable { private static final long serialVersionUID = -563329217866858622L; @Column(nullable = false, length = 1, columnDefinition = "CHAR(1) DEFAULT '0'") private String type; @Column(nullable = false, unique = true, length = 100) private String email; @Column(nullable = false, length = 50) private String name; @Column(nullable = false, length = 1, columnDefinition = "CHAR(1) DEFAULT '1'") private String sex; @Column(nullable = false, length = 6) private String birthDate; @Column(nullable = false, length = 20) private String phoneNumber; @Column(nullable = false, length = 150) private String password; @Builder public User(String type, String name, String email, String sex, String birthDate, String phoneNumber, String password) { this.type = type; this.name = name; this.email = email; this.sex = sex; this.birthDate = birthDate; this.phoneNumber = phoneNumber; this.password = password; } }
@NoArgsConstructor(access = AccessLevel.PROTECTED) @Getter @Setter @Entity(name = "store") @DynamicUpdate public class Store extends BaseEntity implements Serializable { private static final long serialVersionUID = 3321044622977739271L; @JsonBackReference @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "user_id", foreignKey = @ForeignKey(name = "FK_STORE_USER")) private User user; @Column(nullable=false, length = 100) private String name; @Column(length = 30) private String storeBusiness; @Builder private Store(User user, String name, String storeBusiness) { this.user = user; this.name = name; this.storeBusiness = storeBusiness; } }
BaseEntity에 작성한 코드를 제거한 후 BaseEntity를 상속받으면 끝!
3. 공통 컬럼명을 override 하고싶을 경우엔 @AttributeOverride로 재정의
PK로 이용하는 id 컬럼을 id가 아닌 다른 이름으로 사용할 경우엔 어떻게 해야할까
(예를 들면 Store 엔티티에서 PK를 store_id
로 이용하는 경우)
이 경우에는, del, create_timestamp, update_timestamp
를 공통적으로 이용하고 있음에도 BaseEntity를 상속할 수 없는걸까?
이 경우에는 @AttributeOverride 로 매칭할 컬럼명을 override 할 수 있습니다.
@AttributeOverride(name = "id", column = @Column(name = "store_id")) @Entity(name = "store") @DynamicUpdate public class Store extends BaseEntity implements Serializable { }
이렇게 store_id 컬럼을 @Column Long id
에 매핑할 수 있으며
@AttributeOverride(name = "updateTimestamp", column = @Column(name = "reply_timestamp", columnDefinition = "TIMESTAMP DEFAULT NULL")) @NoArgsConstructor(access = AccessLevel.PROTECTED) @Getter @Setter @ApiModel(description = "문의") @Entity(name = "contact") @DynamicUpdate public class Contact extends BaseEntity implements Serializable { ... }
updateTimestamp에 contact 엔티티의 reply_timestamp 를 매칭시킬 수 있습니다.
이때, 기존에 @Column 애너테이션에 설정했었던 columnDefinition 속성도 정의할 수 있습니다.
(이 외의 다른 속성 역시 재정의 할 수 있습니다.)
'Spring > Basic' 카테고리의 다른 글
[Spring Boot] JavaConfig로 Datasource 설정하기 (0) | 2020.04.05 |
---|---|
[Spring Security] postman에서 csrf token 이용하기 (0) | 2020.03.23 |
[Spring Boot] JPA와 MyBatis 동시에 사용하기 (2) | 2019.12.27 |
[Spring Boot] Custom Banners (2) | 2019.08.02 |
[Spring Boot] 이미지 파일 경로 외부에 설정하기 with yml (5) | 2019.07.31 |