EntityListener란 특정 메서드에 어노테이션을 붙이면 어노테이션에서 명시된 시점에 해당 메서드가 호출되어 실행되는 것을 말한다.
예를 들어, 내가 View단에서 데이터를 보낼 때 [1,2,3,4] 같은 List 형식이 아닌 String 형식인 "1,2,3,4"로 데이터를 받았다고 가정하자.
그럴 경우 우리는 로직상 이 "1,2,3,4"를 List 형식인 [1,2,3,4]로 바꾸거나, 반대로 [1,2,3,4] List Data를 "1,2,3,4"로 변환하는 과정이 필요할 수도 있다.
그런데 이런 Case가 매번 실행되어야 한다면 같은 메서드를 계속해서 입력해줘야 한다는 것이고, 이는 객체지향적인 개발이 아닐 것이다.
따라서 EntityLister를 활용해 특정 메서드를 명시된 시점에 실행시켜 메서드의 중복 없이도 내가 원하는 로직을 실행하도록 하는 것이다.
import com.example.demo.domain.BaseTimeEntity;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import javax.persistence.*;
@Getter
@NoArgsConstructor
@Entity
@Table(name = "posts")
public class Posts extends BaseTimeEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name="id")
private Long id;
@Column(length=500, nullable=false, name = "title")
private String title;
@Column(columnDefinition = "TEXT", nullable = false, name = "content")
private String content;
@Column(name = "author")
private String author;
// 개인적인 취향. 필자는 생성자를 Builder 형식으로 지정하는 것을 좋아한다.
@Builder
public Posts(String title, String content, String author) {
this.title = title;
this.content = content;
this.author = author;
}
// EntityLister 부분
@PrePersist
public void prePersist(){
this.title = this.title==null?"없음":this.title;
}
}
내가 User 정보를 저장하고 싶다고 가정하자.
이 때 User 정보에서 "주소"라는 정보가 존재할 것이다.
그런데 이 "주소"라는 정보는 한 번에 객체 형식으로 다루는 것이 더욱 편하다.
예를 들어 내가 User의 우편 번호 및 주소를 알고 싶을 때 원래라면 User.zipCode와 user.address를 통해 알아내야 했지만, 이 둘을 Address라는 객체에 모아 User.address를 통해 한 번에 데이터를 뽑아와 처리할 수 있다면 더욱 좋을 것이다.
이때 활용하는 것이 @Embedded와 @Embeddable이다.
사실 @Embeddable은 생략 가능하지만 가독성을 위해 웬만하면 추가하는 것을 추천한다.
@Entity
public class User{
@Id
@GeneratedValue(startegy=GenerationType.IDENTITY)
private Long id;
private String name;
// 객체로 다루고 싶은 주소 정보
@Embedded
private Address address;
}
/////// 객체로 다룰 주소 정보에 대한 Entity ////////////
@Embeddable // 생략은 가능
public class Address{
private String zipCode;
private String address;
private String detailAddress;
}
여기서 주의 깊게 봐야 하는 것은 이 @Embedded는 @OneToMany나 @ManyToOne처럼 따로 Table을 만드는 게 아니라 그냥 User Table에 ZIPCODE, ADDRESS, DETAILADDRESS라는 Column 3개가 한 번에 추가되는 것이며 데이터를 뽑아올 때도 Table의 ZIPCODE Column을 zipCode에 저장하는 등의 형식으로 데이터를 처리하는 것이다.
아마 JPA의 장점 중 하나는 "패러다임 불일치 문제의 해결"일 것이다.
그리고 패러다임 불일치 문제로 가장 많이 거론되는 예시가 "Super타입과 Sub 타입"일 것이다.
JPA는 Annotation을 통해 이 문제를 해결하고 있다.
@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name="DTYPE")
public class Game {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private int price;
}
@Entity
@DiscriminatorValue("S")
public class Starcraft extends Game{
}
@Entity
@DiscriminatorValue("L")
public class LOL extends Game{
}
먼저 위 예시를 보자면 Game이라는 큰 Table이 존재하고, 이 Table이 Suepr Type이 되는 것이다.
그리고 이 Super Type에 연동되는 Sub Type Table인 Starcraft와 LOL이라는 Table이 존재하게 될 것이다.
이제 우리는 Super와 Sub라는 DB의 특수한 패러다임을 객체 지향에서 매우 익숙한 상속 관계를 통해서 처리할 수 있게 되는 것이다.
사용되는 Annotation에 대한 설명은 아래와 같다.