如何强制Hibernate将日期返回为java.util.Date而不是Timestamp? [英] How to force Hibernate to return dates as java.util.Date instead of Timestamp?

查看:105
本文介绍了如何强制Hibernate将日期返回为java.util.Date而不是Timestamp?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述



我有一个带有java.util.Date类型变量的持久化类:

  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)
私人日期startDate;


$ / code>

DB中的对应表格:

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

然后我将Period对象保存到DB:

 期间p =新期间(); 
日期d =新日期();
p.setStartDate();
myDao.save(p);

然后,如果我试图从DB中提取我的对象,它会返回变量startDate时间戳记类型 - 以及我尝试使用的所有地方等于(...)都返回false。



问题:是任何强制Hibernate返回日期作为java.util.Date类型的对象而不是时间戳的对象,而不需要明确修改每个这样的变量(例如,它必须能够工作,而不需要显式修改java.util.Date类型的已存在变量)



注:



我发现了多个显式解决方案,使用或设置器被修改 - 但我有很多类与日期变量 - 所以我需要一些集中的解决方案,所有下面描述的不够好:

    $ b $

      @Column 
    @Type(type =date)
    private日期startDate;


  1. 使用注释@Temporal(TemporalType.DATE) - java .sql.Date将被返回

      @Temporal(TemporalType.DATE)
    @Column(name =CREATION_DATE )
    私人日期startDate;


  2. 通过修改setter(深度复制) - java.util .Date将被退回

      public void setStartDate(Date startDate){
    if(startDate!= null){
    this.startDate = new Date(startDate.getTime());
    } else {
    this.startDate = null;
    }
    }


  3. 通过创建我自己的类型: - 将返回java.util.Date




  4. 详细信息如下:
    http://blogs.sourceallies.com/2012/02/hibernate-date-vs- timestamp /

    解决方案

    所以,我花了一些时间解决这个问题。这不是很漂亮,但至少有一个起点 - 也许有人会用一些有用的评论来补充这个。



    关于我在过程中发现的映射的一些信息:


    • 包含Hibernate类型到属性类型的基本映射的类是org.hibernate.type.TypeFactory。所有这些映射都存储在不可修改的映射中

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




    正如您在java.util中看到的那样。与Hibernate类型相关的日期类型org.hibernate.type.TimestampType


    • 下一个有趣的时刻 - 创建Hibernate org.hibernate.cfg .Configuration - 包含有关映射类的所有信息的对象。这些类和它们的属性可以像下面这样提取:

        Iterator clsMappings = cfg.getClassMappings(); 
      while(clsMappings.hasNext()){
      PersistentClass mapping =(PersistentClass)clsMappings.next();
      handleProperties(mapping.getPropertyIterator(),map);
      }


    • 绝大多数属性都是org.hibernate.mapping的对象.SimpleValue类型。我们感兴趣的是SimpleValue.getType()方法 - 在这个方法中定义了什么类型将用于在使用数据库时来回转换属性值

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




    此时我明白我无法修改BASIC_TYPES - 这是唯一的方法 - 将SimpleValue对象替换为java.util.Date类型的属性到我的自定义对象,以便能够知道要转换的确切类型。



    解决方案: 通过扩展HibernatePersistence类和创建自定义容器实体管理器工厂覆盖其方法createContainerEntityManagerFactory:

    pre $ public class HibernatePersistenceExtensions extends HibernatePersistence {

    @Override
    public EntityManagerFactory createContainerEntityManagerFactory(PersistenceUnitInfo info,Map map){

    if(true.equals(map.get(hibernate.use.custom.entity.manager.factory))){
    返回CustomeEntityManagerFactoryFactory.createCustomEntityManagerFactory(info,map);
    } else {
    return super.createContainerEntityManagerFactory(info,map);



    code

  5. 创建Hibernate配置对象,修改java.util.Date属性的值,然后创建自定义实体管理器工厂。

      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);

    返回配置!= 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);



    $ b private static void handleReturnUtilDateInsteadOfTimestamp(Property prop,Map map){
    if(true.equals(map.get( hibernate.return.date.instead.of.timestamp))){
    Value value = prop.getValue();

    if(valueValue instanceof SimpleValue){
    SimpleValue simpleValue =(SimpleValue)value;
    String typeName = simpleValue.getTypeName();
    if(java.util.Date.equals(typeName)){
    UtilDateSimpleValue udsv = new UtilDateSimpleValue(simpleValue);
    prop.setValue(udsv);
    }
    }
    }
    }

    }




  6. 正如你所看到的,我只是迭代每一个属性,并将用于UtilDateSimpleValue的SimpleValue-object替换为java.util.Date类型的属性。这是非常简单的类 - 它实现与SimpleValue对象相同的接口,例如org.hibernate.mapping.KeyValue。在构造函数中传递原始的SimpleValue对象 - 因此对UtilDateSimpleValue的每次调用都被重定向到原始对象,但有一个例外 - 方法getType(...)返回我的自定义类型。

      public class UtilDateSimpleValue implements KeyValue {

    private SimpleValue value;

    public UtilDateSimpleValue(SimpleValue value){
    this.value = value;
    }

    public SimpleValue getValue(){
    返回值;
    }

    @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);
    }

    键入result = new UtilDateUserType();

    返回结果;
    }
    ...
    }




    • 最后一步是实现UtilDateUserType。我只是扩展原来的org.hibernate.type.TimestampType并覆盖它的get()方法:

        public class UtilDateUserType extends TimestampType {

      @Override
      public Object get(ResultSet rs,String name)throws SQLException {
      Timestamp ts = rs.getTimestamp(name);

      日期结果=空;
      if(ts!= null){
      result = new Date(ts.getTime());
      }

      返回结果;


      code $

      $ b $ p

      就是这样。有点棘手,但现在每个java.util.Date属性都以java.util.Date的形式返回,而不需要对现有代码(注释或修改设置器)进行任何额外的修改。正如我在Hibernate 4或更高版本中发现的那样,有一种更简单的方法可以替代你自己的类型(详情请参阅: Hibernate TypeResolver )。任何建议或批评都是受欢迎的。

      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/

      解决方案

      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.

      这篇关于如何强制Hibernate将日期返回为java.util.Date而不是Timestamp?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆