-->
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.  [ 2 posts ] 
Author Message
 Post subject: Retrieving many-to-many association deletes association
PostPosted: Thu Mar 03, 2005 9:49 pm 
Newbie

Joined: Thu Mar 03, 2005 8:59 pm
Posts: 2
Hi,

I have discovered that when I have a many-to-many association that there are circumstances where association is deleted when hibernate gets the collection. For example I have a Car object that has many Owners. When hibernate retrieves the Owners associated with the Car it deletes the association.

I have simplified this example so that it is easy to run and see the behaviour. It should be possible to copy and paste the code to create a running example.

Thanks in advance,

Simon

Hibernate version:2.1.3

Mapping documents:

car.hbm.xml
Code:
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
  "-//Hibernate/Hibernate Mapping DTD 2.0//EN"
  "http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd">
<hibernate-mapping>
  <class name="Car" table="CAR">
    <id name="id" column="CAR_ID" type="integer">
      <generator class="increment"/>
    </id>
    <set name="owners" table="OWNER_CAR">
      <key column="CAR_ID"/>
      <many-to-many column="OWNER_ID" class="Owner"/>
    </set>
  </class>
</hibernate-mapping>


owner.hbm.xml
Code:
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
  "-//Hibernate/Hibernate Mapping DTD 2.0//EN"
  "http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd">
<hibernate-mapping>
  <class name="Owner" table="OWNER">
    <id name="id" column="OWNER_ID" type="integer">
      <generator class="increment"/>
    </id>
    <set name="cars" table="OWNER_CAR" inverse="true">
      <key column="OWNER_ID"/>
      <many-to-many column="CAR_ID" class="Car"/>
    </set>
  </class>
</hibernate-mapping>


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

This code creates a car, several owners, and then associates the car with the owners. I am using the standard HibernateUtil implementation. Note that this behaviour occurs when I execute the load in a new session. When executed in the same session hibernate still deletes the association but then creates it again.

Code:
Session session;
session = HibernateUtil.currentSession();

Transaction tx;
tx = session.beginTransaction();
Car car = new Car();
Serializable id = session.save(car);
car = (Car) session.load(Car.class, id);
for(int i = 0; i < 10; i++) {
  Owner owner = new Owner();
  session.save(owner);
  car.getOwners().add(owner);
}
tx.commit();
HibernateUtil.closeSession();
session = HibernateUtil.currentSession();
tx = session.beginTransaction();
car = (Car) session.load(Car.class, id);
System.out.println("Size = " + car.getOwners().size());
tx.commit();
HibernateUtil.closeSession();


Classes and other files:

Car.java
Code:
import java.util.Set;
import java.util.HashSet;
import java.util.List;
import java.util.ArrayList;

public class Car {

  public Integer getId() {
    return id;
  }

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

  private Integer id = null;

  public boolean equals(Object obj) {
    if (obj != null && this.id != null && obj instanceof Car) {
      return this.id.equals(((Car)obj).id);
    }
    return super.equals(obj);
  }

  public int hashCode() {
    if (id != null) {
      return id.hashCode();
    }
    return super.hashCode();
  }

  private Set owners = new HashSet();

  public Set getOwners() {
    return owners;
  }

  public void setOwners(Set set) {
    this.owners = owners;
  }

}


Owner.java
Code:
import java.util.Set;
import java.util.HashSet;
import java.util.List;
import java.util.ArrayList;

public class Owner {

  public Integer getId() {
    return id;
  }

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

  private Integer id = null;

  public boolean equals(Object obj) {
    if (obj != null && this.id != null && obj instanceof Owner) {
      return this.id.equals(((Owner)obj).id);
    }
    return super.equals(obj);
  }

  public int hashCode() {
    if (id != null) {
      return id.hashCode();
    }
    return super.hashCode();
  }

  private Set cars = new HashSet();

  public Set getCars() {
    return cars;
  }

  public void setCars(Set set) {
    this.cars = set;
  }

}


HibernateUtil.java
Code:
import net.sf.hibernate.SessionFactory;
import net.sf.hibernate.Session;
import net.sf.hibernate.HibernateException;
import net.sf.hibernate.cfg.Configuration;

public class HibernateUtil {

    private static final SessionFactory sessionFactory;

    static {
        try {
            // Create the SessionFactory
            sessionFactory = new Configuration()
            .addClass(Car.class)
            .addClass(Owner.class)
            .buildSessionFactory();
        } catch (Throwable ex) {
            throw new ExceptionInInitializerError(ex);
        }
    }

    public static final ThreadLocal session = new ThreadLocal();

    public static Session currentSession() throws HibernateException {
        Session s = (Session) session.get();
        // Open a new Session, if this Thread has none yet
        if (s == null) {
            s = sessionFactory.openSession();
            session.set(s);
        }
        return s;
    }

    public static void closeSession() throws HibernateException {
        Session s = (Session) session.get();
        session.set(null);
        if (s != null)
            s.close();
    }
}


Main.java
Code:
import net.sf.hibernate.Session;
import net.sf.hibernate.Transaction;
import java.io.Serializable;

public class Main {
  public static void main(String args[]) throws Exception {
    Session session;
    session = HibernateUtil.currentSession();
       
    Transaction tx;
    tx = session.beginTransaction();
    Car car = new Car();
    Serializable id = session.save(car);
    car = (Car) session.load(Car.class, id);
    for(int i = 0; i < 10; i++) {
           Owner owner = new Owner();
           session.save(owner);
           car.getOwners().add(owner);
    }
    tx.commit();
    HibernateUtil.closeSession();
    session = HibernateUtil.currentSession();
    tx = session.beginTransaction();
    car = (Car) session.load(Car.class, id);
    System.out.println("Size = " + car.getOwners().size());
    tx.commit();
    HibernateUtil.closeSession();
  }
}


hibernate.properties
Code:
hibernate.connection.driver_class=net.sourceforge.jtds.jdbc.Driver
hibernate.connection.url=jdbc:jtds:sqlserver://localhost:1433/master
hibernate.connection.username=sa
hibernate.connection.password=
hibernate.connection.pool_size=1
hibernate.dialect=net.sf.hibernate.dialect.SQLServerDialect
hibernate.show_sql=false


Name and version of the database you are using:

I have tried this with both SQL Server 8 and Postgres 7.4 and get the same behaviour.

The generated SQL (show_sql=true):

As you can see from the sql below the last sql statement executed deletes the association even though the code clearly only inserts and retrieves and object.

Hibernate: insert into CAR (CAR_ID) values (?)
Hibernate: insert into OWNER (OWNER_ID) values (?)
Hibernate: insert into OWNER_CAR (CAR_ID, OWNER_ID) values (?, ?)
Hibernate: select car0_.CAR_ID as CAR_ID0_ from CAR car0_ where car0_.CAR_ID=?
Hibernate: select owners0_.OWNER_ID as OWNER_ID__, owners0_.CAR_ID as CAR_ID__, owner1_.OWNER_ID as OWNER_ID0_ from OWNER_CAR owners0_ inner join OWNER owner1_ on owners0_.OWNER_ID=owner1_.OWNER_ID where owners0_.CAR_ID=?
Hibernate: select cars0_.CAR_ID as CAR_ID__, cars0_.OWNER_ID as OWNER_ID__ from OWNER_CAR cars0_ where cars0_.OWNER_ID=?
Hibernate: select cars0_.CAR_ID as CAR_ID__, cars0_.OWNER_ID as OWNER_ID__ from OWNER_CAR cars0_ where cars0_.OWNER_ID=?
Hibernate: select cars0_.CAR_ID as CAR_ID__, cars0_.OWNER_ID as OWNER_ID__ from OWNER_CAR cars0_ where cars0_.OWNER_ID=?
Hibernate: select cars0_.CAR_ID as CAR_ID__, cars0_.OWNER_ID as OWNER_ID__ from OWNER_CAR cars0_ where cars0_.OWNER_ID=?
Hibernate: select cars0_.CAR_ID as CAR_ID__, cars0_.OWNER_ID as OWNER_ID__ from OWNER_CAR cars0_ where cars0_.OWNER_ID=?
Hibernate: select cars0_.CAR_ID as CAR_ID__, cars0_.OWNER_ID as OWNER_ID__ from OWNER_CAR cars0_ where cars0_.OWNER_ID=?
Hibernate: select cars0_.CAR_ID as CAR_ID__, cars0_.OWNER_ID as OWNER_ID__ from OWNER_CAR cars0_ where cars0_.OWNER_ID=?
Hibernate: select cars0_.CAR_ID as CAR_ID__, cars0_.OWNER_ID as OWNER_ID__ from OWNER_CAR cars0_ where cars0_.OWNER_ID=?
Hibernate: select cars0_.CAR_ID as CAR_ID__, cars0_.OWNER_ID as OWNER_ID__ from OWNER_CAR cars0_ where cars0_.OWNER_ID=?
Hibernate: select cars0_.CAR_ID as CAR_ID__, cars0_.OWNER_ID as OWNER_ID__ from OWNER_CAR cars0_ where cars0_.OWNER_ID=?
Size = 0
[i]Hibernate: delete from OWNER_CAR where CAR_ID=?[b][i]

[b]Debug level Hibernate log excerpt:


I'm not sure if this log is much use but here it is anyway.

14:09:46,839 INFO Environment:462 - Hibernate 2.1.3
14:09:46,855 INFO Environment:496 - loaded properties from resource hibernate.properties: {hibernate.connection.username=sa, hibernate.connection.password=, hibernate.cglib.use_reflection_optimizer=true, hibernate.dialect=net.sf.hibernate.dialect.SQLServerDialect, hibernate.connection.pool_size=1, hibernate.show_sql=false, hibernate.connection.url=jdbc:jtds:sqlserver://simontpc:1433/webshop/lastUpdateCount=true, hibernate.connection.driver_class=net.sourceforge.jtds.jdbc.Driver}
14:09:46,855 INFO Environment:519 - using CGLIB reflection optimizer
14:09:46,886 INFO Configuration:347 - Mapping resource: Car.hbm.xml
14:09:47,074 INFO Binder:229 - Mapping class: Car -> CAR
14:09:47,199 INFO Binder:560 - Mapping collection: Car.owners -> OWNER_CAR
14:09:47,214 INFO Configuration:347 - Mapping resource: Owner.hbm.xml
14:09:47,246 INFO Binder:229 - Mapping class: Owner -> OWNER
14:09:47,246 INFO Binder:560 - Mapping collection: Owner.cars -> OWNER_CAR
14:09:47,246 INFO Configuration:613 - processing one-to-many association mappings
14:09:47,246 INFO Configuration:622 - processing one-to-one association property references
14:09:47,246 INFO Configuration:647 - processing foreign key constraints
14:09:47,277 INFO Dialect:82 - Using dialect: net.sf.hibernate.dialect.SQLServerDialect
14:09:47,277 INFO SettingsFactory:62 - Use outer join fetching: true
14:09:47,277 INFO DriverManagerConnectionProvider:42 - Using Hibernate built-in connection pool (not for production use!)
14:09:47,292 INFO DriverManagerConnectionProvider:43 - Hibernate connection pool size: 1
14:09:47,292 INFO DriverManagerConnectionProvider:77 - using driver: net.sourceforge.jtds.jdbc.Driver at URL: jdbc:jtds:sqlserver://simontpc:1433/webshop/lastUpdateCount=true
14:09:47,292 INFO DriverManagerConnectionProvider:78 - connection properties: {user=sa, password=}
14:09:47,308 INFO TransactionManagerLookupFactory:33 - No TransactionManagerLookup configured (in JTA environment, use of process level read-write cache is not recommended)
14:09:47,308 DEBUG DriverManagerConnectionProvider:84 - total checked-out connections: 0
14:09:47,324 DEBUG DriverManagerConnectionProvider:100 - opening new JDBC connection
14:09:47,527 DEBUG DriverManagerConnectionProvider:106 - created connection to: jdbc:jtds:sqlserver://simontpc:1433/webshop/lastUpdateCount=true, Isolation Level: 2
14:09:47,527 DEBUG DriverManagerConnectionProvider:120 - returning connection to pool, pool size: 1
14:09:47,527 INFO SettingsFactory:102 - Use scrollable result sets: true
14:09:47,527 INFO SettingsFactory:105 - Use JDBC3 getGeneratedKeys(): true
14:09:47,527 INFO SettingsFactory:108 - Optimize cache for minimal puts: false
14:09:47,527 INFO SettingsFactory:117 - Query language substitutions: {}
14:09:47,527 INFO SettingsFactory:128 - cache provider: net.sf.ehcache.hibernate.Provider
14:09:47,542 INFO Configuration:1093 - instantiating and configuring caches
14:09:47,683 INFO SessionFactoryImpl:119 - building session factory
14:09:48,152 INFO SessionFactoryObjectFactory:82 - no JNDI name configured
14:09:48,214 DEBUG DriverManagerConnectionProvider:84 - total checked-out connections: 0
14:09:48,214 DEBUG DriverManagerConnectionProvider:90 - using pooled JDBC connection, pool size: 0
14:09:48,246 DEBUG BatcherImpl:196 - about to open: 0 open PreparedStatements, 0 open ResultSets
14:09:48,246 DEBUG SQL:237 - insert into CAR (CAR_ID) values (?)
14:09:48,261 DEBUG BatcherImpl:241 - preparing statement
14:09:48,261 DEBUG BatcherImpl:203 - done closing: 0 open PreparedStatements, 0 open ResultSets
14:09:48,261 DEBUG BatcherImpl:261 - closing statement
14:09:48,261 DEBUG BatcherImpl:196 - about to open: 0 open PreparedStatements, 0 open ResultSets
14:09:48,261 DEBUG SQL:237 - insert into OWNER (OWNER_ID) values (?)
14:09:48,261 DEBUG BatcherImpl:241 - preparing statement
14:09:48,261 DEBUG BatcherImpl:203 - done closing: 0 open PreparedStatements, 0 open ResultSets
14:09:48,277 DEBUG BatcherImpl:261 - closing statement
14:09:48,277 DEBUG BatcherImpl:196 - about to open: 0 open PreparedStatements, 0 open ResultSets
14:09:48,277 DEBUG SQL:237 - insert into OWNER_CAR (CAR_ID, OWNER_ID) values (?, ?)
14:09:48,277 DEBUG BatcherImpl:241 - preparing statement
14:09:48,277 DEBUG BatcherImpl:203 - done closing: 0 open PreparedStatements, 0 open ResultSets
14:09:48,277 DEBUG BatcherImpl:261 - closing statement
14:09:48,293 DEBUG DriverManagerConnectionProvider:120 - returning connection to pool, pool size: 1
14:09:48,293 DEBUG DriverManagerConnectionProvider:84 - total checked-out connections: 0
14:09:48,293 DEBUG DriverManagerConnectionProvider:90 - using pooled JDBC connection, pool size: 0
14:09:48,308 DEBUG BatcherImpl:196 - about to open: 0 open PreparedStatements, 0 open ResultSets
14:09:48,308 DEBUG SQL:237 - select car0_.CAR_ID as CAR_ID0_ from CAR car0_ where car0_.CAR_ID=?
14:09:48,308 DEBUG BatcherImpl:241 - preparing statement
14:09:48,308 DEBUG BatcherImpl:203 - done closing: 0 open PreparedStatements, 0 open ResultSets
14:09:48,308 DEBUG BatcherImpl:261 - closing statement
14:09:48,308 DEBUG BatcherImpl:196 - about to open: 0 open PreparedStatements, 0 open ResultSets
14:09:48,324 DEBUG SQL:237 - select owners0_.OWNER_ID as OWNER_ID__, owners0_.CAR_ID as CAR_ID__, owner1_.OWNER_ID as OWNER_ID0_ from OWNER_CAR owners0_ inner join OWNER owner1_ on owners0_.OWNER_ID=owner1_.OWNER_ID where owners0_.CAR_ID=?
14:09:48,324 DEBUG BatcherImpl:241 - preparing statement
14:09:48,339 DEBUG BatcherImpl:203 - done closing: 0 open PreparedStatements, 0 open ResultSets
14:09:48,339 DEBUG BatcherImpl:261 - closing statement
14:09:48,339 DEBUG BatcherImpl:196 - about to open: 0 open PreparedStatements, 0 open ResultSets
14:09:48,339 DEBUG SQL:237 - select cars0_.CAR_ID as CAR_ID__, cars0_.OWNER_ID as OWNER_ID__ from OWNER_CAR cars0_ where cars0_.OWNER_ID=?
14:09:48,339 DEBUG BatcherImpl:241 - preparing statement
14:09:48,339 DEBUG BatcherImpl:203 - done closing: 0 open PreparedStatements, 0 open ResultSets
14:09:48,339 DEBUG BatcherImpl:261 - closing statement
14:09:48,355 DEBUG BatcherImpl:196 - about to open: 0 open PreparedStatements, 0 open ResultSets
14:09:48,355 DEBUG SQL:237 - select cars0_.CAR_ID as CAR_ID__, cars0_.OWNER_ID as OWNER_ID__ from OWNER_CAR cars0_ where cars0_.OWNER_ID=?
14:09:48,355 DEBUG BatcherImpl:241 - preparing statement
14:09:48,355 DEBUG BatcherImpl:203 - done closing: 0 open PreparedStatements, 0 open ResultSets
14:09:48,355 DEBUG BatcherImpl:261 - closing statement
14:09:48,371 DEBUG BatcherImpl:196 - about to open: 0 open PreparedStatements, 0 open ResultSets
14:09:48,371 DEBUG SQL:237 - select cars0_.CAR_ID as CAR_ID__, cars0_.OWNER_ID as OWNER_ID__ from OWNER_CAR cars0_ where cars0_.OWNER_ID=?
14:09:48,371 DEBUG BatcherImpl:241 - preparing statement
14:09:48,371 DEBUG BatcherImpl:203 - done closing: 0 open PreparedStatements, 0 open ResultSets
14:09:48,371 DEBUG BatcherImpl:261 - closing statement
14:09:48,371 DEBUG BatcherImpl:196 - about to open: 0 open PreparedStatements, 0 open ResultSets
14:09:48,371 DEBUG SQL:237 - select cars0_.CAR_ID as CAR_ID__, cars0_.OWNER_ID as OWNER_ID__ from OWNER_CAR cars0_ where cars0_.OWNER_ID=?
14:09:48,371 DEBUG BatcherImpl:241 - preparing statement
14:09:48,371 DEBUG BatcherImpl:203 - done closing: 0 open PreparedStatements, 0 open ResultSets
14:09:48,386 DEBUG BatcherImpl:261 - closing statement
14:09:48,386 DEBUG BatcherImpl:196 - about to open: 0 open PreparedStatements, 0 open ResultSets
14:09:48,386 DEBUG SQL:237 - select cars0_.CAR_ID as CAR_ID__, cars0_.OWNER_ID as OWNER_ID__ from OWNER_CAR cars0_ where cars0_.OWNER_ID=?
14:09:48,386 DEBUG BatcherImpl:241 - preparing statement
14:09:48,386 DEBUG BatcherImpl:203 - done closing: 0 open PreparedStatements, 0 open ResultSets
14:09:48,386 DEBUG BatcherImpl:261 - closing statement
14:09:48,386 DEBUG BatcherImpl:196 - about to open: 0 open PreparedStatements, 0 open ResultSets
14:09:48,386 DEBUG SQL:237 - select cars0_.CAR_ID as CAR_ID__, cars0_.OWNER_ID as OWNER_ID__ from OWNER_CAR cars0_ where cars0_.OWNER_ID=?
14:09:48,386 DEBUG BatcherImpl:241 - preparing statement
14:09:48,402 DEBUG BatcherImpl:203 - done closing: 0 open PreparedStatements, 0 open ResultSets
14:09:48,402 DEBUG BatcherImpl:261 - closing statement
14:09:48,402 DEBUG BatcherImpl:196 - about to open: 0 open PreparedStatements, 0 open ResultSets
14:09:48,402 DEBUG SQL:237 - select cars0_.CAR_ID as CAR_ID__, cars0_.OWNER_ID as OWNER_ID__ from OWNER_CAR cars0_ where cars0_.OWNER_ID=?
14:09:48,402 DEBUG BatcherImpl:241 - preparing statement
14:09:48,418 DEBUG BatcherImpl:203 - done closing: 0 open PreparedStatements, 0 open ResultSets
14:09:48,418 DEBUG BatcherImpl:261 - closing statement
14:09:48,418 DEBUG BatcherImpl:196 - about to open: 0 open PreparedStatements, 0 open ResultSets
14:09:48,418 DEBUG SQL:237 - select cars0_.CAR_ID as CAR_ID__, cars0_.OWNER_ID as OWNER_ID__ from OWNER_CAR cars0_ where cars0_.OWNER_ID=?
14:09:48,418 DEBUG BatcherImpl:241 - preparing statement
14:09:48,418 DEBUG BatcherImpl:203 - done closing: 0 open PreparedStatements, 0 open ResultSets
14:09:48,418 DEBUG BatcherImpl:261 - closing statement
14:09:48,418 DEBUG BatcherImpl:196 - about to open: 0 open PreparedStatements, 0 open ResultSets
14:09:48,418 DEBUG SQL:237 - select cars0_.CAR_ID as CAR_ID__, cars0_.OWNER_ID as OWNER_ID__ from OWNER_CAR cars0_ where cars0_.OWNER_ID=?
14:09:48,418 DEBUG BatcherImpl:241 - preparing statement
14:09:48,418 DEBUG BatcherImpl:203 - done closing: 0 open PreparedStatements, 0 open ResultSets
14:09:48,433 DEBUG BatcherImpl:261 - closing statement
14:09:48,433 DEBUG BatcherImpl:196 - about to open: 0 open PreparedStatements, 0 open ResultSets
14:09:48,433 DEBUG SQL:237 - select cars0_.CAR_ID as CAR_ID__, cars0_.OWNER_ID as OWNER_ID__ from OWNER_CAR cars0_ where cars0_.OWNER_ID=?
14:09:48,433 DEBUG BatcherImpl:241 - preparing statement
14:09:48,433 DEBUG BatcherImpl:203 - done closing: 0 open PreparedStatements, 0 open ResultSets
14:09:48,433 DEBUG BatcherImpl:261 - closing statement
Size = 0
14:09:48,449 DEBUG BatcherImpl:196 - about to open: 0 open PreparedStatements, 0 open ResultSets
14:09:48,449 DEBUG SQL:237 - delete from OWNER_CAR where CAR_ID=?
14:09:48,449 DEBUG BatcherImpl:241 - preparing statement
14:09:48,449 DEBUG BatcherImpl:203 - done closing: 0 open PreparedStatements, 0 open ResultSets
14:09:48,449 DEBUG BatcherImpl:261 - closing statement
14:09:48,449 DEBUG DriverManagerConnectionProvider:120 - returning connection to pool, pool size: 1[/code]


Top
 Profile  
 
 Post subject:
PostPosted: Thu Mar 03, 2005 10:41 pm 
Newbie

Joined: Thu Mar 03, 2005 8:59 pm
Posts: 2
My colleague discovered that in the setOwners method of the Car class was

Code:
  public void setCars(Set set) {
    this.cars = owners;
  }


and should be

Code:
  public void setCars(Set set) {
    this.cars = set;
  }


Hopefully this is reason we have observed this behaviour in our application.[/code]


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