Table에 추가적인 데이터를 Json 문자열로 저장하기 위해, JPA Converter를 이용하려고한다.
즉, 서비스단에서는 bean으로 사용을하고, DB에 저장될 때는 이 bean을 Json 문자열로 자동 파싱하여 저장하기 위해서이다.
일단 샘플로 Student 클래스를 만들자
@Entity
@Getter
@Setter
public class Student {
@Id
private String key;
private String name;
private int age;
private AdditionalData additionalData;
}
Student는 추가데이터(additionalData)를 갖고 있으며, 이 데이터는 additionalData라는 컬럼에 Json 형태로 들어갈 것이다.
@Getter
@Setter
public class AdditionalData {
private Double height;
private Double weight;
private String hobby;
}
AdditionalData는 위와같이 구성되어 있으며, 우리는 아무 생각없이 AdditionalData를 Student 객체에 넣어주기만하면 자동으로 Json형태로 변환되서 DB에 저장되길 원한다.
그러려면 컨버터가 필요하다. JPA Converter는 AttributeConverter라는 인터페이스를 구현함으로써 만들 수 있다.
AdditionalDataConveter를 만들어보자. (Json변환은 Jackson라이브러리를 사용한다.)
⇒ ObjectMapper는 thread-safe하다고 하여 static으로 선언해두고 사용해도됨
@Slf4j
public class AdditionalDataConverter implements AttributeConverter<AdditionalData, String> {
private final ObjectMapper objectMapper = new ObjectMapper();
@Override
public String convertToDatabaseColumn(AdditionalData additionalData) {
// AdditionalData -> Json문자열로 변환
try {
return objectMapper.writeValueAsString(additionalData);
} catch (JsonProcessingException e) {
log.error("fail to serialize as object into Json : {}", additionalData, e);
throw new RuntimeException(e);
}
}
@Override
public AdditionalData convertToEntityAttribute(String jsonStr) {
// Json 문자열 -> AdditionalData로 변환
try {
return objectMapper.readValue(jsonStr, AdditionalData.class);
} catch (IOException e) {
log.error("fail to deserialize as Json into Object : {}", jsonStr, e);
throw new RuntimeException(e);
}
}
}
Student에 컨버터 설정
@Entity
@Getter
@Setter
public class Student {
@Id
private String key;
private String name;
private int age;
@Convert(converter = AdditionalDataConverter.class)
private AdditionalData additionalData;
}
잘들어가는 것일까...? 테스트
@Component
public class StudentRunner implements ApplicationRunner {
@PersistenceContext
EntityManager em;
@Override
@Transactional
public void run(ApplicationArguments args) throws Exception {
AdditionalData additionalData = new AdditionalData();
additionalData.setHeight(180d);
additionalData.setWeight(70d);
additionalData.setHobby("음악감상");
Student student = new Student();
student.setKey("1");
student.setAge(20);
student.setName("corn");
student.setAdditionalData(additionalData);
em.persist(student);
}
}
Json으로 잘 들어가는 것 확인!!
그런데 사용하다보니, 생각보다 많은 서비스들이 추가적인데이터를 Json문자열로 저장하는 경우가 많았다. 그때마다 우리는 각기다른 additionalData에 해당하는 Converter를 만들어줘야했다. 예를들면 BookAdditionalData, CustomerAdditionalData 에 해당하는 BookAdditionalDataConverter, CustomerAdditionalDataConverter를 말이다. 코드는 대부분 중복이기 때문에 비효율적으로 느껴져 제네릭한 컨버터가 필요했다.
제네릭한 컨버터를 만들어보자
@Slf4j
public class GenericJsonConverter<T> implements AttributeConverter<T, String> {
private final ObjectMapper objectMapper = new ObjectMapper();
@Override
public String convertToDatabaseColumn(T additionalData) {
// AdditionalData -> Json문자열로 변환
try {
return objectMapper.writeValueAsString(additionalData);
} catch (JsonProcessingException e) {
log.error("fail to serialize as object into Json : {}", additionalData, e);
throw new RuntimeException(e);
}
}
@Override
public T convertToEntityAttribute(String jsonStr) {
// Json 문자열 -> AdditionalData로 변환
try {
return objectMapper.readValue(jsonStr, new TypeReference<T>(){});
} catch (IOException e) {
log.error("fail to deserialize as Json into Object : {}", jsonStr, e);
throw new RuntimeException(e);
}
}
}
GenericJsonConverter를 상속받은 AdditionDataConverter2 생성
public class AdditionDataConverter2 extends GenericJsonConverter<AdditionalData> {
}
이미 부모 객체에서 모두 구현되어 있기 때문에 위처럼만 선언해도 바로 사용가능하다.
AdditionDataConverter2로 컨버터 변경
@Entity
@Getter
@Setter
public class Student {
@Id
private String key;
private String name;
private int age;
@Convert(converter = AdditionDataConverter2.class)
private AdditionalData additionalData;
}
테스트결과 잘 동작하는 것을 확인했다.
이 또한 Converter를 additionalData수 만큼 만들어야 하지만, 중복되는 안의 로직들을 복붙안해도 된다는 장점이 있다.
음.. 제 머리로는 이게 한계인데.. 만약 더 좋은 방법이 있다면 추천을 해주세요~!
'프로그래밍 노트 > JPA' 카테고리의 다른 글
[JPA] 다양한 연관관계_다대일(N:1) (0) | 2019.10.27 |
---|---|
[JPA] 연관관계 매핑 기초(단방향/양방향) (5) | 2019.10.10 |
[JPA] 영속성관리_3 (플러시, 준영속) (0) | 2019.09.18 |
[JPA] 영속성관리_2 (영속성 컨텍스트 특징, 조회, 수정, 삭제) (0) | 2019.09.16 |
[JPA] 영속성 관리_1 (EntityManager, EntityManagerFactory, PersistContext) (0) | 2019.09.03 |