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

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

问题描述

情况:

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

import java.util.Date;@实体@Table(name = "prd_period")@Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)公共类 Period 扩展 ManagedEntity 实现 Interval {@Column(name = "startdate_", nullable = false)私人日期开始日期;}

对应的数据库表:

创建表'prd_period'('id_' bigint(20) NOT NULL AUTO_INCREMENT,...'startdate_' 日期时间 NOT NULL)

然后我将我的 Period 对象保存到数据库:

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

之后,如果我试图从数据库中提取我的对象,它会以时间戳类型的变量 startDate 返回 - 并且我尝试使用 equals(...) 的所有地方都返回 false.

问题:是否有任何方法可以强制 Hibernate 将日期作为 java.util.Date 类型的对象而不是 Timestamp 返回,而无需显式修改每个此类变量(例如,它必须能够正常工作,没有显式修改 java.util.Date 类型的现有变量)?

注意:

我发现了许多显式解决方案,其中使用了注释或修改了 setter - 但我有许多带有日期变量的类 - 所以我需要一些集中的解决方案,下面描述的所有内容都不够好:

  1. 使用注解@Type: - java.sql.Date 将被返回

    @Column@Type(类型=日期")私人日期开始日期;

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

    @Temporal(TemporalType.DATE)@Column(名称=CREATION_DATE")私人日期开始日期;

  3. 通过修改setter(深拷贝) - java.util.Date 将被返回

    public void setStartDate(Date startDate) {如果(开始日期!= null){this.startDate = new Date(startDate.getTime());} 别的 {this.startDate = null;}}

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

详情如下: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( 基础知识);

如您所见,java.util.Date 类型与 Hibernate 类型 org.hibernate.type.TimestampType 相关联

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

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

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

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

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

解决办法:

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

    public class HibernatePersistenceExtensions extends HibernatePersistence {@覆盖public EntityManagerFactory createContainerEntityManagerFactory(PersistenceUnitInfo info, Map map) {if ("true".equals(map.get("hibernate.use.custom.entity.manager.factory"))) {返回 CustomeEntityManagerFactoryFactory.createCustomEntityManagerFactory(info, map);} 别的 {返回 super.createContainerEntityManagerFactory(info, map);}}}

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

    public class ReattachingEntityManagerFactoryFactory {@SuppressWarnings("rawtypes")公共静态 EntityManagerFactory createContainerEntityManagerFactory(PersistenceUnitInfo 信息,地图地图){Ejb3Configuration cfg = new Ejb3Configuration();Ejb3Configuration 配置 = cfg.configure( info, map );handleClassMappings(cfg, map);返回配置 != null ?configure.buildEntityManagerFactory() : null;}@SuppressWarnings("rawtypes")private static void handleClassMappings(Ejb3Configuration cfg, Map map) {迭代器 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()){属性 prop = (Property) props.next();值值 = prop.getValue();如果(组件的值实例){组件 c = (组件) 值;handleProperties(c.getPropertyIterator(), map);} 别的 {handleReturnUtilDateInsteadOfTimestamp(prop, map);}}private static void handleReturnUtilDateInsteadOfTimestamp(Property prop, Map map) {if ("true".equals(map.get("hibernate.return.date.instead.of.timestamp"))) {值值 = prop.getValue();如果(简单值的值实例){SimpleValue simpleValue = (SimpleValue) 值;String typeName = simpleValue.getTypeName();if ("java.util.Date".equals(typeName)) {UtilDateSimpleValue udsv = 新的 UtilDateSimpleValue(simpleValue);prop.setValue(udsv);}}}}}

如您所见,我只是遍历每个属性并用 SimpleValue 对象替换 UtilDateSimpleValue 以获取 java.util.Date 类型的属性.这是一个非常简单的类——它实现了与 SimpleValue 对象相同的接口,例如 org.hibernate.mapping.KeyValue.在构造函数中传递原始 SimpleValue 对象 - 因此对 UtilDateSimpleValue 的每次调用都被重定向到原始对象,只有一个例外 - 方法 getType(...) 返回我的自定义类型.

公共类UtilDateSimpleValue实现KeyValue{私有 SimpleValue 值;公共 UtilDateSimpleValue(SimpleValue 值) {this.value = 值;}公共 SimpleValue getValue() {返回值;}@覆盖公共 int getColumnSpan() {返回值.getColumnSpan();}...@覆盖公共类型 getType() 抛出 MappingException {final String typeName = value.getTypeName();if (typeName == null) {throw new MappingException("没有类型名");}输入结果 = 新的 UtilDateUserType();返回结果;}...}

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

    public class UtilDateUserType extends TimestampType{@覆盖public Object get(ResultSet rs, String name) 抛出 SQLException {时间戳 ts = rs.getTimestamp(name);日期结果=空;if(ts != null){结果 = 新日期(ts.getTime());}返回结果;}}

仅此而已.有点棘手,但现在每个 java.util.Date 属性都作为 java.util.Date 返回,无需对现有代码进行任何额外修改(注释或修改 setter).正如我在 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 而不是时间戳返回?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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