Search code examples
javajsonspringhibernatejpa

Persisting a JSON Object using Hibernate and JPA


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.

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

    }

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.

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


Solution

  • Maven dependency

    The first thing you need to do is to set up the following Hibernate Types Maven dependency in your project pom.xml configuration file:

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

    Domain model

    Let's assume you have the following entity:

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

    Notice the @TypeDef is used to instruct Hibernate to map the JsonNode object using the JsonType offered by the Hibernate Types project.

    Testing time

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

    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