How to map a PostgreSQL HStore entity property with JPA and Hibernate

Imagine having a tool that can automatically detect JPA and Hibernate performance issues. Wouldn’t that be just awesome?

Well, Hypersistence Optimizer is that tool! And it works with Spring Boot, Spring Framework, Jakarta EE, Java EE, Quarkus, or Play Framework.

So, enjoy spending your time on the things you love rather than fixing performance issues in your production system on a Saturday night!

Introduction

The Hypersistence Utils open-source project allows you to map a great variety of database types that are not supported natively by Hibernate ORM (e.g. JSON, ARRAY, YearMonth, Month, INET addresses).

In this article, we are going to see how you can map a PostgreSQL HStore type, which allows you to store key/value pairs, to a Java Map entity property when using JPA and Hibernate.

Maven dependency

First of all, you need to set up the following Maven dependency in your project pom.xml configuration file:

<dependency>
    <groupId>io.hypersistence</groupId>
    <artifactId>hypersistence-utils-hibernate-55</artifactId>
    <version>${hypersistence-utils.version}</version>
</dependency>

If you’re using older versions of Hibernate (e.g., 5.4, 5.3, or 5.2), check out the Hypersistence Utils GitHub repository for more info about the matching dependency for your current Hibernate version.

Domain Model

First of all, we need to make sure we have the hstore extension installed in our database:

CREATE EXTENSION IF NOT EXISTS hstore;

Our application needs to store Book entities in the following database table:

PostgreSQL HStore Book database table

Notice that the properties column type is hstore which is not supported by Hibernate ORM.

To map the PostgreSQL hstore column types to a Java Map, you need a custom Hibernate type since the built-in types don’t support persisting database-specific types. Luckily, thanks to the Hypersistence Utils library you can easily map the book table to the following Book entity.

For Hibernate 6, the mapping will look as follows:

@Entity(name = "Book")
@Table(name = "book")
public class Book {

    @Id
    @GeneratedValue
    private Long id;

    @NaturalId
    @Column(length = 15)
    private String isbn;

    @Type(PostgreSQLHStoreType.class)
    @Column(columnDefinition = "hstore")
    private Map<String, String> properties = new HashMap<>();
}

And for Hibernate 5, like this:

@Entity(name = "Book")
@Table(name = "book")
@TypeDef(name = "hstore", typeClass = PostgreSQLHStoreType.class)
public class Book {

    @Id
    @GeneratedValue
    private Long id;

    @NaturalId
    @Column(length = 15)
    private String isbn;

    @Type(type = "hstore")
    @Column(columnDefinition = "hstore")
    private Map<String, String> properties = new HashMap<>();
}

The @TypeDef annotation is used to register the PostgreSQLHStoreType custom Hibernate Type with the hstore name. Afterward, the properties entity attribute uses the @Type annotation to specify that the hstore Hibernate Type should be used to handle this entity attribute.

Testing Time

Now, when storing the following Book entity:

Book book = new Book();

book.setIsbn("978-9730228236");
book.getProperties().put("title", "High-Performance Java Persistence");
book.getProperties().put("author", "Vlad Mihalcea");
book.getProperties().put("publisher", "Amazon");
book.getProperties().put("price", "$44.95");

entityManager.persist(book);

Hibernate executes the following SQL INSERT statement:

INSERT INTO book (isbn, properties, id)
VALUES (
    '978-9730228236', 
    '"author"=>"Vlad Mihalcea", 
     "price"=>"$44.95", "publisher"=>"Amazon", 
     "title"=>"High-Performance Java Persistence"', 
    1
)

And, when we fetch the Book entity, we can see that all properties are fetched properly.

Book book = entityManager
.unwrap(Session.class)
.bySimpleNaturalId(Book.class)
.load("978-9730228236");

assertEquals(
    "High-Performance Java Persistence", 
    book.getProperties().get("title")
);
assertEquals(
    "Vlad Mihalcea", 
    book.getProperties().get("author")
);

Awesome, right?

I'm running an online workshop on the 20-21 and 23-24 of November about High-Performance Java Persistence.

If you enjoyed this article, I bet you are going to love my Book and Video Courses as well.

Conclusion

The Hypersistence Utils project supports more than hstore PostgreSQL type. You can map PostgreSQL-specific Enums, nullable Character, JSON, or even provide your own immutable Hibernate custom Types.

Transactions and Concurrency Control eBook

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.