Introduction
The open-source hibernate-types
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 type to a Java array 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>com.vladmihalcea</groupId> <artifactId>hibernate-types-52</artifactId> <version>2.3.5</version> </dependency>
If you’re using older versions of Hibernate, check out the hibernate-types
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. 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 hibernate-types
library you can easily map the event
table to the following Event
entity:
@Entity(name = "Event") @Table(name = "event") @TypeDefs({ @TypeDef( typeClass = StringArrayType.class, defaultForType = String[].class ), @TypeDef( typeClass = IntArrayType.class, defaultForType = int[].class ), @TypeDef( typeClass = EnumArrayType.class, defaultForType = SensorState[].class, parameters = { @Parameter( name = EnumArrayType.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; public Long getId() { return id; } public Event setId( Long id) { this.id = id; return this; } public String[] getSensorNames() { return sensorNames; } public Event setSensorNames( String[] sensorNames) { this.sensorNames = sensorNames; return this; } public int[] getSensorValues() { return sensorValues; } public Event setSensorValues( int[] sensorValues) { this.sensorValues = sensorValues; return this; } public SensorState[] getSensorStates() { return sensorStates; } public Event setSensorStates( SensorState[] sensorStates) { this.sensorStates = sensorStates; return this; } }
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
. TheEnumArrayType.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() );
Cool, right?
If you enjoyed this article, I bet you are going to love my Book and Video Courses as well.
Conclusion
The hibernate-types
project supports more than ARRAY types. You can map PostgreSQL-specific Enums, nullable Character
, JSON, or even provide your own immutable Hibernate custom Types
.
For more details about the hibernate-types
project, check out this article.
Author: Vlad Mihalcea
I work as a Hibernate Developer Advocate, I wrote the High-Performance Java Persistence book and video course series. I speak at conferences and I created several open-source projects, like FlexyPool, hibernate-types, and db-util.