How to map a PostgreSQL Enum ARRAY to a JPA entity property using 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 OSS project allows you to map JSON, ARRAY, YearMonth
, Month
or database-specific columns (e.g., INET addresses).
In this article, we are going to see how you can map a PostgreSQL Enum ARRAY type to a Java array 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, 5.2, or 5.1), then check out the Hypersistence Utils GitHub repository for more info about the matching dependency for your current Hibernate version.
Domain Model
Let’s assume we have the following sensor_state
PostgreSQL enum in our database schema:
CREATE TYPE sensor_state AS ENUM ( 'ONLINE', 'OFFLINE', 'UNKNOWN' );
Our application needs to store Events in the following database table:
CREATE TABLE event ( id bigint NOT NULL, sensor_names text[], sensor_values integer[], sensor_states sensor_state[], CONSTRAINT event_pkey PRIMARY KEY (id) )
Notice that the sensor_names
, sensor_values
and sensor_states
columns are stored as arrays.
Now, we want to map the event
database table to the following Event
JPA entity:
To map the PostgreSQL array column types to Java arrays, you need a custom Hibernate type since the built-in types don’t support persisting database-specific arrays.
However, thanks to the Hypersistence Utils library, you can easily map the event
table to the following Event
entity.
For Hibernate 6, the mapping will look as follows:
@Entity(name = "Event") @Table(name = "event") public class Event { @Id private Long id; @Type(StringArrayType.class) @Column( name = "sensor_names", columnDefinition = "text[]" ) private String[] sensorNames; @Type(IntArrayType.class) @Column( name = "sensor_values", columnDefinition = "integer[]" ) private int[] sensorValues; @Type( value = EnumArrayType.class, parameters = @Parameter( name = AbstractArrayType.SQL_ARRAY_TYPE, value = "sensor_state" ) ) @Column( name = "sensor_states", columnDefinition = "sensor_state[]" ) private SensorState[] sensorStates; }
And for Hibernate 5, like this:
@Entity(name = "Event") @Table(name = "event") @TypeDef( typeClass = StringArrayType.class, defaultForType = String[].class ) @TypeDef( typeClass = IntArrayType.class, defaultForType = int[].class ) @TypeDef( typeClass = EnumArrayType.class, defaultForType = SensorState[].class, parameters = { @Parameter( name = AbstractArrayType.SQL_ARRAY_TYPE, value = "sensor_state" ) } ) public class Event { @Id private Long id; @Column( name = "sensor_names", columnDefinition = "text[]" ) private String[] sensorNames; @Column( name = "sensor_values", columnDefinition = "integer[]" ) private int[] sensorValues; @Column( name = "sensor_states", columnDefinition = "sensor_state[]" ) private SensorState[] sensorStates; }
Notice the Fluent-style API used by the
Event
entity. While JPA is more strict when it comes to defining setters, Hibernate allows you to define the setters so that you can build the entity using a Fluent-style API. For more details, check out this article.
The @TypeDef
annotation is used to define the mapping between the Java array class types and their associated Hibernate types:
- The Java
String[]
array type is handled by theStringArrayType
. - The Java
int[]
array type is handled by theIntArrayType
- The Java
SensorState[]
is handled by theEnumArrayType
. TheAbstractArrayType.SQL_ARRAY_TYPE
parameter is used to describe the database-specific column type used for storing the Enum.
The SensorState
Java enum is mapped as follows:
public enum SensorState { ONLINE, OFFLINE, UNKNOWN; }
Testing Time
Now, when storing the following Event
entity:
entityManager.persist( new Event() .setId(1L) .setSensorNames( new String[]{ "Temperature", "Pressure" }) .setSensorValues( new int[]{ 12, 756 } ) .setSensorStates( new SensorState[]{ SensorState.ONLINE, SensorState.OFFLINE, SensorState.ONLINE, SensorState.UNKNOWN } ) );
Hibernate executes the following SQL INSERT statement:
Query:[" insert into event ( sensor_names, sensor_states, sensor_values, id ) values ( ?, ?, ?, ? ) "], Params:[( {"Temperature","Pressure"}, {"ONLINE","OFFLINE","ONLINE","UNKNOWN"}, {"12","756"}, 1 )]
And, when we fetch the Event
entity, we can see that all properties are fetched properly
Event event = entityManager.find(Event.class, 1L); assertArrayEquals( new String[]{ "Temperature", "Pressure" }, event.getSensorNames() ); assertArrayEquals( new int[]{ 12, 756 }, event.getSensorValues() ); assertArrayEquals( new SensorState[]{ SensorState.ONLINE, SensorState.OFFLINE, SensorState.ONLINE, SensorState.UNKNOWN }, event.getSensorStates() );
Awesome, right?
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 ARRAY types.
You can map PostgreSQL-specific Enums, nullable Character
, JSON, or even provide your own immutable Hibernate custom Types
.
