spring

@MappedSuperclass와 @AttributeOverrides, @AttributeOverride

seungmin576 2025. 3. 14. 18:16
package org.example.expert.domain.common.entity;

import jakarta.persistence.*;
import lombok.Getter;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;

import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;

@Getter
@MappedSuperclass
@EntityListeners(AuditingEntityListener.class)
public abstract class Timestamped {

    @CreatedDate
    @Column(updatable = false)
    @Temporal(TemporalType.TIMESTAMP)
    private LocalDateTime createdAt;

    @LastModifiedDate
    @Column
    @Temporal(TemporalType.TIMESTAMP)
    private LocalDateTime modifiedAt;

    @PrePersist
    public void onPrePersist() {    //생성시 나노초 저장안하기
        this.createdAt = createdAt.truncatedTo(ChronoUnit.SECONDS);
        this.modifiedAt = modifiedAt.truncatedTo(ChronoUnit.SECONDS);
    }

    @PreUpdate
    public void onPreUpdate() { //업뎃시 나노초 저장안하기
        this.modifiedAt = modifiedAt.truncatedTo(ChronoUnit.SECONDS);
    }
}

이러한 추상클래스가 있어서 다른 entity 클래스들에서 상속받아 편하게 생성시간과 수정시간을 생성할 수 있다.

그러나 만약 어떤 클래스는 Timestamp 클래스를 상속 받더라도 수정시간은 필요없다면?

 

package org.example.expert.domain.log.entity;

import jakarta.persistence.*;
import lombok.NoArgsConstructor;
import org.example.expert.domain.common.entity.Timestamped;
import org.springframework.http.HttpStatus;

@Entity
@NoArgsConstructor
@AttributeOverrides({
        @AttributeOverride(name = "modifiedAt", column = @Column(insertable = false, updatable = false))
})
public class Log extends Timestamped {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String message;

    @Enumerated(EnumType.STRING)
    private HttpStatus httpStatus;

    public Log(String message, HttpStatus httpStatus) {
        this.message = message;
        this.httpStatus = httpStatus;
    }
}

로그를 저장하는 entity 클래스이다.

log는 저장만 필요할뿐 수정을 할 이유는 전혀 없어서 modifiedAt이 전혀 필요없다.

굳이 Timestamped를 상속을 받으면서 modifiedAt을 저장하고 싶지 않다면

@AttributeOverrides, @AttributeOverride를 사용하면 된다.

@AttributeOverrides와 @AttributeOverride는 JPA에서 사용하는 어노테이션으로, 엔티티 상속 관계에서 상속받은 속성의 매핑을 변경하거나 재정의할 때 사용한다.

 

@AttributeOverride

@AttributeOverride는 단일 속성을 재정의할 때 사용한다.

예를 들어, 상위 클래스의 필드 이름과 매핑 정보를 하위 클래스에서 다르게 지정하고 싶을 때 사용한다.

@AttributeOverride(name = "속성_이름", column = @Column(새로운_속성_정보))

위의 Log 클래스에서는

 

        @AttributeOverride(name = "modifiedAt", column = @Column(insertable = false, updatable = false))

 

이런식으로 사용했는데 의미는 modifiedAt 필드의 속성을 생성과 업데이트가 불가능하게 Log 클래스에서 재정의한 것이다.

 

 

 

@AttributeOverrides

 

@AttributeOverrides는 여러 속성을 한꺼번에 재정의할 때 사용된다.

@AttributeOverride 어노테이션을 배열로 받아서 여러 속성을 재정의할 수 있다.

@AttributeOverrides({
    @AttributeOverride(name = "속성_이름1", column = @Column(새로운_속성_정보1)),
    @AttributeOverride(name = "속성_이름2", column = @Column(새로운_속성_정보2))
})

사실 위 Log 클래스에서는 사용할 필요가 없었다.

 

 

 

 

 

Log 클래스에 적용한 결과

H2 데이터베이스이다. MODIFIED_AT이 null인걸 확인 가능하다.

저렇게 필드는 생기고 null로 저장되는데 만약 필드조차 생기게 하고 싶지 않다면 Timestamped 클래스를 상속 받는게 아니라 따로 코드로 작성해주는게 좋아 보인다.