【问题标题】:Persisting a JSON Object using Hibernate and JPA使用 Hibernate 和 JPA 持久化 JSON 对象
【发布时间】:2017-04-09 17:12:33
【问题描述】:

我正在尝试在 Spring Boot 中将 JSON 对象存储在 MySQL 数据库中。我知道我做错了什么,但我无法弄清楚它是什么,因为我对 Spring 还很陌生。

我有一个休息端点,我在其中获取以下 JSON 对象(通过 HTTP PUT),我需要将它存储在数据库中,以便用户稍后可以获取它(通过 HTTP GET)。

{
  "A": {
    "Name": "Cat",
    "Age": "1"
  },
  "B": {
    "Name": "Dog",
    "Age": "2"
  },
  "C": {
    "Name": "Horse",
    "Age": "1"
  }
}

请注意,在上述情况下,对象中的 number of keys 可能会有所不同,由于该要求,我使用的是 @987654322 @ 在控制器中捕获对象。

@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 映射。

谁能指出我以不同方式坚持HashMap 的方向? (或者是使用@ManyToMany 唯一且正确的方法吗?)

【问题讨论】:

  • "整个 HashMap 在一条记录中" ??还是单个数据库事务中的整个哈希图?
  • 我的意思是单条记录。

标签: java json spring hibernate jpa


【解决方案1】:

Maven 依赖

你需要做的第一件事是在你的项目pom.xml配置文件中设置如下Hibernate TypesMaven依赖:

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

领域模型

假设您有以下实体:

@Entity(name = "Book")
@Table(name = "book")
@TypeDef(
    typeClass = JsonType.class, 
    defaultForType = JsonNode.class
)
public class Book {

    @Id
    @GeneratedValue
    private Long id;

    @NaturalId
    private String isbn;

    @Column(columnDefinition = "jsonb")
    private JsonNode properties;

    //Getters and setters omitted for brevity
}

请注意,@TypeDef 用于指示 Hibernate 使用 Hibernate Types 项目提供的 JsonType 映射 JsonNode 对象。

测试时间

现在,如果你保存一个实体:

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 语句:

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

你也可以加载回来修改:

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/\"" +
        "}"
    )
);

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

【讨论】:

  • 感谢您的提示!但是,您可以使用不知道任何 JSON 类型的 H2 数据库来完成这项工作吗?我使用 Spring Boot 和 H2 进行测试。也许以某种方式覆盖 H2 方言以将 JSON 类型视为文本?但不知道如何......谢谢
  • @VladMihalcea 我的集成测试确实使用 MySQL 数据库,但使用 MySQL 进行 JPA 单元测试会很痛苦和荒谬。
【解决方案2】:

您可以使用 FasterXML(或类似的)将 Json 解析为实际对象(您需要定义类)并使用 Json.toJson(yourObj).toString() 检索 Json 字符串。它还简化了对对象的处理,因为您的数据类也可能具有功能。

【讨论】:

    【解决方案3】:

    您的 JSON 结构良好,因此通常无需将整个地图保存在一条记录中。您将无法使用 Hibernate/JPA 查询功能等等。

    如果您真的想将整个地图保存在一条记录中,您可以将地图保存在其字符串表示中,并且如前所述,使用 JSON 解析器(如 Jackson)来重建您的 HashMap

    @Entity
    public class Animals {
    
      private String animalsString;
    
      public void setAnimalsString(String val) {
        this.animalsString = val;
      }
    
      public String getAnimalsString() {
        return this.animalsMap;
      }
    
      public HashMap<String, Animal> getAnimalsMap() {
        ObjectMapper mapper = new ObjectMapper();
        TypeReference<HashMap<String,Animal>> typeRef = new TypeReference<HashMap<String,Animal>>() {};
        return mapper.readValue(animalsString, typeRef); 
      }
    
    }
    

    你的动物类:

    public class Animal {
    
      private String name;
      private int age;
    
      /* getter and setter */
      /* ... */
    }
    

    您可以将控制器方法更改为

    @RequestMapping(method = RequestMethod.POST)
    public String addPostCollection(@RequestBody String hp) {
      Animals animals = new Animals();
      animals.setAnimalsString(hp);
      animalsRepository.save(hp);
      return "OK";
    }
    

    【讨论】:

    • 我认为这对我来说是最好的选择。将对象保存为字符串并将其映射到地图。
    【解决方案4】:

    一种动物就是一种记录。您正在保存更多记录,而不是一条记录。您可以在一个事务中提交更多记录。 见:How to persist a lot of entities (JPA)

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-11-12
      • 1970-01-01
      • 1970-01-01
      • 2017-10-10
      • 2015-05-26
      • 2011-01-19
      • 1970-01-01
      相关资源
      最近更新 更多