Search This Blog

Tuesday, 15 October 2013

Saving an Enum in JPA

Consider the below entity:
@Entity
@Table(name = "STUDENT")
public class Student {
 @Id
 @GeneratedValue(strategy = GenerationType.IDENTITY)
 private Long id;
 @Column(name = "NAME")
 private String name;
 private Rating rating;
//setter getters
}
where Rating is a simple Enum
public enum Rating {
 MOTIVATED,NEEDS_PUSH,MUST_MEET
}
I tried to save an object of Student in the the database:
public static void testCreate() {
 EntityManager entityManager = emFactory.createEntityManager();
 EntityTransaction transaction = entityManager.getTransaction();
 Student student = new Student();
 student.setName("Robin");
 student.setRating(Rating.MUST_MEET);
 transaction.begin();
 entityManager.persist(student);
 transaction.commit();
}
If we ran this code the record would be ......................... SAVED
This is actually pretty cool. I actually thought this might be some easy facility provided by Hibernate, but the JPA docs indicate differently. JPA will automatically save Enum properties to the database.
I ran the code with table creation enabled. This is what was created in PostGRE SQL.
3015 [main] DEBUG org.hibernate.tool.hbm2ddl.SchemaExport  - 
    create table firstOne.STUDENT (
        id  bigserial not null,
        NAME varchar(255),
        rating int4,
        primary key (id)
    )
JPA requires that the ORM map any enum present in the entity with the value of the ordinal property. Since the value we used (MUST_MEET) occurred as the third value in the class, the record in the table held the value 2. (Ordinal for Enums are 0 based)
There is a problem with this approach though. If we accidentally modified the sequence of the constants in the java file, it would make a mess of the records in the database.
The alternative is to use the enum values instead of the ordinals.
@Entity
@Table(name = "ADRESSED_USER")
public class User {
 //....
 @Enumerated(EnumType.STRING)
 private Rating rating;
// remaining code
}
The table now generated is:
3797 [main] DEBUG org.hibernate.tool.hbm2ddl.SchemaExport  - 
    create table firstOne.STUDENT (
        id  bigserial not null,
        NAME varchar(255),
        rating varchar(255),
        primary key (id)
    )
The rating column is now a string column. If we look at the insert SQL now:
insert 
    into
        firstOne.STUDENT
        (NAME, rating) 
    values
        (?, ?)
What if we need to get all records of a particular rating ?
public static void findMustMeet() {
 EntityManager entityManager = emFactory.createEntityManager();
 final Query query = entityManager
  .createQuery("SELECT student FROM Student as student " +
   "WHERE student.rating = :rating");
 query.setParameter("rating", Rating.MUST_MEET);
 System.out.println(((Student)query.getSingleResult()).getName());
}
The code executes a JPA Query to return a record with a RATING of MUST_MEET. The logs indicate the same:
5328 [main] DEBUG org.hibernate.engine.query.HQLQueryPlan  - HQL param location 
recognition took 31 mills (SELECT student FROM Student as student WHERE student.
rating = :rating)
...
5375 [main] DEBUG org.hibernate.SQL  - 
    select
        student0_.id as id0_,
        student0_.NAME as NAME0_,
        student0_.rating as rating0_ 
    from
        firstOne.STUDENT student0_ 
    where
        student0_.rating=? limit ?
...
5375 [main] DEBUG org.hibernate.type.EnumType  - Binding 'MUST_MEET' to paramete
r: 1

1 comment: