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;
私はこの方法を好む:
コードは次のとおりです。
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<>();
...
このソリューションは完全に私のために動作します。
Json配列内の値が単純な文字列である場合、これを行うことができます。
@Type( type = "json" )
@Column( columnDefinition = "json" )
private String[] jsonValue;
この記事 で説明したように、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 で利用可能なすべてのコード。
誰にとっても@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でコードをテストします。
Heril Muratovicの回答 は良いですが、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;
}
}
}