-->
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.  [ 18 posts ]  Go to page 1, 2  Next
Author Message
 Post subject: Problem with equals / hashcode
PostPosted: Mon Jun 30, 2008 11:40 am 
Newbie

Joined: Mon Jun 05, 2006 12:41 pm
Posts: 19
Hibernate version: 3.2final

Mapping documents:

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

<hibernate-mapping package="fr.ineo.tinea.ramses.model.persistent" auto-import="true">
   <class name="RMSSite" table="sit_site">
      <id name="id" type="integer" column="SIT_ID">
         <generator class="increment"/>
      </id>

      <property name="ip"                column="SIT_ip"             type="string" not-null="true" length="20"/>
      <property name="latitude"             column="SIT_latitude"          type="string" not-null="false" length="20"/>
      <property name="longitude"             column="SIT_longitude"          type="string" not-null="false" length="20"/>
      <property name="name"                column="SIT_name"             type="string" not-null="false" length="20"/>
      <property name="description"          column="SIT_description"       type="string" not-null="false" length="200"/>
      <property name="lastSignOfLifeDate"    column="SIT_last_sign_of_life"    type="java.util.Date" not-null="false"/>
   
      <idbag name="paramValueList" table="SPA_SITE_PARAMETER">
         <collection-id type="integer" column="SPA_ID">
            <generator class="increment" />
         </collection-id>
         <key column="SIT_SITE_SIT_ID"/>
         <many-to-many class="RMSParamValue" column="PVA_PARAMETER_VALUE_PVA_ID" lazy="false" fetch="join"/>
      </idbag>

   </class>   
</hibernate-mapping>


Code between sessionFactory.openSession() and session.close():

Use of a standard HibernateUtil class


Code:

   public static Session getSession()
      throws HibernateException {
      Session s = (Session) threadSession.get();
      try {
         if (s == null) {
            log.debug("Opening new Session for this thread.");
            if (getInterceptor() != null) {
               log.debug("Using interceptor: " + getInterceptor().getClass());
               s = getSessionFactory().openSession(getInterceptor());
            } else {
               s = getSessionFactory().openSession();
            }
            threadSession.set(s);
         }
      } catch (HibernateException ex) {
         throw new HibernateException(ex);
      }
      return s;
   }


   public static void closeSession()
      throws HibernateException {
      try {
         Session s = (Session) threadSession.get();
         threadSession.set(null);
         if (s != null && s.isOpen()) {
            log.debug("Closing Session of this thread.");
            s.close();
         }
      } catch (HibernateException ex) {
         throw new HibernateException(ex);
      }
   }



Full stack trace of any exception that occurs:

No error occurs in console

Name and version of the database you are using:

Mysql 5 stable latest version, installer mysql-essential-5.0.51b-win32.msi
All tables are created in MyISAM format

Java file (hashcode/equals) of Site class:


Code:
private int ip;

....

   @Override
   public boolean equals(Object obj) {
      if (!(obj instanceof RMSSite) || obj == null) return false;
      else {
         RMSSite site = (RMSSite)obj;
         return this.getIp() == site.getIp();
      }
   }

   @Override
   public int hashCode() {
      return new Integer(getId()).hashCode();
   }



What happen? :

Code:
Session session = RMSHibernateUtil.getSession();
RMSHibernateUtil.beginTransaction();

RMSSite s1 = new RMSSite();
s1.setIp("192.168.0.1");
s1.setDescription("description 1");
session.saveOrUpdate(s1);
session.flush();

RMSSite s2 = new RMSSite();
s2.setIp("192.168.0.1");
s1.setDescription("description 2");
//here should be the same object with the same business id (IP of a site)

session.saveOrUpdate(s2);
session.flush();


After this code (used in JUnit to validate my mapping files), i have 2 rows in database, and the two have the same value in IP field.

Why Hibernate does not assume the two object are "the same one" and do something like an update on s1, or nothing at all, but not creating a second row in DB?
What do i ignore?

I read this and i think i should be right in my use of "equals"...
http://www.hibernate.org/109.html

Thanks for helping !


Top
 Profile  
 
 Post subject:
PostPosted: Mon Jun 30, 2008 3:13 pm 
Pro
Pro

Joined: Tue Jun 12, 2007 4:13 am
Posts: 209
Location: Berlin, Germany
Maybe it is a simple typo error in you code. What I can see is: hashCode() and equals() do not match:
Code:
getId()

and
Code:
getIp()

will be different animals, no?

_________________
Carlo
-----------------------------------------------------------
please don't forget to rate if this post helped you


Top
 Profile  
 
 Post subject:
PostPosted: Tue Jul 01, 2008 3:33 am 
Newbie

Joined: Mon Jun 05, 2006 12:41 pm
Posts: 19
carlolf wrote:
Maybe it is a simple typo error in you code. What I can see is: hashCode() and equals() do not match:
Code:
getId()

and
Code:
getIp()

will be different animals, no?


ID is the database subroged key : it does not mean anything for me except primary key in DB... it's an auto inc. int.

The business key is IP (like 192.xxx.xxx.xxx ) and have to be unique, and can clearly identify 2 differents site perfectly. I could write in my mapping IP is unique but it's not my goal : i have the same problem with an object more complex having :
- id auto inc in primary key for the DB
- a composite business key based on a date and a foreign key
and so i won't be able to put an "unique" key on the two props.

Actually, equals() identify perfectly when two objects are equals. My JUnit test pass with

Code:
assertTrue(s1.equals(s2)) ;


So equals method return true, but hibernate still add 2 lines in DB!
I tried to put in hashcode
Code:
getIp().hashcode()

instead of
Code:
getId().hashcode()

but the problem remain the same ... Hibernate still create two lines in DB even when object1.equals(object2) is true

What should i do?

--> i tried to put unique="true" in mapping file for property IP, and i still can create two objects with the same IP and generated different ID!


Top
 Profile  
 
 Post subject:
PostPosted: Tue Jul 01, 2008 3:47 am 
Pro
Pro

Joined: Tue Jun 12, 2007 4:13 am
Posts: 209
Location: Berlin, Germany
Hi Slash,

if you tried to change the hashCode method to "getIP()" as in equals (which now is correct according to the general contract between equals and hashCode!!!), and you still got the DB problem with duplicated lines, then there must be another bug in your code.

Anyway, one of the strongest recommodations for Hibernate usage is:

Code:
DO NOT USE the id attribute in the equals or hashCode methods - use business attributes there!


If you use the id attribute there - you would have at least to check if it is already present; and if not, you would have to switch to the business attributes anyway.

_________________
Carlo
-----------------------------------------------------------
please don't forget to rate if this post helped you


Top
 Profile  
 
 Post subject:
PostPosted: Tue Jul 01, 2008 3:49 am 
Expert
Expert

Joined: Thu Jul 05, 2007 9:38 am
Posts: 287
why shouldn't it? You didn't make the ip unique as far as I can see.
(http://www.hibernate.org/hib_docs/core/ ... n-property)

I am not sure if hibernate uses hashCode or equal at all.

_________________
Please rate useful posts.


Schauderhaft: Softwaredevelopment, Projectmanagement, Qualitymanagement and all things "schauderhaft"


Top
 Profile  
 
 Post subject:
PostPosted: Tue Jul 01, 2008 3:52 am 
Expert
Expert

Joined: Thu Jul 05, 2007 9:38 am
Posts: 287
carlolf wrote:
Code:
DO NOT USE the id attribute in the equals or hashCode methods - use business attributes there!



And let me add another one which is independent from hibernate
Code:
DO NOT CHANGE the attributes used in the equals or hashCode methods while the object is contained in any kind of Map, Set or similiar

_________________
Please rate useful posts.


Schauderhaft: Softwaredevelopment, Projectmanagement, Qualitymanagement and all things "schauderhaft"


Top
 Profile  
 
 Post subject:
PostPosted: Tue Jul 01, 2008 4:05 am 
Newbie

Joined: Mon Jun 05, 2006 12:41 pm
Posts: 19
carlolf wrote:
Hi Slash,

if you tried to change the hashCode method to "getIP()" as in equals (which now is correct according to the general contract between equals and hashCode!!!), and you still got the DB problem with duplicated lines, then there must be another bug in your code.

Anyway, one of the strongest recommodations for Hibernate usage is:

Code:
DO NOT USE the id attribute in the equals or hashCode methods - use business attributes there!


If you use the id attribute there - you would have at least to check if it is already present; and if not, you would have to switch to the business attributes anyway.


Hi
Thanks for answering so quickly.
I ve just checked and modified all my classes (my model is very small). Now all my classes implements equals and hashcode based on something logical (business key) and not primary key (id) anymore.
I ve rewrited hashcode and equals for each object, using the same identifier in hashcode and equals per class. Exemple : for Site, hashcode and equals are based on getIp().

I still have 2 rows inserted with the code i put in the first post...


Top
 Profile  
 
 Post subject:
PostPosted: Tue Jul 01, 2008 4:10 am 
Newbie

Joined: Mon Jun 05, 2006 12:41 pm
Posts: 19
schauder wrote:
carlolf wrote:
Code:
DO NOT USE the id attribute in the equals or hashCode methods - use business attributes there!



And let me add another one which is independent from hibernate
Code:
DO NOT CHANGE the attributes used in the equals or hashCode methods while the object is contained in any kind of Map, Set or similiar


I respect this two rules and i agree to this rules.
My database is empty when i launch JUnit tests, there are no element, so the secodn rules is ok by anyway.

... but the problem still remain.

All my business classes extends a abstract class having inside :

Code:
private int id;
   
   public int getId() {
      return id;
   }

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

   @Override
   public    abstract int hashCode();

   @Override
   public abstract boolean equals(Object obj);


Could the problem come from here?


Top
 Profile  
 
 Post subject:
PostPosted: Tue Jul 01, 2008 4:57 am 
Pro
Pro

Joined: Tue Jun 12, 2007 4:13 am
Posts: 209
Location: Berlin, Germany
I really have no idea where your problems come from.

I just want to give you the advice to NOT use a primitive int member, but an

Code:
Integer object


to represent the primary key attribute. Primitive int gets a default value of 0, and Integer a default value of null. It is much better do see, that a null value means: the entity has not yet been stored into the database.

_________________
Carlo
-----------------------------------------------------------
please don't forget to rate if this post helped you


Top
 Profile  
 
 Post subject:
PostPosted: Tue Jul 01, 2008 5:09 am 
Newbie

Joined: Mon Jun 05, 2006 12:41 pm
Posts: 19
carlolf wrote:
I really have no idea where your problems come from.

I just want to give you the advice to NOT use a primitive int member, but an

Code:
Integer object


to represent the primary key attribute. Primitive int gets a default value of 0, and Integer a default value of null. It is much better do see, that a null value means: the entity has not yet been stored into the database.


I ve just tried this too
Code:
private Integer id;
   
   public Integer getId() {
      return id;
   }
   private void setId(int id) {
      this.id = new Integer(id);
   }


... and the problem remains :-s
I can still create two record in DB that are equals() and hashCode() identical ...
I feel a bit lost and don't understand what s happening.

I m still searching, perhaps it comes from my config ?
Code:
      <property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
      <property name="hibernate.connection.url">jdbc:mysql://localhost/ramses</property>
      <property name="hibernate.connection.username">ramses</property>
      <property name="hibernate.connection.password">ramses</property>

      <property name="hibernate.c3p0.min_size">5</property>
      <property name="hibernate.c3p0.max_size">20</property>
      <property name="hibernate.c3p0.timeout">1800</property>
      <property name="hibernate.c3p0.max_statement">50</property>
      
      
        <property name="hibernate.dialect">org.hibernate.dialect.MySQLMyISAMDialect</property>
      
       <property name="hibernate.default_batch_fetch_size">8</property>
        <property name="hibernate.transaction.factory_class">org.hibernate.transaction.JDBCTransactionFactory</property>

      <!--  <property name="cache.provider_class">org.hibernate.cache.NoCacheProvider</property>-->
      <property name="CacheProviderClass">org.hibernate.cache.EhCacheProvider</property>
      <property name="SecondLevelCacheEnabled">true</property>
      <property name="QueryCacheEnabled">true</property>


Another thing i tried : i recreated all my DB with InnoDB instead of MyISAM. I changed dialect in config file. All is working in the same way.


Top
 Profile  
 
 Post subject:
PostPosted: Tue Jul 01, 2008 7:15 am 
Expert
Expert

Joined: Thu Jul 05, 2007 9:38 am
Posts: 287
I said it before: there is no reason why hibernate should not save any number of records with same hashcode and being mutually equal as as equals() is concerned.

Hibernate only cares about things you tell it about. If you want th IP to be unique, declare it as such in the configuration.

_________________
Please rate useful posts.


Schauderhaft: Softwaredevelopment, Projectmanagement, Qualitymanagement and all things "schauderhaft"


Top
 Profile  
 
 Post subject:
PostPosted: Tue Jul 01, 2008 7:42 am 
Newbie

Joined: Mon Jun 05, 2006 12:41 pm
Posts: 19
schauder wrote:
I said it before: there is no reason why hibernate should not save any number of records with same hashcode and being mutually equal as as equals() is concerned.

Hibernate only cares about things you tell it about. If you want th IP to be unique, declare it as such in the configuration.


First, there is unique="true" on my property IP in my mapping file.
Second, i think that hibernate should assume that two object that are equal and hashcode identical are the same and unique one object. It should not write in DB a new object.
Moreover, it should not write the second object when the first one still be in memory and attached to the current session and a unique value is written twice...
Am i wrong? Should hibernate write twice objects in DB that are equals with same business identifier, equals method and so on??


Top
 Profile  
 
 Post subject:
PostPosted: Tue Jul 01, 2008 9:34 am 
Pro
Pro

Joined: Tue Jun 12, 2007 4:13 am
Posts: 209
Location: Berlin, Germany
Okay, I do not think you have a mapping problem, but you have a problem in your code.


Quote:
RMSSite s1 = new RMSSite();
s1.setIp("192.168.0.1");
s1.setDescription("description 1");
session.saveOrUpdate(s1);
session.flush();

RMSSite s2 = new RMSSite();
s2.setIp("192.168.0.1");
s1.setDescription("description 2");
//here should be the same object with the same business id (IP of a site)

session.saveOrUpdate(s2);
session.flush();


Because the IP of your object is not mapped as UNIQUE (otherwise MySql would cry loud!), it is completely legitime for Hibernate to assume that your two Java objects also represent two rows in the database. You do explicitely say: save object s2; and: you already saved object s1. After flushing s1, s1 now should have a primary key assigned to, whereas your new object s2 does not have it - therefore for Hibernate they are different. Why should Hibernate call your equals method on these objects?

_________________
Carlo
-----------------------------------------------------------
please don't forget to rate if this post helped you


Top
 Profile  
 
 Post subject:
PostPosted: Tue Jul 01, 2008 9:45 am 
Newbie

Joined: Mon Jun 05, 2006 12:41 pm
Posts: 19
carlolf wrote:
Okay, I do not think you have a mapping problem, but you have a problem in your code.


Quote:
RMSSite s1 = new RMSSite();
s1.setIp("192.168.0.1");
s1.setDescription("description 1");
session.saveOrUpdate(s1);
session.flush();

RMSSite s2 = new RMSSite();
s2.setIp("192.168.0.1");
s1.setDescription("description 2");
//here should be the same object with the same business id (IP of a site)

session.saveOrUpdate(s2);
session.flush();


Because the IP of your object is not mapped as UNIQUE (otherwise MySql would cry loud!), it is completely legitime for Hibernate to assume that your two Java objects also represent two rows in the database. You do explicitely say: save object s2; and: you already saved object s1. After flushing s1, s1 now should have a primary key assigned to, whereas your new object s2 does not have it - therefore for Hibernate they are different. Why should Hibernate call your equals method on these objects?


Thanks for answering
IP is now mapped as unique, because it should be. I get JDBC transaction error when i run code after i alter my table in DB to put IP unique.

Because of the mapping and equals, i think hibernate should check before saving s2 in DB. But it does not !
I have show-sql=true for hibernate config, and i clearly see two consecutice INSERT orders.

The second insert order fails because of MySQL error for violation of constraint "unique IP". But if in mysql i don't put unique on column IP (but still unique in mapping file), i have 2 rows created in DB with the same IP and two different ID (which is the DB primary key).

Is my problem not a problem? Do i assume hibernate do things it doesn't do? Why Hibernate write records with the same value which should be unique, according to mapping file?


Top
 Profile  
 
 Post subject:
PostPosted: Tue Jul 01, 2008 10:01 am 
Pro
Pro

Joined: Tue Jun 12, 2007 4:13 am
Posts: 209
Location: Berlin, Germany
Slash wrote:
Is my problem not a problem? Do i assume hibernate do things it doesn't do? Why Hibernate write records with the same value which should be unique, according to mapping file?


Hi Slash,

yes, you are assuming things Hibernate will not do. Hibernate does not check your unique constraints at runtime; the unique mapping is simply there for creating the database structure with hibernate tools.

Therefore, if you don't want a database saving call to fail with that unique constraint violation, YOUR application code must do the unique check!

_________________
Carlo
-----------------------------------------------------------
please don't forget to rate if this post helped you


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 18 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.