programing

JPA 및 휴지 상태를 사용하여 MySQL JSON 열을 Java 엔티티 속성에 매핑하는 방법

bestprogram 2023. 3. 18. 09:28

JPA 및 휴지 상태를 사용하여 MySQL JSON 열을 Java 엔티티 속성에 매핑하는 방법

MySQL 컬럼이 JSON 타입으로 선언되어 있는데 JPA/Hibernate로 매핑하는 데 문제가 있습니다.백엔드에서 스프링 부츠를 사용하고 있습니다.

다음은 제 코드의 일부입니다.

@Entity
@Table(name = "some_table_name")
public class MyCustomEntity implements Serializable {

private static final long serialVersionUID = 1L;

@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;

@Column(name = "json_value")
private JSONArray jsonValue;

프로그램에서 오류를 반환하고 열을 매핑할 수 없다는 메시지를 표시합니다.

mysql 테이블에서 열은 다음과 같이 정의됩니다.

json_value JSON NOT NULL;

다음과 같이 하는 것이 좋습니다.

  • Map에서 String으로 변환기(아트리뷰트 변환기)를 작성하거나 그 반대도 마찬가지입니다.
  • Map을 사용한 도메인(엔티티) 클래스의 mysql JSON 열 유형 매핑

암호는 아래입니다.

Json ToMap Converted.java

@Converter
public class JsonToMapConverter 
                    implements AttributeConverter<String, Map<String, Object>> 
{
    private static final Logger LOGGER = LoggerFactory.getLogger(JsonToMapConverter.class);

    @Override
    @SuppressWarnings("unchecked")
    public Map<String, Object> convertToDatabaseColumn(String attribute)
    {
        if (attribute == null) {
           return new HashMap<>();
        }
        try
        {
            ObjectMapper objectMapper = new ObjectMapper();
            return objectMapper.readValue(attribute, HashMap.class);
        }
        catch (IOException e) {
            LOGGER.error("Convert error while trying to convert string(JSON) to map data structure.");
        }
        return new HashMap<>();
    }

    @Override
    public String convertToEntityAttribute(Map<String, Object> dbData)
    {
        try
        {
            ObjectMapper objectMapper = new ObjectMapper();
            return objectMapper.writeValueAsString(dbData);
        }
        catch (JsonProcessingException e)
        {
            LOGGER.error("Could not convert map to json string.");
            return null;
        }
    }
}

도메인(엔티티 매핑) 클래스의 일부

...

@Column(name = "meta_data", columnDefinition = "json")
@Convert(attributeName = "data", converter = JsonToMapConverter.class)
private Map<String, Object> metaData = new HashMap<>();

...

이 솔루션은 나에게 완벽하게 효과가 있다.

이러한 타입을 모두 수동으로 작성할 필요는 없습니다.Maven Central을 통해 입수할 수 있는 것은 다음과 같습니다.

<dependency>
    <groupId>com.vladmihalcea</groupId>
    <artifactId>hibernate-types-52</artifactId>
    <version>${hibernate-types.version}</version> 
</dependency> 

자세한 내용은 Hibernate Types 오픈소스 프로젝트를 참조하십시오.

자, 이제 모든 것이 어떻게 돌아가는지 설명하겠습니다.

다음 엔티티가 있다고 가정합니다.

@Entity(name = "Book")
@Table(name = "book")
@TypeDef(
    name = "json", 
    typeClass = JsonType.class
)
public class Book {
 
    @Id
    @GeneratedValue
    private Long id;
 
    @NaturalId
    private String isbn;
 
    @Type(type = "json")
    @Column(columnDefinition = "json")
    private String properties;
 
    //Getters and setters omitted for brevity
}

위의 코드 스니펫에서 다음 두 가지를 주의해 주십시오.

  • @TypeDef새로운 커스텀 휴지 타입을 정의하는 데 사용됩니다.json이것은, 에 의해서 처리됩니다.
  • propertiesAtribute에는json컬럼 타입으로 매핑되어 있습니다.String

바로 그거야!

엔티티를 저장하는 경우:

Book book = new Book();
book.setIsbn("978-9730228236");
book.setProperties(
    "{" +
    "   \"title\": \"High-Performance Java Persistence\"," +
    "   \"author\": \"Vlad Mihalcea\"," +
    "   \"publisher\": \"Amazon\"," +
    "   \"price\": 44.99" +
    "}"
);
 
entityManager.persist(book);

휴지 상태에서는 다음 SQL 문이 생성됩니다.

INSERT INTO
    book 
(
    isbn, 
    properties, 
    id
) 
VALUES
(
    '978-9730228236', 
    '{"title":"High-Performance Java Persistence","author":"Vlad Mihalcea","publisher":"Amazon","price":44.99}',  
    1
)

또한 다시 로드하여 수정할 수도 있습니다.

Book book = entityManager
    .unwrap(Session.class)
    .bySimpleNaturalId(Book.class)
    .load("978-9730228236");
     
book.setProperties(
    "{" +
    "   \"title\": \"High-Performance Java Persistence\"," +
    "   \"author\": \"Vlad Mihalcea\"," +
    "   \"publisher\": \"Amazon\"," +
    "   \"price\": 44.99," +
    "   \"url\": \"https://www.amazon.com/High-Performance-Java-Persistence-Vlad-Mihalcea/dp/973022823X/\"" +
    "}"
);

다음 명령을 사용하여 최대 절전 모드로 전환UPDATE설명:

SELECT  b.id AS id1_0_
FROM    book b
WHERE   b.isbn = '978-9730228236'
 
SELECT  b.id AS id1_0_0_ ,
        b.isbn AS isbn2_0_0_ ,
        b.properties AS properti3_0_0_
FROM    book b
WHERE   b.id = 1    
 
UPDATE
    book 
SET
    properties = '{"title":"High-Performance Java Persistence","author":"Vlad Mihalcea","publisher":"Amazon","price":44.99,"url":"https://www.amazon.com/High-Performance-Java-Persistence-Vlad-Mihalcea/dp/973022823X/"}'
WHERE
    id = 1

깃헙에서 모든 코드를 사용할 수 있습니다.

@J. Wang이 응답할 수 없는 경우:

이 의존관계를 추가해 보세요(최대 절전 모드 5.1 및 5.0, 기타 버전 확인은 이쪽).

<dependency>
    <groupId>com.vladmihalcea</groupId>
    <artifactId>hibernate-types-5</artifactId>
    <version>1.2.0</version>
</dependency>

엔티티에 이 행을 추가합니다.

@TypeDef(name = "json", typeClass = JsonStringType.class)

따라서 엔티티 클래스의 풀버전은 다음과 같습니다.

@Entity
@Table(name = "some_table_name")
@TypeDef(name = "json", typeClass = JsonStringType.class)
public class MyCustomEntity implements Serializable {

   private static final long serialVersionUID = 1L;

   @Id
   @GeneratedValue(strategy = GenerationType.AUTO)
   private Long id;

   @Type( type = "json" )
   @Column( columnDefinition = "json" )
   private List<String> jsonValue;
}

spring boot 1.5.9 및 hibernate-types-5 1.2.0을 사용하여 코드를 테스트합니다.

json 배열 내의 값이 단순한 문자열일 경우 다음과 같이 수행할 수 있습니다.

@Type( type = "json" )
@Column( columnDefinition = "json" )
private String[] jsonValue;

헤릴 무라토비치의 대답은 좋지만JsonToMapConverter구현해야 합니다.AttributeConverter<Map<String, Object>, String>,것은 아니다.AttributeConverter<String, Map<String, Object>>여기 나에게 맞는 코드가 있습니다.

@Slf4j
@Converter
public class JsonToMapConverter implements AttributeConverter<Map<String, Object>, String> {
    @Override
    @SuppressWarnings("unchecked")
    public Map<String, Object> convertToEntityAttribute(String attribute) {
        if (attribute == null) {
            return new HashMap<>();
        }
        try {
            ObjectMapper objectMapper = new ObjectMapper();
            return objectMapper.readValue(attribute, HashMap.class);
        } catch (IOException e) {
            log.error("Convert error while trying to convert string(JSON) to map data structure.", e);
        }
        return new HashMap<>();
    }

    @Override
    public String convertToDatabaseColumn(Map<String, Object> dbData) {
        try {
            ObjectMapper objectMapper = new ObjectMapper();
            return objectMapper.writeValueAsString(dbData);
        } catch (JsonProcessingException e) {
            log.error("Could not convert map to json string.", e);
            return null;
        }
    }
}

Kotlin에서는 위의 제안들을 다음과 같이 변형/조합하여 사용할 수 있었습니다.

@Entity
@Table(name = "product_menu")
@TypeDef(name = "json", typeClass = JsonStringType::class)
data class ProductMenu(

    @Type(type = "json")
    @Column(name = "menu_json", columnDefinition = "json")
    @Convert(attributeName = "menuJson", converter = JsonToMapConverter::class)
    val menuJson: HashMap<String, Any> = HashMap()

) : Serializable



import com.fasterxml.jackson.core.JsonProcessingException
import com.fasterxml.jackson.databind.ObjectMapper
import org.slf4j.LoggerFactory
import java.io.IOException
import javax.persistence.AttributeConverter

class JsonToMapConverter : AttributeConverter<String, HashMap<String, Any>> {

    companion object {
        private val LOGGER = LoggerFactory.getLogger(JsonToMapConverter::class.java)
    }

    override fun convertToDatabaseColumn(attribute: String?): HashMap<String, Any> {
        if(attribute == null) {
            return HashMap()
        }
        try {
            val objectMapper = ObjectMapper()
            @Suppress("UNCHECKED_CAST")
            return objectMapper.readValue(attribute, HashMap::class.java) as HashMap<String, Any>
        } catch (e: IOException) {
            LOGGER.error("Convert error while trying to convert string(JSON) to map data structure.")
        }
        return HashMap()
    }

    override fun convertToEntityAttribute(dbData: HashMap<String, Any>?): String? {
        return try {
            val objectMapper = ObjectMapper()
            objectMapper.writeValueAsString(dbData)
        } catch (e: JsonProcessingException) {
            LOGGER.error("Could not convert map to json string.")
            return null
        }
    }
}

언급URL : https://stackoverflow.com/questions/44308167/how-to-map-a-mysql-json-column-to-a-java-entity-property-using-jpa-and-hibernate