web-dev-qa-db-ja.com

JPAとHibernateを使用してMySQL JSON列をJavaエンティティプロパティにマップする方法-

MySQL列をタイプ[〜#〜] json [〜#〜]として宣言していて、それをJpa/Hibernateでマップするのに問題があります。私はバックエンドでSpring Bootを使用しています。

これが私のコードのほんの一部です:

@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;

13
Heril Muratovic

私はこの方法を好む:

  • MapからStringへ、またはその逆へのコンバーター(属性コンバーター)の作成。
  • マップを使用してドメイン(エンティティ)クラスでmysql JSON列タイプをマップする

コードは次のとおりです。

JsonToMapConverted.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<>();

...

このソリューションは完全に私のために動作します。

6
Heril Muratovic

Json配列内の値が単純な文字列である場合、これを行うことができます。

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

この記事 で説明したように、Hibernateを使用してJSONオブジェクトを永続化するのは非常に簡単です。

これらすべてのタイプを手動で作成する必要はありません。次の依存関係を使用して、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 = JsonStringType.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
}

上記のコードスニペットの2つの点に注意してください。

  • @TypeDefは、新しいカスタムHibernateタイプjsonを定義するために使用され、JsonStringTypeによって処理されます
  • properties属性には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);

Hibernateは次の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/\"" +
    "}"
);

Hibernateが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

GitHub で利用可能なすべてのコード。

2
Vlad Mihalcea

誰にとっても@Jは作れません。王の答えの仕事:

この依存関係を追加してみてください(Hibernate 5.1および5.0の場合、他のバージョンのチェック here

<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でコードをテストします。

2
user1686407

Heril Muratovicの回答 は良いですが、JsonToMapConverterAttributeConverter<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;
        }
    }
}
1
Songle Bao