问题描述
我正在尝试在春季启动时将JSON对象存储在MySQL数据库中.我知道我做错了事,但是我无法弄清楚到底是什么原因,因为我对Spring还很陌生.
I am trying to store a JSON object in MySQL database in spring boot. I know I am doing something wrong but I a can't figure out what it is because I am fairly new to Spring.
我有一个休息端点,在这里我通过HTTP PUT获取以下JSON对象,并且需要将其存储在数据库中,以便用户以后可以通过HTTP GET获取它.
I have a rest endpoint where I get the following JSON object (via HTTP PUT) and I need to store it in database so that the user can fetch it later (via HTTP GET).
{
"A": {
"Name": "Cat",
"Age": "1"
},
"B": {
"Name": "Dog",
"Age": "2"
},
"C": {
"Name": "Horse",
"Age": "1"
}
}
请注意,在上述情况下,对象中的数量 键可能会有所不同,由于该要求,我使用的是在控制器中捕获对象.
Note that in the above case The number of keys in the object may vary, Due to that requirement I am using a HashMap
to catch the object in the controller.
@RequestMapping(method = RequestMethod.POST)
public String addPostCollection(@RequestBody HashMap<String, Animal> hp) {
hp.forEach((x, y) -> {
postRepository.save(hp.get(x));
});
return "OK";
}
如您在方法中所见,
我可以迭代HashMap
并将每个Animal
对象保留在db中.但是我正在寻找一种将整个HashMap
保留在一条记录中的方法.我读了一些书,他们建议我使用@ManyToMany
映射.
As you can see in the method, I can iterate the HashMap
and persist each Animal
object in db. But I am looking for a way to persist the entire HashMap
in a single record. I have did some reading and they suggest me to use a @ManyToMany
mapping.
有人可以指出我以不同的方式坚持HashMap
的方向吗? (或者使用@ManyToMany
是唯一正确的方法吗?)
Can anyone point me in a direction to persist the HashMap
in a different way? (or is using the @ManyToMany
the only and right way to do this?)
推荐答案
正如我在这篇文章,使用Hibernate持久存储JSON对象非常容易.
As I explained in this article, it's very easy to persist JSON objects using Hibernate.
<dependency>
<groupId>com.vladmihalcea</groupId>
<artifactId>hibernate-types-52</artifactId>
<version>${hibernate-types.version}</version>
</dependency>
有关更多信息,请查看休眠类型开源项目.
For more info, check out the hibernate-types open-source project.
现在,解释一下它们是如何工作的.
Now, to explain how it all works.
假设您具有以下实体:
@Entity(name = "Book")
@Table(name = "book")
@TypeDef(
name = "jsonb-node",
typeClass = JsonNodeBinaryType.class
)
public class Book {
@Id
@GeneratedValue
private Long id;
@NaturalId
private String isbn;
@Type( type = "jsonb-node" )
@Column(columnDefinition = "jsonb")
private JsonNode properties;
//Getters and setters omitted for brevity
}
请注意上面的代码片段中的两件事:
Notice two things in the code snippet above:
-
@TypeDef
用于定义新的自定义休眠类型jsonb-node
,由JsonNodeBinaryType
处理 -
properties
属性的列类型为jsonb
,并且已映射为JacksonJsonNode
- the
@TypeDef
is used to define a new custom Hibernate Type,jsonb-node
which is handled by theJsonNodeBinaryType
- the
properties
attribute has ajsonb
column type and it's mapped as a JacksonJsonNode
JsonNodeBinaryType
的实现方式如下:
public class JsonNodeBinaryType
extends AbstractSingleColumnStandardBasicType<JsonNode> {
public JsonNodeBinaryType() {
super(
JsonBinarySqlTypeDescriptor.INSTANCE,
JsonNodeTypeDescriptor.INSTANCE
);
}
public String getName() {
return "jsonb-node";
}
}
JsonBinarySqlTypeDescriptor
看起来如下:
public class JsonBinarySqlTypeDescriptor
extends AbstractJsonSqlTypeDescriptor {
public static final JsonBinarySqlTypeDescriptor INSTANCE =
new JsonBinarySqlTypeDescriptor();
@Override
public <X> ValueBinder<X> getBinder(
final JavaTypeDescriptor<X> javaTypeDescriptor
) {
return new BasicBinder<X>(javaTypeDescriptor, this) {
@Override
protected void doBind(
PreparedStatement st,
X value,
int index,
WrapperOptions options
) throws SQLException {
st.setObject(
index,
javaTypeDescriptor.unwrap(
value,
JsonNode.class,
options
),
getSqlType()
);
}
@Override
protected void doBind(
CallableStatement st,
X value,
String name,
WrapperOptions options
) throws SQLException {
st.setObject(
name,
javaTypeDescriptor.unwrap(
value,
JsonNode.class,
options
),
getSqlType()
);
}
};
}
}
可以在这篇文章.
现在,JsonNodeTypeDescriptor
负责将JsonNode
转换为各种表示形式,在绑定参数或从底层ResultSet
的JSON对象获取参数时,底层JDBC驱动程序可能会使用它们.
Now, the JsonNodeTypeDescriptor
is responsible for transforming the JsonNode
into various representations which might be used by the underlying JDBC Driver during binding parameters or fetching from the JSON object from the underlying ResultSet
.
public class JsonNodeTypeDescriptor
extends AbstractTypeDescriptor<JsonNode> {
public static final JsonNodeTypeDescriptor INSTANCE =
new JsonNodeTypeDescriptor();
public JsonNodeTypeDescriptor() {
super(
JsonNode.class,
new MutableMutabilityPlan<JsonNode>() {
@Override
protected JsonNode deepCopyNotNull(
JsonNode value
) {
return JacksonUtil.clone(value);
}
}
);
}
@Override
public boolean areEqual(JsonNode one, JsonNode another) {
if ( one == another ) {
return true;
}
if ( one == null || another == null ) {
return false;
}
return
JacksonUtil.toJsonNode(
JacksonUtil.toString(one)
).equals(
JacksonUtil.toJsonNode(
JacksonUtil.toString(another)
)
);
}
@Override
public String toString(JsonNode value) {
return JacksonUtil.toString(value);
}
@Override
public JsonNode fromString(String string) {
return JacksonUtil.toJsonNode(string);
}
@SuppressWarnings({ "unchecked" })
@Override
public <X> X unwrap(
JsonNode value,
Class<X> type,
WrapperOptions options
) {
if ( value == null ) {
return null;
}
if ( String.class.isAssignableFrom( type ) ) {
return (X) toString(value);
}
if ( JsonNode.class.isAssignableFrom( type ) ) {
return (X) JacksonUtil.toJsonNode(toString(value));
}
throw unknownUnwrap( type );
}
@Override
public <X> JsonNode wrap(X value, WrapperOptions options) {
if ( value == null ) {
return null;
}
return fromString(value.toString());
}
}
就是这样!
现在,如果您保存实体:
Now, if you save an entity:
Book book = new Book();
book.setIsbn( "978-9730228236" );
book.setProperties(
JacksonUtil.toJsonNode(
"{" +
" \"title\": \"High-Performance Java Persistence\"," +
" \"author\": \"Vlad Mihalcea\"," +
" \"publisher\": \"Amazon\"," +
" \"price\": 44.99" +
"}"
)
);
entityManager.persist( book );
Hibernate将生成以下SQL语句:
Hibernate is going to generate the following SQL statement:
INSERT INTO
book
(
isbn,
properties,
id
)
VALUES
(
'978-9730228236',
'{"title":"High-Performance Java Persistence","author":"Vlad Mihalcea","publisher":"Amazon","price":44.99}',
1
)
您还可以将其重新加载并进行修改:
And you can also load it back and modify it:
Session session = entityManager.unwrap( Session.class );
Book book = session
.bySimpleNaturalId( Book.class )
.load( "978-9730228236" );
LOGGER.info( "Book details: {}", book.getProperties() );
book.setProperties(
JacksonUtil.toJsonNode(
"{" +
" \"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
语句的问题:
Hibernate taking caare of the UPDATE
statement for you:
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
-- Book details: {"price":44.99,"title":"High-Performance Java Persistence","author":"Vlad Mihalcea","publisher":"Amazon"}
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 .
这篇关于使用Hibernate和JPA持久化JSON对象的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!