一尘不染

如何强制Hibernate将日期返回为java.util.Date而不是Timestamp?

hibernate

情况

我有一个具有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)
       private Date startDate;

    }

DB中的对应表:

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

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

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

之后,如果我尝试从数据库中提取对象,则使用时间戳记类型的变量startDate返回该对象-我尝试使用equals(…)的所有位置均返回false。

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

注意:

我发现了许多显式解决方案,其中使用了批注或修改了setter,但是我有很多带有Date-variables的类-
因此,我需要一些集中式解决方案,而下面描述的所有内容还不够好:

  1. 使用注释@Type: -将返回java.sql.Date
        @Column
    @Type(type="date")
    private Date startDate;
  1. 使用注释@Temporal(TemporalType.DATE) -将返回java.sql.Date
        @Temporal(TemporalType.DATE)
    @Column(name=”CREATION_DATE”)
    private Date startDate;
  1. 通过修改setter(深拷贝) -将返回java.util.Date
        public void setStartDate(Date startDate) {
        if (startDate != null) {
            this.startDate = new Date(startDate.getTime());
        } else {
            this.startDate = null;
        }
    }
  1. 通过创建我自己的类型: -将返回java.util.Date

此处提供了详细信息:http : //blogs.sourceallies.com/2012/02/hibernate-date-vs-
timestamp/


阅读 529

收藏
2020-06-20

共1个答案

一尘不染

因此,我花了一些时间解决这个问题,并找到了解决方案。它不是一个漂亮的东西,但至少是一个起点-也许有人会用一些有用的注释来补充它。

我在处理中发现的一些有关映射的信息:

  • 包含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.Date类型与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()-在此方法中定义了在处理DB时将使用哪种类型来回转换属性值
        Type result = TypeFactory.heuristicType(typeName, typeParameters);

至此,我了解到我无法修改BASIC_TYPES(这是唯一的方法),无法将SimpleValue对象替换为java.util.Date类型的属性,更改为我的自定义对象,从而可以知道要转换的确切类型。

解决方案:

  • 通过扩展HibernatePersistence类并覆盖其方法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);
            }
        }
    }
  • 创建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);

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

    }

如您所见,我只是遍历每个属性,并用SimpleValue-object替换UtilDateSimpleValue来替换类型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() {
            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;
        }
        ...
    }
  • 最后一步是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);

            Date result = null;
            if(ts != null){
                result = new Date(ts.getTime());
            }

            return result;
        }
    }

就这些。有点棘手,但是现在每个java.util.Date属性都作为java.util.Date返回,而无需对现有代码进行任何其他修改(注释或修改setter)。

2020-06-20