-->
These old forums are deprecated now and set to read-only. We are waiting for you on our new forums!
More modern, Discourse-based and with GitHub/Google/Twitter authentication built-in.

All times are UTC - 5 hours [ DST ]



Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 24 posts ]  Go to page 1, 2  Next
Author Message
 Post subject: Problem with TimestampType isDirty check
PostPosted: Sun Jun 13, 2004 6:11 pm 
Newbie

Joined: Mon Oct 06, 2003 12:02 pm
Posts: 17
Hello, I'm using hibernate 2.1.4 with MSSQL 2000. I'm encountering a problem with unchanged objects being flushed back to the database. The hibernate log is showing an unchanged timestamp column as being dirty.

I patched class TimestampType and added some logging to equals(). What I am seeing is the timestamps appear to be the same, but looking carefully, x.getTime() is returning milliseconds, while y.getTime() is not. For instance:

(TimestampType.java:70) - TimestampType.equals(x=2004-06-06 17:23:08.92, y=2004-06-06 17:23:08.92) equals=false
(TimestampType.java:77) - TimestampType.equals(xTime=1086560588920 yTime=1086560588000)
(TimestampType.java:78) - TimestampType.equals(xts=true yts=true)
(TimestampType.java:85) - TimestampType.equals(xNanos=920000000 yNanos=920000000)
(TimestampType.java:87) - TimestampType.equals(xTime=1086560589840 yTime=1086560588920)
(AbstractEntityPersister.java:274) - com.foo.ClientTransactionClass.createStamp is dirty
(SessionImpl.java:2529) - Updating entity: [com.foo.ClientTransactionClass#CTT000000369001]

What I don't understand is that at line 70, the values for both x.toString() and y.toString() both display 92 milliseconds. However, yTime [y.getTime()] is missing the milliseconds. Line 70 also shows that x.equals(y) returns false. Line 85 shows 92 milliseconds for both x and y.

The updated TimestampType.equals() method is below.

Thanks for your help,
Dave

Code:
   public boolean equals(Object x, Object y) {
      
      if (x==y) return true;
      if (x==null || y==null) return false;
      
     log.debug("TimestampType.equals(x=" + x + ", y=" + y + ") equals=" + x.equals(y));
      
      long xTime = ( (java.util.Date) x ).getTime();
      long yTime = ( (java.util.Date) y ).getTime();
      boolean xts = x instanceof Timestamp;
      boolean yts = y instanceof Timestamp;
                                           
      log.debug("TimestampType.equals(xTime=" + xTime + " yTime=" + yTime + ")");
      log.debug("TimestampType.equals(xts=" + xts + " yts=" + yts + ")");
      
      int xNanos = xts ? ( (Timestamp) x ).getNanos() : 0;
      int yNanos = yts ? ( (Timestamp) y ).getNanos() : 0;
      xTime += xNanos / 1000000;
      yTime += yNanos / 1000000;
      
      log.debug("TimestampType.equals(xNanos=" + xNanos + " yNanos=" + yNanos + ")");
      
      log.debug("TimestampType.equals(xTime=" + xTime + " yTime=" + yTime + ")");
      
      if ( xTime!=yTime ) return false;
      if (xts && yts) {
         // both are Timestamps
         int xn = xNanos % 1000000;
         int yn = yNanos % 1000000;
         
         log.debug("TimestampType.equals(" + xn + ", " + yn + ")->" + (xn==yn));
         
         return xn==yn;
      }
      else {
         // at least one is a plain old Date
         return true;
      }
      
   }


Top
 Profile  
 
 Post subject:
PostPosted: Sun Jun 13, 2004 6:29 pm 
Senior
Senior

Joined: Sun Jan 04, 2004 2:46 pm
Posts: 147
Yeah think this is a bug with java.sql.Timestamp class. If one of the objects is a java.sql.Timestamp and the other is a java.util.Date then you get equality problems.

See this thread.

http://forum.hibernate.org/viewtopic.php?t=927602


Top
 Profile  
 
 Post subject:
PostPosted: Sun Jun 13, 2004 6:55 pm 
Hibernate Team
Hibernate Team

Joined: Tue Aug 26, 2003 12:50 pm
Posts: 5130
Location: Melbourne, Australia
Clearly, your timestamp is not, in fact, unchanged. Hibernate is perfect, your code is the problem ;-)


Top
 Profile  
 
 Post subject:
PostPosted: Sun Jun 13, 2004 7:21 pm 
Newbie

Joined: Mon Oct 06, 2003 12:02 pm
Posts: 17
My createStamp property of the object in question is a java.sql.Timestamp. My hibernate mapping file doesn't explicitly set the type, but hibernate appears to be mapping it to TimestampType as expected.

Gavin, of course hibernate is perfect! Unfortunately, I am having a bit of trouble understanding this less than perfect behavior.

How can both x.toString() and y.toString() return the same "2004-06-06 17:23:08.92", but x.getTime() returns 1086560588920, and y.getTime() returns 1086560588000?

Thanks,
Dave

_________________
Dave


Top
 Profile  
 
 Post subject: Java dates
PostPosted: Sun Jun 13, 2004 7:47 pm 
Newbie

Joined: Fri Jun 11, 2004 4:58 pm
Posts: 9
The long value is measured in milliseconds which are not significant digits when expressed in the string representation of the date.


Top
 Profile  
 
 Post subject:
PostPosted: Sun Jun 13, 2004 8:04 pm 
Hibernate Team
Hibernate Team

Joined: Tue Aug 26, 2003 12:50 pm
Posts: 5130
Location: Melbourne, Australia
You are forgetting about Timestamp.getNanos(), which sometimes sucks up some of the millisecond value!


Top
 Profile  
 
 Post subject:
PostPosted: Sun Jun 13, 2004 9:09 pm 
Newbie

Joined: Mon Oct 06, 2003 12:02 pm
Posts: 17
Gavin,

Timestamp.getNanos() is returning 920000000 (92 milliseconds) for both x and y.

The issue appears to be that x.getTime() is reflecting the 92 milliseconds (the final three digits in 1086560588920), but y.getTime() is not (1086560588000).

Sorry if I am being slow here, but why would getNanos() reflect 92 milliseconds, while getTime() does not for the same Timestamp?

Thanks,
Dave


Top
 Profile  
 
 Post subject:
PostPosted: Sun Jun 13, 2004 9:27 pm 
Hibernate Team
Hibernate Team

Joined: Tue Aug 26, 2003 12:50 pm
Posts: 5130
Location: Melbourne, Australia
A Timestamp sometimes holds some of the milliseconds stuff in the nanos part. You have to add them together to get the "real" milliseconds.


Top
 Profile  
 
 Post subject:
PostPosted: Sun Jun 13, 2004 9:42 pm 
Newbie

Joined: Mon Oct 06, 2003 12:02 pm
Posts: 17
Gavin,

I see. Thanks. So:

Code:
    Timestamp t = new Timestamp(1086560588920L);
    System.out.println("t1=" + t + " gettime=" + t.getTime() + " nanos=" + t.getNanos());


Results in:

Code:
t1=2004-06-06 17:23:08.92 gettime=1086560588000 nanos=920000000


The TimestampType.equals() method appears to be expecting this behavior. The problem is that for the 'old' value, stored by hibernate in the session, x.getTime() *does* return the 92 milliseconds. So the code:

Code:
long xTime = ( (java.util.Date) x ).getTime();
boolean xts = x instanceof Timestamp;
int xNanos = xts ? ( (Timestamp) x ).getNanos() : 0;
xTime += xNanos / 1000000;


Is actually calculating the wrong value, don't you think? I notice that you keep stating that Timestamp 'sometimes' pulls the milliseconds component of the time out of getTime(). What is this dependent on?

Thanks,
Dave


Top
 Profile  
 
 Post subject:
PostPosted: Mon Jun 14, 2004 4:00 pm 
Newbie

Joined: Mon Oct 06, 2003 12:02 pm
Posts: 17
Sorry to reply to my own post, but I think this is a bug in TimestampType. Depending upon the JVM version (and some other unknown factor), java.sql.Timestamp.getTime() may or may not return the milliseconds component of the time.

The TimestampType.equals() method assumes that the milliseconds component has been stripped, and adds the milliseconds to getTime() using getNanos() before doing the equals comparison.

TimestampType.equals() also happens to work when Timestamp.getTime() does return the milliseconds component for both x and y. The xTime and yTime variables are actually calculated to the incorrect value (the milliseconds component of the timestamp are included twice), but this doesn't affect the outcome of the comparison.

For me, for a reason I have yet to determine, x.getTime() [hibernate session stored value] returns the milliseconds component of the timestamp, but y.getTime() [my object] strips it. This is why the equals fails. This can be easily corrected with the following code:

Code:
      xTime = xts ? ((xTime / 1000L) * 1000L) : xTime;
      yTime = yts ? ((yTime / 1000L) * 1000L) : yTime;


This will mask off the milliseconds component prior to adding it back via getNanos().

Can anyone who is familiar with the Timestamp.getTime() issues and this component please comment on this?

Thanks for your help,
Dave


Top
 Profile  
 
 Post subject:
PostPosted: Mon Jun 14, 2004 7:35 pm 
Hibernate Team
Hibernate Team

Joined: Tue Aug 26, 2003 12:50 pm
Posts: 5130
Location: Melbourne, Australia
I am very happy with the current implementation, which always increments the millis part with any millisecond value contained in the nanos part.

It does not just naively compare millis parts.

Code:
      long xTime = ( (java.util.Date) x ).getTime();
      long yTime = ( (java.util.Date) y ).getTime();
      boolean xts = x instanceof Timestamp;
      boolean yts = y instanceof Timestamp;
      int xNanos = xts ? ( (Timestamp) x ).getNanos() : 0;
      int yNanos = yts ? ( (Timestamp) y ).getNanos() : 0;
      xTime += xNanos / 1000000;
      yTime += yNanos / 1000000;
      if ( xTime!=yTime ) return false;
      if (xts && yts) {
         // both are Timestamps
         int xn = xNanos % 1000000;
         int yn = yNanos % 1000000;
         return xn==yn;
      }
      else {
         // at least one is a plain old Date
         return true;
      }


Top
 Profile  
 
 Post subject:
PostPosted: Mon Jun 14, 2004 7:39 pm 
Hibernate Team
Hibernate Team

Joined: Tue Aug 26, 2003 12:50 pm
Posts: 5130
Location: Melbourne, Australia
Look at this, your timestamps actually have different values:

Quote:
TimestampType.equals(xTime=1086560589840 yTime=1086560588920)


This is after the adjustment that accounts for nanoseconds parts!


certainly not Hibernate's fault.


Top
 Profile  
 
 Post subject:
PostPosted: Mon Jun 14, 2004 7:41 pm 
Hibernate Team
Hibernate Team

Joined: Tue Aug 26, 2003 12:50 pm
Posts: 5130
Location: Melbourne, Australia
Please note that IBM JDK 1.3.1 has a bug where

Code:
new Timestamp(x).getTime()!=x



I hope you are not using new Timestamp().


Top
 Profile  
 
 Post subject:
PostPosted: Mon Jun 14, 2004 7:58 pm 
Newbie

Joined: Mon Oct 06, 2003 12:02 pm
Posts: 17
Gavin,

Please look again at the inputs to equals(). The x input object instance is from my jdbc driver and is in my mapped object.

x.toString()=2004-06-06 17:23:08.92
x.getTime()=1086560588920
x.getNanos()=920000000

The y input is a copy that hibernate stores in the session and is used in the dirty check.

y.toString()=2004-06-06 17:23:08.92 (same as x)
y.getTime() = 1086560588000 (not same as x -> 2004-06-06 17:23:08.0)
y.getNanos()=920000000

As you stated, the final xTime you calculate after the nanosecond adjustment is 1086560589840. Please note that new Timestamp(1086560589840L) = '2004-06-06 17:23:09.84'. Clearly that is incorrect.

Now the issue may be actually 'y'. When hibernate takes my timestamp 1086560588920 and stores in in the session, the resulting timestamp.getTime() returns 1086560588000.

Thanks for working with me on this,
Dave


Top
 Profile  
 
 Post subject:
PostPosted: Mon Jun 14, 2004 8:07 pm 
Newbie

Joined: Mon Oct 06, 2003 12:02 pm
Posts: 17
Gavin,

I am using Sun's Java(TM) 2 Runtime Environment, Standard Edition (build 1.3.1_03-b03). Yes it does have the new Timestamp(x).getTime()!=x defect. Hibernate's Environment.jvmHasTimestampBug() method returns true.

Dave

_________________
Dave


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 24 posts ]  Go to page 1, 2  Next

All times are UTC - 5 hours [ DST ]


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum

Search for:
© Copyright 2014, Red Hat Inc. All rights reserved. JBoss and Hibernate are registered trademarks and servicemarks of Red Hat, Inc.