We have a postgres DB with postgres enums. We are starting to build JPA into our application. We also have Java enums which mirror the postgres enums. Now the big question is how to get JPA to understand Java enums on one side and postgres enums on the other? The Java side should be fairly easy but I'm not sure how to do the postgres side.
-
1we are now moving our system to JPA too, but we have java enums mapped to varchar columns still had no problems with Hibernate – miceuz May 12 '09 at 10:05
5 Answers
I've actually been using a simpler way than the one with PGObject and Converters. Since in Postgres enums are converted quite naturally to-from text you just need to let it do what it does best. I'll borrow Arjan's example of moods, if he doesn't mind:
The enum type in Postgres:
CREATE TYPE mood AS ENUM ('sad', 'ok', 'happy');
The class and enum in Java:
public @Entity class Person {
public static enum Mood {sad, ok, happy};
@Enumerated(EnumType.STRING)
Mood mood;
}
That @Enumerated tag says that serialization/deserialization of the enum should be done in text. Without it, it uses int, which is more troublesome than anything.
At this point you have two options. You either:
Add stringtype=unspecified to the connection string, as explained in JDBC connection parameters.This lets Postgres guess the right-side type and convert everything adequately, since it receives something like 'enum = unknown', which is an expression it already knows what to do with (feed the ? value to the left-hand type deserialiser). This is the preferred option, as it should work for all simple UDTs such as enums in one go.
jdbc:postgresql://localhost:5432/dbname?stringtype=unspecified
Or:
Create an implicit conversion from varchar to the enum in the database. So in this second case the database receives some assignment or comparison like 'enum = varchar' and it finds a rule in its internal catalog saying that it can pass the right-hand value through the serialization function of varchar followed by the deserialization function of the enum. That's more steps than should be needed; and having too many implicit casts in the catalog can cause arbitrary queries to have ambiguous interpretations, so use it sparingly. The cast creation is:
CREATE CAST (CHARACTER VARYING as mood) WITH INOUT AS IMPLICIT;
Should work with just that.

- 848
- 11
- 17

- 941
- 1
- 8
- 3
-
11
-
1The CREATE CAST solution does not seem to work when the enum is used as an argument of a JPA Repository. E.g. ```Entity findByMyEnum(MyEnum myEnum)``` – Daniele Repici May 01 '20 at 10:18
-
1With auto-ddl this still gets created as integer/character varying. – Adrian Elder Jul 31 '20 at 19:20
This involves making multiple mappings.
First, a Postgres enum is returned by the JDBC driver as an instance of type PGObject. The type property of this has the name of your postgres enum, and the value property its value. (The ordinal is not stored however, so technically it's not an enum anymore and possibly completely useless because of this)
Anyway, if you have a definition like this in Postgres:
CREATE TYPE mood AS ENUM ('sad', 'ok', 'happy');
Then the resultset will contain a PGObject with type "mood" and value "happy" for a column having this enum type and a row with the value 'happy'.
Next thing to do is writing some interceptor code that sits between the spot where JPA reads from the raw resultset and sets the value on your entity. E.g. suppose you had the following entity in Java:
public @Entity class Person {
public static enum Mood {sad, ok, happy}
@Id Long ID;
Mood mood;
}
Unfortunately, JPA does not offer an easy interception point where you can do the conversion from PGObject to the Java enum Mood. Most JPA vendors however have some proprietary support for this. Hibernate for instance has the TypeDef and Type annotations for this (from Hibernate-annotations.jar).
@TypeDef(name="myEnumConverter", typeClass=MyEnumConverter.class)
public @Entity class Person {
public static enum Mood {sad, ok, happy}
@Id Long ID;
@Type(type="myEnumConverter") Mood mood;
These allow you to supply an instance of UserType (from Hibernate-core.jar) that does the actual conversion:
public class MyEnumConverter implements UserType {
private static final int[] SQL_TYPES = new int[]{Types.OTHER};
public Object nullSafeGet(ResultSet arg0, String[] arg1, Object arg2) throws HibernateException, SQLException {
Object pgObject = arg0.getObject(X); // X is the column containing the enum
try {
Method valueMethod = pgObject.getClass().getMethod("getValue");
String value = (String)valueMethod.invoke(pgObject);
return Mood.valueOf(value);
}
catch (Exception e) {
e.printStackTrace();
}
return null;
}
public int[] sqlTypes() {
return SQL_TYPES;
}
// Rest of methods omitted
}
This is not a complete working solution, but just a quick pointer in hopefully the right direction.

- 37,782
- 12
- 108
- 140
-
3>we are now moving our system to JPA too, but we have java enums mapped to varchar columns still had no problems with Hibernate varchars look nice in the DB, but you miss 2 things compared to real enums in the DB: * Type safety, unless you want to add a check constraint to each and every column or use a FK to a separate table that holds all varchar values as a PK. * Speed. string lookups and compares are slower, even when indexed. So its a tradeoff anyway. Either you use varchars, and except the above restrictions, or you use native PG enums and accept the non-automatic mapping. – Arjan Tijms Dec 26 '09 at 11:43
-
p.s. There is a third way, mapping by ordinal, but that's not really recommended; making the code hard to read a brittle; if you or remove a single constant from your Java enum, all ordinals that are already in the DB may become invalid! – Arjan Tijms Dec 26 '09 at 11:45
-
1Is there a reason to use reflection instead of casting to [PgObject](https://jdbc.postgresql.org/documentation/publicapi/org/postgresql/util/PGobject.html)? – Martin Feb 26 '15 at 19:24
-
1@Martin to prevent the compile time dependency. Not every project has the driver on its classpath, e.g. often the driver is in some /lib folder of the server. If this is no issue for you, you don't have to use reflection. – Arjan Tijms Feb 27 '15 at 11:14
-
Although it doesn't use the ordinal, It doesn't store the entire string that can go up to 63 bytes fixed. The enum value is of size 4 bytes. Look in implementation details at the end in the link(https://www.postgresql.org/docs/current/datatype-enum.html) – Darshan K N Feb 23 '21 at 11:17
I filed a bug report with a patch included for Hibernate: HHH-5188
The patch works for me to read a PostgreSQL enum into a Java enum using JPA.

- 6,853
- 5
- 55
- 76

- 1,529
- 13
- 20
-
4This seems to be broken even worse in 4.3.5. Now I can't even persist the enum to Postgres because Hibernate seems to insist on writing it as text or integer. – Jannik Jochem Dec 11 '14 at 12:35
it's work for me
@org.hibernate.annotations.TypeDef(name = "enum_type", typeClass = PostgreSQLEnumType.class)
public class SomeEntity {
...
@Enumerated(EnumType.STRING)
@Type(type = "enum_type")
private AdType name;
}
and
import org.hibernate.HibernateException;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.type.EnumType;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.Types;
public class PostgreSQLEnumType extends EnumType {
@Override
public void nullSafeSet(PreparedStatement ps, Object obj, int index,
SharedSessionContractImplementor session) throws HibernateException, SQLException {
if (obj == null) {
ps.setNull(index, Types.OTHER);
} else {
ps.setObject(index, obj.toString(), Types.OTHER);
}
}
}

- 1,949
- 22
- 16
I tried the above suggestions without success
The only thing I could get working was making the POSTGRES def a TEXT type, and using the @Enumerated(STRING) annotation in the entity.
Ex (In Kotlin):
CREATE TABLE IF NOT EXISTS some_example_enum_table
(
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
some_enum TEXT NOT NULL,
);
Example Kotlin Class:
enum class SomeEnum { HELLO, WORLD }
@Entity(name = "some_example_enum_table")
data class EnumExampleEntity(
@Id
@GeneratedValue
var id: UUID? = null,
@Enumerated(EnumType.STRING)
var some_enum: SomeEnum = SomeEnum.HELLO,
)
Then my JPA lookups actually worked:
@Repository
interface EnumExampleRepository : JpaRepository<EnumExampleEntity, UUID> {
fun countBySomeEnumNot(status: SomeEnum): Int
}

- 3,665
- 34
- 52