-
JPA를 사용하는 프로젝트에서 Entity를 만들다보면 공통으로 곂치는 부분들이 있었는데, 이런 사소한 부분도 코드를 재활용할 수 있게 묶어 상속할 수 있는 방법이 있을까 하며 찾다 발견하여 프로젝트에 적용한 내용을 소개하겠다.
EntityDate
package spring.postproject.Entity.Common; import lombok.Getter; import org.springframework.data.annotation.CreatedDate; import org.springframework.data.annotation.LastModifiedDate; import org.springframework.data.jpa.domain.support.AuditingEntityListener; import javax.persistence.Column; import javax.persistence.EntityListeners; import javax.persistence.MappedSuperclass; import java.time.LocalDateTime; @EntityListeners(AuditingEntityListener.class) @MappedSuperclass @Getter public abstract class EntityDate { @CreatedDate @Column(nullable = false,updatable = false) private LocalDateTime createdAt; @LastModifiedDate @Column(nullable = false,updatable = false) private LocalDateTime modifiedAt; }
@EntityListeners(AuditingEntityListener.class) - Spring JPA가 제공
데이터가 생성되거나 업데이트 되는 시간을 기록하기 위해 사용
@CreateDate를 붙인 어노테이션에 생성 날짜 저장
@LastModifiedDate를 붙인 어노테이션에 마지막 수정 날짜 저장
@CreateBy, @LastModifiedBy도 존재함
@MappedSuperclass
- 상속관계 매핑이 아니다.
- @MappedSuperclass가 선언되어 있는 클래스는 엔티티가 아니다. 당연히 테이블과 매핑도 안된다.
- 단순히 부모 클래스를 상속 받는 자식 클래스에 매핑 정보만 제공한다.
- 조회, 검색이 불가하다. 부모 타입으로 조회하는 것이 불가능하다는 이야기.(em.find(BaseEntity) 불가능)
- 직접 생성해서 사용할 일이 없으므로 추상 클래스로 만드는 것을 권장한다.
- 테이블과 관계가 없고, 단순히 엔티티가 공통으로 사용하는 매핑 정보를 모으는 역할을 한다.
- 주로 등록일, 수정일, 등록자, 수정자 같은 전체 엔티티에서 공통으로 적용하는 정보를 모을 때 사용한다.
- 참고
- JPA에서 @Entity 클래스는 @Entity나 @MappedSuperclass로 지정한 클래스만 상속할 수 있다.
이를 활성화 하기 위해 어플리케이션에 @EnableJpaAuditing 추가
@SpringBootApplication @EnableJpaAuditing public class PostProjectApplication { public static void main(String[] args) { SpringApplication.run(PostProjectApplication.class, args); }
Member 엔티티에 상속하여 확인
package spring.postproject.Member.Entity; import lombok.*; import spring.postproject.Common.EntityDate; import spring.postproject.Comment.Entitiy.Comment; import spring.postproject.Post.Entity.Post; import spring.postproject.Excetion.ExceptionBoard; import javax.persistence.*; import java.util.ArrayList; import java.util.List; @NoArgsConstructor(access = AccessLevel.PROTECTED) @Getter @Entity public class Member extends EntityDate { private static final int MAX_LENGTH_NICKNAME = 30; private static final int MAX_LENGTH_PASSWORD = 20; @Id @GeneratedValue(strategy = GenerationType.SEQUENCE) @Column(name = "member_id") private Long id; @Column(nullable = false, unique = true) private String userId; @Column(nullable = false, length = 20,unique = true) private String nickname; @Column(nullable = false, length = 20) private String password; @Enumerated(EnumType.STRING) private MemberRoll memberRoll; @OneToMany(mappedBy = "member",cascade = CascadeType.ALL) private List<Post> postList = new ArrayList<>(); @OneToMany(mappedBy = "member", cascade = CascadeType.ALL) private List<Comment> commentList = new ArrayList<>(); public void updateNickName(String nickname){ validationNickname(nickname); this.nickname = nickname; } public void updatePassword(String password){ validationPassword(password); this.password = password; } public void updateRole(MemberRoll memberRoll){ this.memberRoll = memberRoll; } public void validationNickname(String nickName){ if(nickName.isBlank()|| nickName.length() > MAX_LENGTH_NICKNAME){ throw ExceptionBoard.INVALID_LENGTH.getException(); } } public void validationPassword(String password){ if(password.isBlank()|| password.length() > MAX_LENGTH_PASSWORD){ throw ExceptionBoard.INVALID_LENGTH.getException(); } } @Builder public Member(String userId, String nickName, String password){ validationNickname(nickName); validationPassword(password); this.userId = userId; this.nickname = nickName; this.password = password; } }
H2 DB에서 확인 해볼 수 있었다.
ref
https://velog.io/@seongwon97/Spring-Boot-Entity-Listener
https://ict-nroo.tistory.com/129
https://wave1994.tistory.com/161
'프레임워크 > JPA' 카테고리의 다른 글
[Spring Data Jpa] delete VS deleteById (2) 2022.11.13 댓글