4

My company uses java 7 with hibernate 3 with spring 3 in one of it's legacy projects. All of the spring and hibernate configs are in xml files and they don't use annotations.

For some customers the database is oracle and for others it's db2.

In oracle we have problem when the data type is Date and we use java.util.date with time. The problem was located at the binding level when Hibernate prepared the query.

java.util.Date is converted to java.sql.Timestamp and finally bound to oracle.sql.TIMESTAMP.

Our column has oracle.sql.DATE type so there is an implicit conversion in Oracle to perform the query causing the index not to be used.

Our solution was using UserTypes in hibernate to convert java Date to Oracle Date in oracle databases.

But as I said we use xml config and I couldn't find xml config for @Type annotation when using hibernate with spring Jpa.

code for person class with logindate with Date type:

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

code for OracleDate UserType:

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 file:

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

entitymapping file:

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

At first I want know Is using usertype the best solution for handling my problem?

And what's the xml config for TypeDef and Type annotations?

3
  • Hi, your reason for the data type conversion is unclear to me. Could you please rephrase that part of your question? Namely the 2nd paragraph. What you save to database vs. what you get from it, what's the actual column type in database, what's wrong with the indexes... Commented Nov 16, 2019 at 16:07
  • @PetrBodnár I edited my question to clarify the problem. here is an article that express the same Issue dzone.com/articles/… Commented Nov 16, 2019 at 17:35
  • thanks, that's much better :) I guess you could link to the article directly from your question as well... Commented Nov 16, 2019 at 19:33

2 Answers 2

5
+50

At first I want know Is using usertype the best solution for handling my problem?

Under given circumstances, when you store date with time and have no other choice but have it stored inside DATE, it probably is. (Note that some resources like this say that using TIMESTAMP should be OK though and that it even doesn't produce much bigger indexes.)

And what's the xml config for TypeDef and Type annotations?

Provided that you cannot use annotations on entities, I can see only the following possibilities today:

A) Use standard Hibernate mapping XML

Preconditions: You would have to migrate your JPA XML mappings to the Hibernate native format.

Add this to your shared mapping XML:

<typedef class="com.company.OracleDate" name="OracleDate" />

And then reference your custom type in an entity mapping XML:

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

An in-depth article

B) Try to use Attribute Converter from JPA 2.1

Preconditions: You would have to migrate to Hibernate 4.3 or higher (see the compatibility matrix) and migrate your JPA 2.0 config files to 2.1.

Instead of implementing Hibenate's UserType you need to implement JPA's AttributeConverter interface. It's pretty similar, you only need to implement 2 methods this time: DbType convertToDatabaseColumn(JavaType value) and JavaType convertToEntityAttribute(DbType value).

After that, reference the converter in an entity mapping XML:

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

An in-depth article, uses annotations,
SO question, uses XML

Sign up to request clarification or add additional context in comments.

Comments

0

Not a direct answer but I'd use the Oracle TIMESTAMP type:

  • TIMESTAMP (fractional_seconds_ precision) Year, month, and day values of date, as well as hour, minute, and second values of time, where fractional_seconds_precision optionally specifies the number of digits in the fractional part of the SECOND datetime field and can be a number in the range 0 to 9. The default is 6. For example, you specify TIMESTAMP as a literal as follows:

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

Please follow the similar question for reference.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.