Java - 엔티티 관리

일반인3호·2023년 12월 28일

Java

목록 보기
1/4

공통된 컬럼이 존재한다면?

엔티티 설계를 하다보면 공통된 컬럼을 가진 엔티티들을 만들어야 할 때가 종종 있다.
똑같은 내용을 두 번 세 번 작업하지 않도록 공통된 부분을 상속받아서 땡겨 쓸 수 있도록 만들어보자.

@MappedSuperclass

JPA에는 @MappedSuperclass라는 어노테이션이 존재한다.
공식 주석에는 아래와 같이 설명하고 있다.

  • 의역 주의
    매핑할 정보들을 엔티티에 상속하여 적용할 수 있도록 클래스를 지정한다. 매핑된 상위 클래스는 정의된 구체적인 테이블은 따로 없다. 하위 클래스에만 적용되는 점을 제외하면 엔티티와 동일한 방식으로 매핑될 수 있다. 하위 클래스에 적용하면 상속된 매핑이 하위 클래스 테이블의 조건에 적용된다. 매핑 정보는 AttributeOverride 및 AssociationOverride 주석이나 해당 XML 요소를 사용하여 이러한 하위 클래스에서 재정의될 수 있다.

즉, 실체는 없으면서 공통으로 상속받아서 쓸 수 있는 테이블 포맷을 만들어둘 수 있다는 의미로 해석된다.

예를 들면, 현재 프로젝트에서 Q&A, 공지사항 등 게시판의 여러 형태가 나올 수 있는데 먼저 공통된 부분을 아래와 같이 상위 클래스로 구현해준다.


@Getter
@Setter
@ToString
@SuperBuilder
@MappedSuperclass
public class BaseBBSEntity {

	public static final int NOTICE_TITLE = 200;

	@ManyToOne
	@JoinColumn(name = "user")
	private User user;

	@Column(name = "title", nullable = false, length = NOTICE_TITLE)
	private String title;

	@Column(name = "contents", nullable = false, columnDefinition = "TEXT")
	private String contents;

	@Column(name = "registered_date")
	private LocalDateTime registeredDate;

	@Column(name = "fixed")
	@ColumnDefault("false")
	private Boolean fixed;

	@Column(name = "hits")
	@ColumnDefault("0")
	private Long hits;

}

그러면 공통된 컬럼을 가진 엔티티를 위 클래스를 상속받아 하위 클래스로 구현하면 된다.


@Getter
@Setter
@ToString
@SuperBuilder
@AllArgsConstructor
@NoArgsConstructor
@Entity
@Table(name = "nt_qna")
public class Qna extends BaseBBSEntity {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "qna_id")
    private Long rentQnaId;
    
    @Column(name = "qna_password")
    private String rentQnaPassword;

    @OneToMany(mappedBy = "qna")
    private List<QnaFile> qnaFiles;

}

@EntityListeners

이제는 공통된 컬럼 뿐만 아니라 공통된 값, 공통된 동작을 수행하고 싶을 때 구현하는 방법을 알아보자.

@EntityListeners 는 엔티티에 속성값으로 받은 콜백 리스너를 추가할 수 있는 어노테이션이라고 설명되어 있다. 간단하게 설명하면 콜백 리스너란 어떤 상태가 되거나 특정 동작이 실행되면 호출되는 함수라고 할 수 있다. 이는 엔티티 클래스나 @MappedSuperclass에 사용될 수 있다고 한다.

예를 들어 보통의 데이터를 관리할 때 저장된 시간, 수정된 시간을 활용하는 경우가 많다. 그래서 대중적으로 사용되는 코드가 아래의 BaseTimeEntity이다.

@Getter
@Setter
@ToString
@SuperBuilder
@MappedSuperclass
@EntityListeners(AuditingEntityListener.class)
public class BaseTimeEntity {

	@CreatedDate
	@Column(name = "created_date")
	private LocalDateTime createdDate;

	@LastModifiedDate
	@Column(name = "modified_date")
	private LocalDateTime modifiedDate;

}

여기서 사용된 AuditingEntityListener.class에 대해 설명하자면
Audit은 감시라는 의미로 엔티티의 특정 동작을 감시하겠다는 의미로 해석할 수 있다. 이 클래스의 경우 persisting과 updating을 감시하겠다는 클래스. 위 코드의 경우 Spring Data의 @CreatedDate, @LastModifiedDate를 이용해 해당 내용을 구현하고 있다. 따라서 엔티티가 저장될 때 생성 일자를, 수정될 때 마지막 수정 일자를 저장하게 된다.

@AttributeOverride / @AssociationOverride

상위 클래스의 필드를 재정의해야 할 필요가 있을 때 위의 두 컬럼을 사용하면 된다.

@AttrivuteOverride의 경우 컬럼의 이름을 재정의할 때 사용한다. 상위 클래스의 필드 이름을 name 속성에 넣고, 재정의할 컬럼의 이름을 column 속성에 넣는다.
@AttrivuteOverrides를 사용해 여러개의 컬럼을 재정의할 수 있다.

@Table(name = "nt_qna")
@AttributeOverride(name = "title", column = @Column(name = "qna_title"))
public class Qna extends BaseBBSEntity {
	...
}

@AssociationOverride의 경우 연관관계 조인컬럼의 이름을 재정의할 때 사용한다. 상위 클래스의 필드 이름을 name 속성에 넣고, 재 정의할 컬럼의 이름을 column 속성에 넣는다.
@AssociationOverrides를 사용해 여러개의 컬럼을 재정의할 수 있다.

@Table(name = "nt_qna")
@AssociationOverride(name="user", joinColumns=@JoinColumn(name="writer_id"))
public class Qna extends BaseBBSEntity {
	...
}
profile
'답답하면 니가 블로그 쓰던지' '예, 그래서 제가 써보겠습니다' 복붙이 난무하는 검색에 질려서 쓰는 블로그

0개의 댓글