views:

1837

answers:

4

How can i map a date from a java object to a database with Hibernate? I try different approaches, but i am not happy with them. Why? Let me explain my issue. I have the following class [1] including the main method i invoke and with the following mapping [2]. The issue about this approach you can see, when you look at the console output.

false

false

1

-1

1224754335648

1224754335000

Thu Oct 23 11:32:15 CEST 2008

Clock@67064

As you can see the the to dates are not exactly equal, although they should, so it is hard to compare them without goofing around with return value of getTime. I also tried java.sql.Date, Timestamp and date instead of timestamp in the mapping, but without success.

I wonder why the last three digits are zero and if this is a hibernate or a java issue or my own stupidity.

Thank you for reading.

[1]

public class Clock {

 int id;
 java.util.Date date;

 public static void main(String[] args) {
  HibernateUtil.init();
  HibernateUtil.getSessionFactory().getCurrentSession().beginTransaction();

  Clock clock = new Clock();
  clock.date = new java.util.Date();

  HibernateUtil.getSessionFactory().getCurrentSession().saveOrUpdate(clock);
  HibernateUtil.getSessionFactory().getCurrentSession().getTransaction().commit();

  HibernateUtil.getSessionFactory().getCurrentSession().beginTransaction();

  Clock fromDBClock = (Clock)HibernateUtil.getSessionFactory()
                        .getCurrentSession().get(Clock.class, 1);

  System.out.println(clock.date.equals(fromDBClock.date));
  System.out.println(fromDBClock.date.equals(clock.date));

  System.out.println(clock.date.compareTo(fromDBClock.date));
  System.out.println(fromDBClock.date.compareTo(clock.date));

  System.out.println(clock.date.getTime());
  System.out.println(fromDBClock.date.getTime());

  System.out.println(clock.date.toString());
  System.out.println(fromDBClock.toString());

  HibernateUtil.getSessionFactory().getCurrentSession().getTransaction().commit();

  HibernateUtil.end();
 }

 public int getId() {
  return id;
 }

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

 public java.util.Date getDate() {
  return date;
 }

 public void setDate(java.util.Date date) {
  this.date = date;
 }


}

[2]

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
        "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
        "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"&gt;

<hibernate-mapping>

    <class name="Clock" table="CLOCK">
        <id name="id" column="CLOCK_ID">
            <generator class="native"/>
        </id>
        <property name="date" type="timestamp"/>


    </class>

</hibernate-mapping>
+1  A: 

Apparently, TIMESTAMP type on your target RDBMS (what are you using, btw?) doesn't store milliseconds. Try to find another native type that does, or map your time onto something else, like long = ms-since-epoch.

Vladimir Dyuzhev
I use MySql database. What another native type do you mean?
Maerch
Some databases have multiple data types for date/time. According to doc, there is no high-precision date datatype in MySQL :( You only easy choice is to map datetime as time in milliseconds to long. If you create a custom type, even the conversion will be automatic.
Vladimir Dyuzhev
+2  A: 

SQL Server stores datetime with a precision of 3 milliseconds which can definitely cause the problem you're seeing. One fallout of this is that 23:59:59.999 in Java ends up being the next day in SQL Server. I've never had a problem with Oracle, Informix, or MySQL, but other databases may have less precision also. You can work around it by using Hibernate custom types. You have to round to something less precise than the underlying database precision when you write out the date values. Search for UserType in the Hibernate documentation, you'll be changing the nullSafeSet method to do the rounding.

Brian Deterling
I also use MySql. This is where this issues happens. Hmmm ... strange. Maybe there is some `option` i have to set to allow this precision?!
Maerch
+1  A: 

MySql DateTime precision is only to the second. Java Date precision is to the millisecond. That is why the last three digits are zeros after it has been put in the database.

Do this to your original Date:

date = date.setTime((date.getTime() / 1000) * 1000);

This will set it to the last exact second, and all your comparisons will then match.

(BTW, System.out.println(fromDBClock.toString()); should be System.out.println(fromDBClock.date.toString());

Skip Head
A: 

Personnaly, I truncate every date I receive in my POJO object with the Apache commons lang package class named DateUtils.

See [Apache commons site][1]

[1]: http://commons.apache.org/lang/api/org/apache/commons/lang/time/DateUtils.html#truncate(java.util.Date, int)

Frederic Morin