Spring Security와 Attribute Converter (이곳을 클릭 하지 마세요! 아직 글 못썼습니다 ㅠㅠ) 이슈에 대한 글을 적다가, 글의 내용이 너무 방대해져 포스팅을 나누어서 하려고 한다.
Attribute Converter는 javax.persistence
패키지에 정의된 interface로 Entity
와 DB
사이에서 데이터를 변환하는 역할을 한다.
Attribute Converter는 <X,Y>로 2개의 제네릭 타입을 받고있다.
X: entity의 타입
Y: DB의 타입
이다.
Attribute Converter 정의된 메소드는 딱 2가지인데
public Y convertToDatabaseColumn(X attribute);
public X convertToEntityAttribute(Y dbData);
가 있다. 메소드 명이 직관적이기 때문에 의미를 바로 파악할 수 있겠지만, 굳이 설명하자면 Entity <-> DB
사이를 변환하는 메소드이다.
AttributeConverter<>
는 javax.persistence
패키지에 포함된 인터페이스이다.
따라서 아래와 같이 gradle에 depencency를 추가해준다.
Spring : implementation("javax.persistence:javax.persistence-api:2.2")
SpringBoot : implementation("org.springframework.boot:spring-boot-starter-data-jpa:2.6.4")
Spring : implementation 'javax.persistence:javax.persistence-api:2.2'
SpringBoot : implementation 'org.springframework.boot:spring-boot-starter-data-jpa:2.6.4'
Spring :
<dependency>
<groupId>javax.persistence</groupId>
<artifactId>javax.persistence-api</artifactId>
<version>2.2</version>
</dependency>
SpringBoot :
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
<version>2.6.4</version>
</dependency>
우선, AttributeConverter<X, Y>를 상속받는 클래스를 정의한다.
나는 MutableList <> String 간에 JSON Serialize / Deserialize 를 하기 때문에 jackson 라이브러리를 추가로 사용했다.
package 패키지명
import com.fasterxml.jackson.core.type.TypeReference
import com.fasterxml.jackson.databind.ObjectMapper
import import javax.persistence.*
class MutableListConverter() : AttributeConverter<MutableList<Long>, String> {
private val mapper: ObjectMapper = ObjectMapper()
override fun convertToDatabaseColumn(attribute: MutableList<Long>?): String {
return mapper.writeValueAsString(attribute)
}
override fun convertToEntityAttribute(dbData: String?): MutableList<Long> {
return mapper.readValue(dbData ?: "", object : TypeReference<MutableList<Long>>() {})
}
}
package 패키지명
import com.fasterxml.jackson.core.type.TypeReference
import com.fasterxml.jackson.databind.ObjectMapper
import import javax.persistence.*
@Entity
@Table(name = "FAVORITE")
data class FavoriteEntity(
// 생성자
@Column(name = "product_id_list")
@Convert(converter = MutableListConverter::class)
var productIdList: MutableList<Long> = mutableListOf()
) {
// 클래스 내부
// 메소드 정의 등
}
class MutableListConverter() : AttributeConverter<MutableList<Long>, String> {
private val mapper: ObjectMapper = ObjectMapper()
override fun convertToDatabaseColumn(attribute: MutableList<Long>?): String {
return mapper.writeValueAsString(attribute)
}
override fun convertToEntityAttribute(dbData: String?): MutableList<Long> {
return mapper.readValue(dbData ?: "", object : TypeReference<MutableList<Long>>() {})
}
}
실제로 사용중인 코드의 일부이다.
AttributeConverter<X, Y>
를 상속하여 Converter를구현한 뒤,
Entity에 @Convert(converter = 클래스)
어노테이션을 붙여주기면 하면 된다.
Entity에 List형태로 값을 쓰면(add(), remove()...) DB에는 MutableListConverter
에 정의된 convertToDatabaseColumn
를 거쳐 String 형태로 저장된다.
반대로 DB에서 값을 읽어오면(repository.findById 등) convertToEntityAttribute
를 통해 MutableList<>
형태로 Entity에 할당된다.