-->
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.  [ 8 posts ] 
Author Message
 Post subject: Mutable Date's and dirty checking
PostPosted: Wed Oct 20, 2004 2:18 pm 
Beginner
Beginner

Joined: Tue Sep 21, 2004 4:04 pm
Posts: 25
Location: Oldsmar, FL
Hibernate version:2.1.6

Since Java Date's are mutable we want to ensure that our objects are never sharing Date objects so we've always written out Date property setters like this:

Code:
    public void setLastUpdated(Date lastUpdated)
    {
        this.lastUpdated = new Date(lastUpdated.getTime());
    }


This seems to cause hibernate to believe the object is dirty and need to be updated. (The object that it sets is not the object it gets back.)

Is there a way to tell hibernate check dirty on a Date by value with an equals() instead of by object id?


Top
 Profile  
 
 Post subject:
PostPosted: Thu Oct 21, 2004 11:39 am 
Hibernate Team
Hibernate Team

Joined: Tue Aug 26, 2003 3:00 pm
Posts: 1816
Location: Austin, TX
Hibernate's built-in date types do all perform comparison by value already. Have a look at DateType and TimestampType in the net.sf.hibernate.type package.

More than likely you might have a problem with precision or something similiar.


Top
 Profile  
 
 Post subject:
PostPosted: Thu Oct 21, 2004 11:42 am 
Hibernate Team
Hibernate Team

Joined: Tue Aug 26, 2003 3:00 pm
Posts: 1816
Location: Austin, TX
for illustration, consider:
Code:
      Date nowD = new Date();

      Timestamp nowTS = new Timestamp( nowD.getTime() );
      nowTS.setNanos(234);


      try {
         System.out.println("     Using Date.equals() : " + nowD.equals(nowTS));
         System.out.println("Using Timestamp.equals() : " + nowTS.equals(nowD));

         System.out.println("----------------------------------");

         System.out.println("Using ts-type, from date : " + Hibernate.TIMESTAMP.equals(nowD, nowTS) );
         System.out.println("  Using ts-type, from ts : " + Hibernate.TIMESTAMP.equals(nowTS, nowD) );

         System.out.println("----------------------------------");

         System.out.println("Using date-type, from date : " + Hibernate.DATE.equals(nowD, nowTS) );
         System.out.println("  Using date-type, from ts : " + Hibernate.DATE.equals(nowTS, nowD) );
      }
      catch(HibernateException e)
      {}


Which outputs:
Code:
     Using Date.equals() : false
Using Timestamp.equals() : false
----------------------------------
Using ts-type, from date : false
  Using ts-type, from ts : false
----------------------------------
Using date-type, from date : true
  Using date-type, from ts : true


Top
 Profile  
 
 Post subject:
PostPosted: Thu Oct 21, 2004 12:07 pm 
Beginner
Beginner

Joined: Tue Sep 21, 2004 4:04 pm
Posts: 25
Location: Oldsmar, FL
Ok, here is what I'm seeing.

Here is the mapping for this property (generated by xdoclet).

Code:
        <property
            name="lastUpdated"
            type="timestamp"
            update="true"
            insert="true"
            access="property"
            column="lastUpdated"
            not-null="true"
        />


here is the full code (it's a java.util.Date):

Code:
    private Date   lastUpdated = new Date();

    /**
     * @hibernate.property
     * type = "timestamp" 
     * not-null = "true"
     */
    public Date getLastUpdated()
    {
        return lastUpdated;
    }

    public void setLastUpdated(Date lastUpdated)
    {
        this.lastUpdated = new Date(lastUpdated.getTime());
    }


If I retrieve this object in a new session (not the one it was saved in) and I don't modify it, Hibernate is (incorrectly) issuing an UPDATE when I commit the transaction.

If I change the setter like the following then there is no UPDATE (as expected).

Code:
    public void setLastUpdated(Date lastUpdated)
    {
        this.lastUpdated = lastUpdate;
    }


That's the only change I make.

The UPDATE should not be happening.
I'm I doing something wrong?


Top
 Profile  
 
 Post subject:
PostPosted: Thu Oct 21, 2004 1:51 pm 
Hibernate Team
Hibernate Team

Joined: Tue Aug 26, 2003 3:00 pm
Posts: 1816
Location: Austin, TX
Well, have you manually done the comparisons yourself using the TimestampType? Or better yet, debugged by setting a break-point at TimestampType.equals()?


Top
 Profile  
 
 Post subject:
PostPosted: Fri Oct 22, 2004 11:17 am 
Beginner
Beginner

Joined: Tue Sep 21, 2004 4:04 pm
Posts: 25
Location: Oldsmar, FL
Ok I ran it thru the debugger and I think I found a bug.

Here is the TimestampType.equals() code and I've annotated it with the debug values as comments.

It looks like Timestamp objects are getting there nano values added twice.

Code:

//net.sf.hibernate.type.TimestampType

   public boolean equals(Object x, Object y) {

// x = Date
//       fasttime = 1098456751887
// y = Timestamp
//       fasttime = 1098456751000
//       nanos = 887000000
      
      if (x==y) return true;
      if (x==null || y==null) return false;
      
      long xTime = ( (java.util.Date) x ).getTime();
// xTime = 1098456751887
      long yTime = ( (java.util.Date) y ).getTime();
// yTime = 1098456751887
//*** xTime == yTime and both have nano values set
      boolean xts = x instanceof Timestamp;
// xts = false
      boolean yts = y instanceof Timestamp;
// yts = true
      int xNanos = xts ? ( (Timestamp) x ).getNanos() : 0;
// xNanos= 0
      int yNanos = yts ? ( (Timestamp) y ).getNanos() : 0;
// yNanos= 887000000
      xTime += xNanos / 1000000;
// xTime= 1098456751887
      yTime += yNanos / 1000000;
// yTime= 1098456752774
// *** I think this is the bug: yTime already had the nanos and they are being added again.

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


Java: Sun's j2sdk1.4.2_01 on Windows XP
Hibernate 2.1.6


Top
 Profile  
 
 Post subject:
PostPosted: Fri Oct 22, 2004 11:23 am 
Beginner
Beginner

Joined: Tue Sep 21, 2004 4:04 pm
Posts: 25
Location: Oldsmar, FL
Correction: I said nanos above but I guess it's really the milliseconds that are being added twice.


Top
 Profile  
 
 Post subject:
PostPosted: Fri Oct 22, 2004 2:21 pm 
Beginner
Beginner

Joined: Tue Sep 21, 2004 4:04 pm
Posts: 25
Location: Oldsmar, FL
I took a look into CVS and the TimestampType has been changed since the Hibernate 2.1.6 release (rev 1.11).

The current code (rev 1.13) looks like this.

Code:
      if ( !Environment.jvmHasJDK14Timestamp() ) {
         xTime += xNanos / 1000000;
         yTime += yNanos / 1000000;
      }


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 8 posts ] 

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.