• JPA Auditing을 이용하여 Entity의 공통속성 상속하기(createAt, updateAt)

    2022. 6. 6.

    by. 내이름은 킹햄찌

    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

     

    [Spring JPA] Entity Listener

    Entity Listener는 엔티티의 변화를 감지하고 데이블의 데이터를 조작하는 일을 한다.

    velog.io

    https://ict-nroo.tistory.com/129

     

    [JPA] @MappedSuperclass

    @MappedSuperclass 객체의 입장에서 공통 매핑 정보가 필요할 때 사용한다. id, name은 객체의 입장에서 볼 때 계속 나온다. 이렇게 공통 매핑 정보가 필요할 때, 부모 클래스에 선언하고 속성만 상속 받

    ict-nroo.tistory.com

    https://wave1994.tistory.com/161

     

    Spring boot :: JPA @EntityListeners 정리

    이번에는 JPA Entity에서 이벤트가 발생할 때마다 특정 로직을 실행시킬 수 있는 @EntityListeners를 정리해보려고 한다. 예제 코드는 Spring boot와 Kotlin을 사용하여 작성하였다. JPA EntityListeners 란? 하..

    wave1994.tistory.com

     

    '프레임워크 > JPA' 카테고리의 다른 글

    [Spring Data Jpa] delete VS deleteById  (2) 2022.11.13

    댓글