Powered By Blogger
Showing posts with label enum. Show all posts
Showing posts with label enum. Show all posts

Sunday, August 19, 2012

Hibernate enum mapping - hmb

The following example shows how to map enums using hbm files. Media is the enum which is mapped in this means. It has a 'description' attribute which will be saved in the database. The methods 'getDescription()' and 'getMedia(String)' are used to retrieve the description and the media of 'Media' respectively. Notice the latter is static and returns corresponding enum type of the value passed into it.

==============================================================
package com.shyarmal.hibernate.example.model;

public enum Media {

    PRINTED("PRINTED"), ELECTRONIC("ELECTRONIC"), UNKNOWN("UNKNOWN");
    private String description;

    Media(String description) {
        this.description = description;
    }

    public String getDescription() {
        return description;
    }

    public static Media getMedia(String value) {
        if ("PRINTED".equals(value)) {
           return PRINTED;
        } else if ("ELECTRONIC".equals(value)) {
           return ELECTRONIC;
        } else {
            return UNKNOWN;
        }
    }
}
==============================================================

A custom enum user type is defined below, implementing the interfaces UserType and ParameterizedType. Parameters of the typedef element of the hbm (found below) are used in 'setParameterValues(Properties)' to initialize the enum type and it's methods. The methods  nullSafeGet(ResultSet rs, String[] names, Object owner)and nullSafeGet(ResultSet rs, String[] names, Object owner) are used to get and set the enum from and to the database.

==============================================================

package com.shyarmal.hibernate.example.model;

import org.hibernate.Hibernate;
import org.hibernate.HibernateException;
import org.hibernate.type.NullableType;
import org.hibernate.type.TypeFactory;
import org.hibernate.usertype.ParameterizedType;
import org.hibernate.usertype.UserType;
import java.io.Serializable;
import java.lang.reflect.Method;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Properties;

public class EnumUserType implements UserType, ParameterizedType {

    private static final String IDENTIFIER_METHOD = "name";
    private static final String VALUE_OF_METHOD = "valueOf";
    private Class<? extends Enum> enumClass;
    private Class<?> identifierType;
    private Method identifierMethod;
    private Method valueOfMethod;
    private NullableType type;
    private int[] sqlTypes;

    public void setParameterValues(Properties parameters) {
        String enumClassName = parameters.getProperty("enumClass");
        try {
            enumClass = Class.forName(enumClassName).asSubclass(Enum.class);
        } catch (ClassNotFoundException cfne) {
            throw new HibernateException("Enum class not found", cfne);
        }

        String identifierMethodName = parameters.getProperty("identifierMethod", IDENTIFIER_METHOD);
        try {
            identifierMethod = enumClass.getMethod(identifierMethodName, new Class[0]);
            identifierType = identifierMethod.getReturnType();
        } catch (Exception e) {
            throw new HibernateException("Failed to obtain identifier method", e);
        }

        type = (NullableType) TypeFactory.basic(identifierType.getName());
        if (type == null)
            throw new HibernateException("Unsupported identifier type " + identifierType.getName());
        sqlTypes = new int[]{Hibernate.STRING.sqlType()};
        String valueOfMethodName = parameters.getProperty("valueOfMethod", VALUE_OF_METHOD);
        try {
            valueOfMethod = enumClass.getMethod(valueOfMethodName, new Class[]{identifierType});
        } catch (Exception e) {
            throw new HibernateException("Failed to obtain valueOf method", e);
        }
    }

    public Class returnedClass() {
        return enumClass;
    }

    public Object nullSafeGet(ResultSet rs, String[] names, Object owner) throws HibernateException, SQLException {
        Object identifier = type.get(rs, names[0]);
        if (rs.wasNull()) {
            return null;
        }

        try {
            return valueOfMethod.invoke(enumClass, new Object[]{identifier});
        } catch (Exception e) {
            throw new HibernateException("Error in valueOf method '" + valueOfMethod.getName()
                    + "' of " + "enum class '" + enumClass + "'", e);
        }
    }

    public void nullSafeSet(PreparedStatement st, Object value, int index) throws HibernateException, SQLException {
        try {
            if (value == null) {
                st.setNull(index, type.sqlType());
            } else {
                Object identifier = identifierMethod.invoke(value, new Object[0]);
                type.set(st, identifier, index);
            }
        } catch (Exception e) {
            throw new HibernateException("Error in identifierMethod '" + identifierMethod.getName()
                    + "' of " + "enum class '" + enumClass + "'", e);
        }
    }

    public int[] sqlTypes() {
        return sqlTypes;
    }

    public Object assemble(Serializable cached, Object owner) throws HibernateException {
        return cached;
    }

    public Object deepCopy(Object value) throws HibernateException {
        return value;
    }

    public Serializable disassemble(Object value) throws HibernateException {
        return (Serializable) value;
    }

    public boolean equals(Object x, Object y) throws HibernateException {
        return x == y;
    }

    public int hashCode(Object x) throws HibernateException {
        return x.hashCode();
    }

    public boolean isMutable() {
        return false;
    }

    public Object replace(Object original, Object target, Object owner) throws HibernateException {
        return original;
    }
}

==============================================================

I have posted the hbm file below. Only the configurations relevant to the type mapping is present. A type should be defined first, which is done with the 'typedef' element. So a type, 'media' is defined providing the class above, EnumUserType. Parameters 'enumClass', 'identifierMethod' and 'valueOfMethod' are defined. The parameter 'enumClass' is our enum, Media. Other two parameters, identifierMethod and valueOfMethod are the methods getDescription() and getMedia(String) respectively.

==============================================================

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
        "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
        "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">

<hibernate-mapping package="com.shyarmal.hibernate.example.model">

    <typedef name="media" class="com.shyarmal.hibernate.example.model.EnumUserType">
        <param name="enumClass">com.shyarmal.hibernate.example.model.Media</param>
        <param name="identifierMethod">getDescription</param>
        <param name="valueOfMethod">getMedia</param>
    </typedef>

    <class name="Xxxxx" table="XXXX_XXX">
        <!--
            ---- Other property definitions. ----
        -->           
        <property name="media" column="MEDIA" type="media"/>
    </class>

</hibernate-mapping>

==============================================================

After defining a type as described above, a property should be defined for the class (here it's Xxxxx), which should have an attribute called media of type 'Media' (our enum). So '<property name="media" column="MEDIA" type="media"/>' says that the property 'media' of the class Xxxxx of type 'media' (our defined type using typedef) will be mapped to 'MEDIA' column of 'XXXX_XXX' table in the database.


reference: using-enum-hibernate

thanks,
Shyarmal.

Saturday, August 27, 2011

Hibernate annotations with enums

    How enums can be dealt using annotations is described here. In the example there are two enums, 'Status' and 'Priority', which are instances of the class, 'Batch'. 'Batch' is a domain class.

===================================================================

package com.shyarmal.hibernate;

public enum Status {

    STARTED, SUSPENDED, FINISHED, ABANDON
}


>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

package com.shyarmal.hibernate;

public enum Priority {

    HIGH, MEDIUM, LOW
}
===================================================================

   'Batch' is annotated as an entity and it's instances will be saved to a table called 'batch' in the database. @Enumerated annotation is used to mark a filed as an enum. Here the EnumType of 'Priority' is ORDINAL and hence an integer will be saved in the database which corresponds to it's actual value. Integers will be assigned to the enum values in the ascending order, starting with 0. As the EnumType of 'Status' is STRING, the values of it will be saved as varchar in the database.
===================================================================

package com.shyarmal.hibernate;

import javax.persistence.Entity;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table(name = "batch")
public class Batch {

    private Long id;
    private Status status;
    private Priority priority;

    @Id
    @GeneratedValue
    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    @Enumerated(value = EnumType.STRING)
    public Status getStatus() {
        return status;
    }

    public void setStatus(Status status) {
        this.status = status;
    }

    @Enumerated(value = EnumType.ORDINAL)
    public Priority getPriority() {
        return priority;
    }

    public void setPriority(Priority priority) {
        this.priority = priority;
    }

    @Override
    public String toString() {
        return String.format("batch: priority [%s], status [%s]", priority.name(), status.name());
    }

}

===================================================================

Following are some CRUD functions performed on 'Batch'.
===================================================================
// saving a batch
private static void save() {
        Transaction transaction = null;
        try {
            Session session = HibernateUtil.getSessionFactory().getCurrentSession();
            transaction = session.beginTransaction();
            Batch batch = new Batch();
            batch.setPriority(Priority.HIGH);
            batch.setStatus(Status.STARTED);
            session.save(batch);
            transaction.commit();
        } catch(Exception e) {
            transaction.rollback();
            e.printStackTrace();
        }
    }
   
// find one batch by the id.
private static void find() {
    Transaction transaction = null;
     try {
         Session session = HibernateUtil.getSessionFactory().getCurrentSession();
         transaction = session.beginTransaction();
         Batch batch = (Batch) session.get(Batch.class, new Long(1)); // batch with the id 1.
         transaction.commit();
     } catch(Exception e) {
         transaction.rollback();
         e.printStackTrace();
     }
}
   
// editing a batch 
private static void edit() {
     Transaction transaction = null;
      try {
         Session session = HibernateUtil.getSessionFactory().getCurrentSession();
         transaction = session.beginTransaction();
         Batch batch = (Batch) session.get(Batch.class, new Long(1));
         batch.setPriority(Priority.LOW);
         batch.setStatus(Status.SUSPENDED);
         transaction.commit();
      } catch(Exception e) {
         transaction.rollback();
         e.printStackTrace();
      }
}
   
// deleting a batch
private static void delete() {
   Transaction transaction = null;
    try {
        Session session = HibernateUtil.getSessionFactory().getCurrentSession();
        transaction = session.beginTransaction();
        session.delete(session.get(Batch.class, new Long(1)));
        transaction.commit();
    } catch(Exception e) {
        transaction.rollback();
        e.printStackTrace();
     }
}

===================================================================

Hibernate configuration file used is below. Notice the mapping, '<mapping class="com.shyarmal.hibernate.Batch"/>'.
===================================================================
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration PUBLIC
          "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
          "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
    <session-factory>
        <property name="connection.url">jdbc:mysql://localhost/test</property>
        <property name="connection.username">danuka</property>
        <property name="connection.password">123</property>
        <property name="connection.driver_class">com.mysql.jdbc.Driver</property>
        <property name="dialect">org.hibernate.dialect.MySQLDialect</property>
        <property name="show_sql">true</property>
        <property name="format_sql">true</property>
        <property name="hbm2ddl.auto">create</property>
        <property name="connection.pool_size">1</property>
        <property name="current_session_context_class">thread</property>
        <mapping class="com.shyarmal.hibernate.Batch"/> 
    </session-factory>
</hibernate-configuration>

===================================================================
A sample record in the database.
mysql> select * from batch;
+----+----------+-------------+
| id | priority | status        |
+----+----------+-------------+
|  1 |        0 | FINISHED   |
+----+----------+-------------+
1 row in set (0.00 sec)


thanks,
Shyarmal.

Saturday, July 9, 2011

Injecting enums to a map - spring

      Assume an enum class MyEnum is to be used as keys of a java.util.Map and injected in the spring configuration file. This can be done as follows;

package src.shyarmal.eg;

public enum MyEnum {
          A, B, C, D
}

The above enum can be injected to a Map in spring;

<util:map id="myMap" key-type="src.shyarmal.eg.MyEnum">
      <entry key="A" value="value a" />
      <entry key="B" value="value b" />
      <entry key="C" value="value c" />
      <entry key="D" value="value d" />

</util:map>

This map can be injected to a class.

package src.shyarmal.eg;

public class MyClass {

     private Map<MyEnum, String> myMap;

     public void doIt() {
          //do something  ....  
    }
  
    public void setMyMap(Map<MyEnum, String> myMap) {
         this.myMap = myMap;    
   }
}

Spring configuration of MyClass.

<bean id="myClass" class="src.shyarmal.eg.MyClass">
     <property name="myMap" ref="myMap">
</bean>


thanks,
Shyarmal