-->
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.  [ 4 posts ] 
Author Message
 Post subject: Unexpected update of parent in merge of parent-child
PostPosted: Wed Apr 12, 2006 5:40 pm 
Regular
Regular

Joined: Wed Jul 07, 2004 2:00 pm
Posts: 64
I have a simple eager unidirectional 1-many relationship between Person and Phone. If I read a Person and Phones in one session, then make a change to a Phone and do a merge in another session, Hibernate is also updating the Person to increment the version number. This is causing an issue with the database due to a trigger. Anyone know how to prevent this update?

Code:
package com.david.model;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

public class Person {
    private Long id;
    private String lastName;
    private String firstName;
    private Date birthDate;
    private int version;
    private List phones;
   
    public Person() {
        phones = new ArrayList();
    }

    public Long getId() {
        return id;
    }

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

    public int getVersion() {
        return version;
    }

    public void setVersion(int version) {
        this.version = version;
    }

    public Date getBirthDate() {
        return birthDate;
    }

    public void setBirthDate(Date birthDate) {
        this.birthDate = birthDate;
    }

    public String getFirstName() {
        return firstName;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }

    public String getLastName() {
        return lastName;
    }

    public void setLastName(String lastName) {
        this.lastName = lastName;
    }

    public List getPhones() {
        return phones;
    }

    public void addPhone(Phone phone) {
        phones.add(phone);
    }
}
package com.david.model;

public class Phone {
    private Long id;
    private int version;
    private String phoneNo;
   
    public Phone() {
    }

    public String getPhoneNo() {
        return phoneNo;
    }

    public void setPhoneNo(String phoneNo) {
        this.phoneNo = phoneNo;
    }
}


Hibernate version: 3.1.3

Mapping documents:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping
package="com.david.model"
default-lazy="false" default-access="field">
<class name="Person" table="person" dynamic-update="true" select-before-update="true">
<id name="id" type="long" column="id">
<generator class="native"/>
</id>
<version name="version" type="int" column="version"/>
<property name="birthDate" column="birth_date" type="date"/>
<property name="firstName" column="first_name" type="string" length="20" not-null="true"/>
<property name="lastName" column="last_name" type="string" length="20" not-null="true"/>
<bag name="phones" cascade="all">
<key column="person_id" not-null="true" update="false"/>
<one-to-many class="Phone"/>
</bag>
</class>
</hibernate-mapping>

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping
package="com.david.model"
default-lazy="false" default-access="field">
<class name="Phone" table="phone" dynamic-update="true">
<id name="id" type="long" column="id">
<generator class="native"/>
</id>
<version name="version" type="int" column="version"/>
<property name="phoneNo" column="phone" type="string" length="20" not-null="true"/>
</class>
</hibernate-mapping>

Code between sessionFactory.openSession() and session.close():
package com.david.app;

import java.util.Iterator;
import java.util.List;

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.cfg.Configuration;

import com.david.model.Person;
import com.david.model.Phone;

public class TestHibApp {
public static void main(String[] args) {
Configuration config = new Configuration();
config.configure();
SessionFactory sf = config.buildSessionFactory();
Session session = sf.openSession();
Transaction txn = session.beginTransaction();
Person joe = (Person) session.get(Person.class,new Long(1));
txn.commit();
session.close();
System.out.println("--- Done load ---");

Person joe2 = new Person();
joe2.setBirthDate(joe.getBirthDate());
joe2.setFirstName(joe.getFirstName());
joe2.setId(joe.getId());
joe2.setLastName(joe.getLastName());
joe2.setVersion(joe.getVersion());
List phones = joe.getPhones();
Iterator it = phones.iterator();
while (it.hasNext()) {
Phone phone = (Phone) it.next();
joe2.addPhone(phone);
}
Phone phone = (Phone) joe2.getPhones().get(0);
phone.setPhoneNo("(999)123-5127");

System.out.println("--- Doing merge---");
Session session2 = sf.openSession();
Transaction txn2 = session2.beginTransaction();
session2.merge(joe2);
txn2.commit();
session2.close();
}
}


Full stack trace of any exception that occurs:

Name and version of the database you are using:
MySQL, also seen in Oracle

The generated SQL (show_sql=true):
17:37:20,331 INFO Environment:479 - Hibernate 3.1.3
17:37:20,341 INFO Environment:509 - hibernate.properties not found
17:37:20,351 INFO Environment:525 - using CGLIB reflection optimizer
17:37:20,351 INFO Environment:555 - using JDK 1.4 java.sql.Timestamp handling
17:37:20,471 INFO Configuration:1308 - configuring from resource: /hibernate.cfg.xml
17:37:20,471 INFO Configuration:1285 - Configuration resource: /hibernate.cfg.xml
17:37:20,592 INFO Configuration:469 - Reading mappings from resource: com/david/model/Person.hbm.xml
17:37:20,732 INFO HbmBinder:309 - Mapping class: com.david.model.Person -> person
17:37:20,762 INFO Configuration:469 - Reading mappings from resource: com/david/model/Phone.hbm.xml
17:37:20,792 INFO HbmBinder:309 - Mapping class: com.david.model.Phone -> phone
17:37:20,802 INFO Configuration:1419 - Configured SessionFactory: null
17:37:20,802 INFO HbmBinder:2349 - Mapping collection: com.david.model.Person.phones -> phone
17:37:20,912 INFO DriverManagerConnectionProvider:41 - Using Hibernate built-in connection pool (not for production use!)
17:37:20,912 INFO DriverManagerConnectionProvider:42 - Hibernate connection pool size: 20
17:37:20,912 INFO DriverManagerConnectionProvider:45 - autocommit mode: false
17:37:20,912 INFO DriverManagerConnectionProvider:80 - using driver: com.mysql.jdbc.Driver at URL: jdbc:mysql:///test
17:37:20,922 INFO DriverManagerConnectionProvider:86 - connection properties: {user=david, password=****}
17:37:21,252 INFO SettingsFactory:77 - RDBMS: MySQL, version: 5.0.16-nt
17:37:21,262 INFO SettingsFactory:78 - JDBC driver: MySQL-AB JDBC Driver, version: mysql-connector-java-3.1.10 ( $Date: 2005/05/19 15:52:23 $, $Revision: 1.1.2.2 $ )
17:37:21,292 INFO Dialect:103 - Using dialect: org.hibernate.dialect.MySQLDialect
17:37:21,302 INFO TransactionFactoryFactory:34 - Transaction strategy: org.hibernate.transaction.JDBCTransactionFactory
17:37:21,302 INFO TransactionManagerLookupFactory:33 - No TransactionManagerLookup configured (in JTA environment, use of read-write or transactional second-level cache is not recommended)
17:37:21,302 INFO SettingsFactory:125 - Automatic flush during beforeCompletion(): disabled
17:37:21,302 INFO SettingsFactory:129 - Automatic session close at end of transaction: disabled
17:37:21,302 INFO SettingsFactory:136 - JDBC batch size: 15
17:37:21,302 INFO SettingsFactory:139 - JDBC batch updates for versioned data: disabled
17:37:21,312 INFO SettingsFactory:144 - Scrollable result sets: enabled
17:37:21,312 INFO SettingsFactory:152 - JDBC3 getGeneratedKeys(): enabled
17:37:21,312 INFO SettingsFactory:160 - Connection release mode: auto
17:37:21,312 INFO SettingsFactory:184 - Maximum outer join fetch depth: 2
17:37:21,312 INFO SettingsFactory:187 - Default batch fetch size: 1
17:37:21,312 INFO SettingsFactory:191 - Generate SQL with comments: disabled
17:37:21,312 INFO SettingsFactory:195 - Order SQL updates by primary key: disabled
17:37:21,312 INFO SettingsFactory:338 - Query translator: org.hibernate.hql.ast.ASTQueryTranslatorFactory
17:37:21,323 INFO ASTQueryTranslatorFactory:24 - Using ASTQueryTranslatorFactory
17:37:21,323 INFO SettingsFactory:203 - Query language substitutions: {}
17:37:21,323 INFO SettingsFactory:209 - Second-level cache: enabled
17:37:21,323 INFO SettingsFactory:213 - Query cache: disabled
17:37:21,323 INFO SettingsFactory:325 - Cache provider: org.hibernate.cache.EhCacheProvider
17:37:21,333 INFO SettingsFactory:228 - Optimize cache for minimal puts: disabled
17:37:21,333 INFO SettingsFactory:237 - Structured second-level cache entries: disabled
17:37:21,343 INFO SettingsFactory:264 - Statistics: disabled
17:37:21,343 INFO SettingsFactory:268 - Deleted entity synthetic identifier rollback: disabled
17:37:21,343 INFO SettingsFactory:283 - Default entity-mode: pojo
17:37:21,383 INFO SessionFactoryImpl:154 - building session factory
17:37:21,393 WARN Configurator:126 - No configuration found. Configuring ehcache from ehcache-failsafe.xml found in the classpath: jar:file:/C:/hibernate-3.1/lib/ehcache-1.1.jar!/ehcache-failsafe.xml
17:37:21,693 INFO SessionFactoryObjectFactory:82 - Not binding factory to JNDI, no JNDI name configured
17:37:21,763 DEBUG SQL:346 - select person0_.id as id0_0_, person0_.version as version0_0_, person0_.birth_date as birth3_0_0_, person0_.first_name as first4_0_0_, person0_.last_name as last5_0_0_ from person person0_ where person0_.id=?
17:37:21,793 DEBUG LongType:80 - binding '1' to parameter: 1
17:37:21,803 DEBUG IntegerType:122 - returning '47' as column: version0_0_
17:37:21,843 DEBUG DateType:122 - returning '13 February 2004' as column: birth3_0_0_
17:37:21,843 DEBUG StringType:122 - returning 'Joe' as column: first4_0_0_
17:37:21,843 DEBUG StringType:122 - returning 'Smith' as column: last5_0_0_
17:37:21,873 DEBUG SQL:346 - select phones0_.person_id as person4_1_, phones0_.id as id1_, phones0_.id as id1_0_, phones0_.version as version1_0_, phones0_.phone as phone1_0_ from phone phones0_ where phones0_.person_id=?
17:37:21,873 DEBUG LongType:80 - binding '1' to parameter: 1
17:37:21,873 DEBUG LongType:122 - returning '1' as column: id1_0_
17:37:21,873 DEBUG IntegerType:122 - returning '17' as column: version1_0_
17:37:21,873 DEBUG StringType:122 - returning '(999)123-5927' as column: phone1_0_
17:37:21,883 DEBUG LongType:122 - returning '1' as column: person4_1_
17:37:21,883 DEBUG LongType:122 - returning '1' as column: id1_
17:37:21,883 DEBUG LongType:122 - returning '2' as column: id1_0_
17:37:21,883 DEBUG IntegerType:122 - returning '5' as column: version1_0_
17:37:21,893 DEBUG StringType:122 - returning '178-7431' as column: phone1_0_
17:37:21,893 DEBUG LongType:122 - returning '1' as column: person4_1_
17:37:21,893 DEBUG LongType:122 - returning '2' as column: id1_
17:37:21,903 DEBUG LongType:122 - returning '8' as column: id1_0_
17:37:21,903 DEBUG IntegerType:122 - returning '0' as column: version1_0_
17:37:21,903 DEBUG StringType:122 - returning '894-2357' as column: phone1_0_
17:37:21,903 DEBUG LongType:122 - returning '1' as column: person4_1_
17:37:21,903 DEBUG LongType:122 - returning '8' as column: id1_
17:37:21,903 DEBUG LongType:122 - returning '9' as column: id1_0_
17:37:21,903 DEBUG IntegerType:122 - returning '0' as column: version1_0_
17:37:21,903 DEBUG StringType:122 - returning '343-7635' as column: phone1_0_
17:37:21,903 DEBUG LongType:122 - returning '1' as column: person4_1_
17:37:21,903 DEBUG LongType:122 - returning '9' as column: id1_
--- Done load ---
--- Doing merge---
17:37:21,943 DEBUG SQL:346 - select person0_.id as id0_1_, person0_.version as version0_1_, person0_.birth_date as birth3_0_1_, person0_.first_name as first4_0_1_, person0_.last_name as last5_0_1_, phones1_.person_id as person4_3_, phones1_.id as id3_, phones1_.id as id1_0_, phones1_.version as version1_0_, phones1_.phone as phone1_0_ from person person0_ left outer join phone phones1_ on person0_.id=phones1_.person_id where person0_.id=?
17:37:21,943 DEBUG LongType:80 - binding '1' to parameter: 1
17:37:21,943 DEBUG LongType:122 - returning '1' as column: id1_0_
17:37:21,943 DEBUG IntegerType:122 - returning '17' as column: version1_0_
17:37:21,943 DEBUG StringType:122 - returning '(999)123-5927' as column: phone1_0_
17:37:21,943 DEBUG IntegerType:122 - returning '47' as column: version0_1_
17:37:21,953 DEBUG DateType:122 - returning '13 February 2004' as column: birth3_0_1_
17:37:21,953 DEBUG StringType:122 - returning 'Joe' as column: first4_0_1_
17:37:21,953 DEBUG StringType:122 - returning 'Smith' as column: last5_0_1_
17:37:21,953 DEBUG LongType:122 - returning '1' as column: person4_3_
17:37:21,953 DEBUG LongType:122 - returning '1' as column: id3_
17:37:21,953 DEBUG LongType:122 - returning '2' as column: id1_0_
17:37:21,953 DEBUG IntegerType:122 - returning '5' as column: version1_0_
17:37:21,953 DEBUG StringType:122 - returning '178-7431' as column: phone1_0_
17:37:21,953 DEBUG LongType:122 - returning '1' as column: person4_3_
17:37:21,953 DEBUG LongType:122 - returning '2' as column: id3_
17:37:21,953 DEBUG LongType:122 - returning '8' as column: id1_0_
17:37:21,953 DEBUG IntegerType:122 - returning '0' as column: version1_0_
17:37:21,953 DEBUG StringType:122 - returning '894-2357' as column: phone1_0_
17:37:21,963 DEBUG LongType:122 - returning '1' as column: person4_3_
17:37:21,963 DEBUG LongType:122 - returning '8' as column: id3_
17:37:21,963 DEBUG LongType:122 - returning '9' as column: id1_0_
17:37:21,963 DEBUG IntegerType:122 - returning '0' as column: version1_0_
17:37:21,963 DEBUG StringType:122 - returning '343-7635' as column: phone1_0_
17:37:21,983 DEBUG LongType:122 - returning '1' as column: person4_3_
17:37:21,993 DEBUG LongType:122 - returning '9' as column: id3_
17:37:22,003 DEBUG SQL:346 - update phone set version=?, phone=? where id=? and version=?
17:37:22,003 DEBUG IntegerType:80 - binding '18' to parameter: 1
17:37:22,003 DEBUG StringType:80 - binding '(999)123-5127' to parameter: 2
17:37:22,003 DEBUG LongType:80 - binding '1' to parameter: 3
17:37:22,003 DEBUG IntegerType:80 - binding '17' to parameter: 4
17:37:22,003 DEBUG SQL:346 - update person set version=? where id=? and version=?
17:37:22,003 DEBUG IntegerType:80 - binding '48' to parameter: 1
17:37:22,003 DEBUG LongType:80 - binding '1' to parameter: 2
17:37:22,003 DEBUG IntegerType:80 - binding '47' to parameter: 3


Debug level Hibernate log excerpt:


Top
 Profile  
 
 Post subject:
PostPosted: Thu Apr 13, 2006 4:53 pm 
Regular
Regular

Joined: Sat Jan 07, 2006 8:30 pm
Posts: 68
I tried a merge call in one of my project but it fails. I see that the difference between you application and mine would be that you do have a version.
Is that mandatory in merge so it would work correctly

"We very strongly recommend that you use version/timestamp columns for optimistic locking with Hibernate.
This is the optimal strategy with respect to performance and is the only strategy that correctly handles modifications
made to detached instances (ie. when Session.merge() is used)."


Top
 Profile  
 
 Post subject:
PostPosted: Thu Apr 13, 2006 5:25 pm 
Regular
Regular

Joined: Wed Jul 07, 2004 2:00 pm
Posts: 64
I expect that a version is necessary in most cases for the merge. Since the entity that you are merging back into Hibernate has been disconnected from the session, you don't know if someone else has modified the record in the database between the time that you read the entity and the time that you are merging the entity back in. Lets say that, when you read the entity initially, a property x was 12. While disconnected, you change the value to 98. But some other user snuck in and changed the value in the database to 23. When you merge your entity back in, Hibernate will see that the x property is dirty. But without a version, it doesn't know if the value of 23 that it sees in the database was the same value that you saw initially before changing it to 98. Having a version allows you to do this:
When you initially read the entity, you also read version=6. When the other user modified the database, the record will now have version=7. When you do the merge, Hibernate sees that some other user changed the data, because the version in the record you are merging doesn't match the newly read current state. Hibernate throws a StaleStateException.


Top
 Profile  
 
 Post subject:
PostPosted: Thu Apr 13, 2006 5:31 pm 
Regular
Regular

Joined: Sat Jan 07, 2006 8:30 pm
Posts: 68
Thanks for replying - yeah, for dirty flag makes totally sense

I describe my problem over here - I have no version but it fails
http://forum.hibernate.org/viewtopic.ph ... highlight=

Then question is, do I have to have a version in order to move away from that exception.

I tried to lookup extra info in the documentation but that particular case doesn't seems to be well documented/explained.


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