使用XML配置替代Spring Data JPA中Hibernate的@Type注解

4

我们公司在一项老项目中使用了Java 7Hibernate 3Spring 3。所有的Spring和Hibernate配置都在XML文件中,不使用注解。

对于一些客户,数据库是Oracle,而对于其他客户,则是DB2

在Oracle数据库中,当数据类型为Date且带有时间时,我们使用java.util.Date会遇到问题。问题出现在Hibernate准备查询时的绑定级别。

java.util.Date被转换为java.sql.Timestamp,最终绑定到oracle.sql.TIMESTAMP上。

我们的列具有oracle.sql.DATE类型,因此在Oracle中需要进行隐式转换以执行查询,导致索引无法使用。

我们的解决方案是在Oracle数据库中使用Hibernate的UserTypes将java Date转换为Oracle Date。

但正如我所说,我们使用XML配置,在使用Hibernate与Spring Jpa时,我找不到@Type注释的XML配置。

以下是具有Date类型logindate的person类的代码:

import org.hibernate.annotations.Type;

import javax.persistence.*;
import java.util.Date;

public class Person {
    private Integer id;
    private String fullName;
//    @Type(type = "mypackage.OracleDate")
    private Date loginDate;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getFullName() {
        return fullName;
    }

    public void setFullName(String fullName) {
        this.fullName = fullName;
    }

    public Date getLoginDate() {
        return loginDate;
    }

    public void setLoginDate(Date loginDate) {
        this.loginDate = loginDate;
    }
}

OracleDate用户类型的代码:

public class OracleDate implements UserType {
private static final int[] SQL_TYPES = new int[] {
        Types.TIMESTAMP
};

@Override
public int[] sqlTypes() {
    return SQL_TYPES;
}
@Override
public Class returnedClass() {
    return Date.class;
}
@Override
public boolean equals(Object x, Object y) throws HibernateException {
    if (x == y) {
        return true;
    }
    if (x == null || y == null) {
        return false;
    }
    Date dtx = (Date) x;
    Date dty = (Date) y;
    return dtx.equals(dty);
}
@Override
public int hashCode(Object o) throws HibernateException {
    return o.hashCode();
}

@Override
public Object nullSafeGet(ResultSet resultSet, String[] names, Object owner) throws HibernateException, SQLException {
    Object timestamp = StandardBasicTypes.TIMESTAMP.nullSafeGet(resultSet, names, null, owner);
    if (resultSet.wasNull())
        return null;
    if (timestamp == null) {
        return null;
    }
    Date ts = (Date) timestamp;
    return ts;
}

@Override
public void nullSafeSet(PreparedStatement preparedStatement, Object value, int index) throws HibernateException, SQLException {
    if (value == null) {
        StandardBasicTypes.TIMESTAMP.nullSafeSet(preparedStatement, null, index);
    } else {
        Date ldt = ((Date) value);
        preparedStatement.setObject(index, new DATE(new Timestamp(ldt.getTime())));
    }
}

JPA Bean:
<bean id="hostEntityManager"
      class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
    <property name="dataSource" ref="dataSource"></property>
    <property name="persistenceUnitManager" ref="persistenceUnitManager"></property>
    <property name="loadTimeWeaver">
        <bean
                class="org.springframework.instrument.classloading.InstrumentationLoadTimeWeaver" />
    </property>
</bean>
<bean id="persistenceUnitManager"
      class="org.springframework.orm.jpa.persistenceunit.DefaultPersistenceUnitManager">

    <property name="persistenceXmlLocations" value="
    classpath:PREPAID.CFG/DA.JPA/PERSISTENCE-ENTITIES/persistence.xml/>

    <property name="persistenceUnitPostProcessors">
        <bean class="mypackage.MergingPersistenceUnitPostProcessor" />
    </property>
    <property name="defaultDataSource" ref="dataSource"></property>
</bean>

persistence.xml文件:

<persistence version="2.0"
         xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd ">
<persistence-unit name="system_persistence_unit">

    <provider>org.hibernate.ejb.HibernatePersistence</provider>
    <mapping-file>TESTAPPLICATION/CFG/DA/JPA/PERSISTENCE-ENTITIES/person.xml</mapping-file>
    <class>mypackage.Person</class>
    <properties>
        <property name="hibernate.dialect" value="org.hibernate.dialect.Oracle10gDialect"/>
        <property name="show_sql" value="true"/>
        <property name="hibernate.archive.autodetection" value="class" />
    </properties>
</persistence-unit>

实体映射文件:
<entity-mappings xmlns="http://java.sun.com/xml/ns/persistence/orm"
             version="2.0">

<entity class="mypackage.Person">

    <table name="Person" />

    <named-query name="findLoginsByDate">
        <query>select p from Person as p where p.loginDate >= :date </query>
    </named-query>

    <attributes>
        <id name="id">
            <column name="id" />
        </id>

        <basic name="fullName">
            <column name="username" />
        </basic>

        <basic name="loginDate"  >
            <column name="loginDate" />
        </basic>
    </attributes>

</entity>

首先,我想知道使用usertype是否是处理我的问题的最佳解决方案?

还有,TypeDef和Type注释的xml配置是什么?


你好,我不太清楚你进行数据类型转换的原因。你能否重新表述一下你问题的第二段?具体来说,是指你保存到数据库和从中获取的内容,数据库中实际的列类型是什么,索引出了什么问题等等。 - Petr Bodnár
@PetrBodnár 我编辑了我的问题以澄清问题。这里有一篇文章表达了相同的问题 https://dzone.com/articles/properly-persist-localdatetime-to-oracle-date-colu - Saman Parser
谢谢,这样好多了 :) 我猜你也可以直接从你的问题中链接到文章... - Petr Bodnár
2个回答

5
起初,我想知道是否使用usertype是处理我的问题的最佳解决方案?
在给定的情况下,当您存储带有时间日期并且没有其他选择而将其存储在DATE中时,它可能是最好的解决方案。(请注意,一些资源,如此这个说使用TIMESTAMP应该没问题,甚至不会产生更大的索引。)
那TypeDef和Type注释的xml配置是什么?
假设您无法在实体上使用注释,今天我只能看到以下可能性:
A)使用标准Hibernate映射XML
前提条件:您必须将JPA XML映射迁移到Hibernate本机格式。
将此添加到共享映射XML中:
<typedef class="com.company.OracleDate" name="OracleDate" />

然后在实体映射XML中引用您的自定义类型:

<property name="loginDate" type="OracleDate" />

一篇深入的文章

B) 尝试使用JPA 2.1中的Attribute Converter

前提条件:需要升级到Hibernate 4.3或更高版本(请查看兼容矩阵),并将您的JPA 2.0配置文件迁移到2.1。

不要实现Hibenate的UserType,而是需要实现JPA的AttributeConverter接口。这两者非常相似,但这次只需实现2个方法:DbType convertToDatabaseColumn(JavaType value)JavaType convertToEntityAttribute(DbType value)

之后,在实体映射XML中引用转换器:

<convert converter="com.company.OracleDateConverter" attribute-name="loginDate" />

深度文章,使用注释
SO问题,使用XML


0

不是直接的答案,但我会使用Oracle的TIMESTAMP类型:

  • TIMESTAMP (fractional_seconds_ precision)日期的年份、月份和日期值,还有时间的小时、分钟和秒值,其中fractional_seconds_precision可以选择指定SECOND日期时间字段中小数部分的位数,可以是0到9范围内的数字。默认值为6。例如,您可以按如下方式指定TIMESTAMP文字:

    TIMESTAMP'1997-01-31 09:26:50.124'

请参考similar问题。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接