Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to force Hibernate to return dates as java.util.Date instead of Timestamp?

Situation:

I have a persistable class with variable of java.util.Date type:

import java.util.Date;  @Entity @Table(name = "prd_period") @Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE) public class Period extends ManagedEntity implements Interval {     @Column(name = "startdate_", nullable = false)    private Date startDate;  } 

Corresponding table in DB:

CREATE TABLE 'prd_period' (   'id_' bigint(20) NOT NULL AUTO_INCREMENT,  ...   'startdate_' datetime NOT NULL ) 

Then I save my Period object to DB:

Period p = new Period(); Date d = new Date(); p.setStartDate(); myDao.save(p); 

After then if I'm trying to extract my object from DB, it is returned with variable startDate of Timestamp type - and all the places where I'm trying to use equals(...) are returning false.

Question: are there any means to force Hibernate to return dates as object of java.util.Date type instead of Timestamp without explicit modification of every such variable (e.g it must be able just work, without explicit modification of existed variables of java.util.Date type)?

NOTE:

I found number of explicit solutions, where annotations are used or setter is modified - but I have many classes with Date-variables - so I need some centralized solution and all that described below is not good enough:

  1. Using annotation @Type: - java.sql.Date will be returned

    @Column @Type(type="date") private Date startDate; 
  2. Using annotation @Temporal(TemporalType.DATE) - java.sql.Date will be returned

    @Temporal(TemporalType.DATE) @Column(name=”CREATION_DATE”) private Date startDate; 
  3. By modifying setter (deep copy) - java.util.Date will be returned

    public void setStartDate(Date startDate) {     if (startDate != null) {         this.startDate = new Date(startDate.getTime());     } else {         this.startDate = null;     } } 
  4. By creation of my own type: - java.util.Date will be returned

Details are given here: http://blogs.sourceallies.com/2012/02/hibernate-date-vs-timestamp/

like image 785
dim1902 Avatar asked Mar 02 '12 13:03

dim1902


People also ask

Does Java Util date have timestamp?

util. Date that allows the JDBC API to identify this as an SQL TIMESTAMP value. It adds the ability to hold the SQL TIMESTAMP fractional seconds value, by allowing the specification of fractional seconds to a precision of nanoseconds.

Which annotation is used to force Hibernate to store years months and days?

You can define the preferred mapping with the @Temporal annotation. As you can see in the following code snippet, it takes a TemporalType enum as a value. The enum allows you to select the SQL type (DATE, TIME or TIMESTAMP) which you want to use.

What is @temporal TemporalType timestamp?

@Temporal is a JPA annotation that converts back and forth between timestamp and java. util. Date . It also converts time-stamp into time. For example, in the snippet below, @Temporal(TemporalType.

What is @temporal in hibernate?

The @Temporal annotation has the single parameter value of type TemporalType. It can be either DATE, TIME or TIMESTAMP, depending on the underlying SQL type that we want to use for the mapping.


2 Answers

A simple alternative to using a custom UserType is to construct a new java.util.Date in the setter for the date property in your persisted bean, eg:

import java.util.Date; import javax.persistence.Entity; import javax.persistence.Column;  @Entity public class Purchase {      private Date date;      @Column     public Date getDate() {         return this.date;     }      public void setDate(Date date) {         // force java.sql.Timestamp to be set as a java.util.Date         this.date = new Date(date.getTime());     }  } 
like image 96
Nathan Avatar answered Oct 18 '22 04:10

Nathan


So, I spent some time with this issue and found a solution. It is not pretty one, but at least a start point - maybe someone will supplement this with some useful comments.

Some info about mapping that I found in process:

  • Class that contains basic mapping of Hibernate types to property types is org.hibernate.type.TypeFactory. All this mappings are stored in unmodifiable map

    private static final Map BASIC_TYPES; ... basics.put( java.util.Date.class.getName(), Hibernate.TIMESTAMP ); ... BASIC_TYPES = Collections.unmodifiableMap( basics ); 

As you can see with java.util.Date type assosited with Hibernate type org.hibernate.type.TimestampType

  • Next interesting moment - creation of Hibernate org.hibernate.cfg.Configuration - object that contains all info about mapped classes. This classes and their properties can be extracted like this:

    Iterator clsMappings = cfg.getClassMappings(); while(clsMappings.hasNext()){     PersistentClass mapping = (PersistentClass) clsMappings.next();     handleProperties(mapping.getPropertyIterator(), map); } 
  • Vast majority of properties are the objects of org.hibernate.mapping.SimpleValue types. Our point of interest is the method SimpleValue.getType() - in this method is defined what type will be used to convert properties values back-and-forth while working with DB

    Type result = TypeFactory.heuristicType(typeName, typeParameters); 

At this point I understand that I am unable to modify BASIC_TYPES - so the only way - to replace SimpleValue object to the properties of java.util.Date types to my custom Object that will be able to know the exact type to convert.

The solution:

  • Create custom container entity manager factory by extending HibernatePersistence class and overriding its method createContainerEntityManagerFactory:

    public class HibernatePersistenceExtensions extends HibernatePersistence {      @Override     public EntityManagerFactory createContainerEntityManagerFactory(PersistenceUnitInfo info, Map map) {          if ("true".equals(map.get("hibernate.use.custom.entity.manager.factory"))) {             return CustomeEntityManagerFactoryFactory.createCustomEntityManagerFactory(info, map);         } else {             return super.createContainerEntityManagerFactory(info, map);         }     } } 
  • Create Hibernate configuration object, modify value ojects for java.util.Date properties and then create custom entity manager factory.

    public class ReattachingEntityManagerFactoryFactory {       @SuppressWarnings("rawtypes")     public static EntityManagerFactory createContainerEntityManagerFactory(     PersistenceUnitInfo info, Map map) {         Ejb3Configuration cfg = new Ejb3Configuration();          Ejb3Configuration configured = cfg.configure( info, map );          handleClassMappings(cfg, map);          return configured != null ? configured.buildEntityManagerFactory() : null;     }      @SuppressWarnings("rawtypes")     private static void handleClassMappings(Ejb3Configuration cfg, Map map) {         Iterator clsMappings = cfg.getClassMappings();         while(clsMappings.hasNext()){              PersistentClass mapping = (PersistentClass) clsMappings.next();              handleProperties(mapping.getPropertyIterator(), map);         }     }         private static void handleProperties(Iterator props, Map map) {          while(props.hasNext()){              Property prop = (Property) props.next();              Value value = prop.getValue();              if (value instanceof Component) {                  Component c = (Component) value;                  handleProperties(c.getPropertyIterator(), map);              } else {                   handleReturnUtilDateInsteadOfTimestamp(prop, map);               }          }      private static void handleReturnUtilDateInsteadOfTimestamp(Property prop, Map map) {         if ("true".equals(map.get("hibernate.return.date.instead.of.timestamp"))) {             Value value = prop.getValue();              if (value instanceof SimpleValue) {                 SimpleValue simpleValue = (SimpleValue) value;                 String typeName = simpleValue.getTypeName();                 if ("java.util.Date".equals(typeName)) {                     UtilDateSimpleValue udsv = new UtilDateSimpleValue(simpleValue);                     prop.setValue(udsv);                 }             }         }     }  } 

As you can see I just iterate over every property and substitute SimpleValue-object for UtilDateSimpleValue for properties of type java.util.Date. This is very simple class - it implements the same interface as SimpleValue object, e.g org.hibernate.mapping.KeyValue. In constructor original SimpleValue object is passed - so every call to UtilDateSimpleValue is redirected to the original object with one exception - method getType(...) return my custom Type.

public class UtilDateSimpleValue implements KeyValue{      private SimpleValue value;      public UtilDateSimpleValue(SimpleValue value) {         this.value = value;     }      public SimpleValue getValue() {         return value;     }      @Override     public int getColumnSpan() {         return value.getColumnSpan();     }      ...      @Override     public Type getType() throws MappingException {         final String typeName = value.getTypeName();          if (typeName == null) {                 throw new MappingException("No type name");         }          Type result = new UtilDateUserType();          return result;     }     ... } 
  • And the last step is implementation of UtilDateUserType. I just extend original org.hibernate.type.TimestampType and override its method get() like this:

    public class UtilDateUserType extends TimestampType{      @Override     public Object get(ResultSet rs, String name) throws SQLException {         Timestamp ts = rs.getTimestamp(name);          Date result = null;         if(ts != null){             result = new Date(ts.getTime());         }          return result;     } } 

That is all. A little bit tricky, but now every java.util.Date property is returned as java.util.Date without any additional modifications of existing code (annotations or modifying setters). As I find out in Hibernate 4 or above there is a much more easier way to substitute your own type (see details here: Hibernate TypeResolver). Any suggestions or criticism are welcome.

like image 41
dim1902 Avatar answered Oct 18 '22 04:10

dim1902