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: