当前位置: 首页 > 面试题库 >

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

秦钟展
2023-03-14
问题内容

情况

我有一个具有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/


问题答案:

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

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

  • 包含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)。



 类似资料:
  • 问题内容: 我正在使用Oracle 11GR2,并且当varchar2字段为空时,在空字段上执行a 将显示在我的Eclipse控制台上。如何让它显示空字符串呢? 问题答案: 你的getter方法:

  • 问题内容: 这是我的发件人实体 当我尝试执行以下查询时: 发生以下错误: 错误:org.hibernate.property.BasicPropertyAccessor- HHH000123:类中的IllegalArgumentException:be.gimme.persistence.entities.Sender,属性的设置方法:senderId 错误:org.hibernate.prope

  • 当我使用Guzzle发送请求时,它会返回一条完整的错误消息,而不会解析内部的JSON。下面是它的外观: “”“ 客户端错误: 导致 响应:\n {\r\n ”错误“: [\r\n ”潜在客户已在此市场活动中“\r\n ]\r\n }\n ”“” 当我通过Postman发送相同的请求时,它确实会返回正确解析的响应消息。 我如何才能让Guzzle只返回消息,而不是完整的响应?

  • Edit:不幸的是,我需要以日期的形式获得结果,而不是字符串,所以我不能使用sdf.format(New Date().getTime()),因为它返回字符串。另外,我需要返回的日期是当前时间的日期,而不是来自静态字符串。我希望这能澄清

  • 问题内容: 输出: IST 2017年9月12日12:36:24 IST 2007年1月1日星期一12:36:24 但是,当我在不同的环境中使用相同的代码时,输​​出更改为以下内容: 输出: IST 2017年9月12日12:36:24 2007年1月1日星期一格林尼治标准时间12:36:24 仅供参考,我尝试在设置值之前和之后打印日历实例的时区,并且两者都在“ IST”中。 我想知道这个的根本原

  • 问题内容: 我正在为我的Web应用程序使用Hibernate和Spring。 在数据库操作中,Hibernate正在缓存实体并在下一个请求中返回它们,而不读取实际的数据库。我知道这将减少数据库的负载并提高性能。 但是,尽管此应用仍在构建中,但我需要在每个请求中从数据库加载数据(测试原因)。 有什么办法可以强制数据库读取? 我确定从此log4j消息进行缓存。 问题答案: 或(如果您使用JPA)将为您