-->
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.  [ 1 post ] 
Author Message
 Post subject: DefaultFlushEntityEventListener and immutable natural ids
PostPosted: Sat Jun 10, 2006 8:03 pm 
Newbie

Joined: Sat Jun 10, 2006 7:50 pm
Posts: 4
There appears to be a bug in DefaultFlushEntityEventListener
when checking for changes in immutable natural ids.


Hibernate version:
We are using Hibernate 3.1.3 and Spring 1.2.6

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

<class name="com.learjet.aces.domain.site.Site" table="A4_SITE">
<id name="entityId" column="ID">
<generator class="native"/>
</id>
<natural-id mutable="false">
<property name="name" type="string"
column="SITE_NAME" >
</property>
</natural-id>

<version name="persistedVersion" column="PERSISTED_VERSION" />


<component name="auditInfo" class="com.learjet.aces.domain.AuditInfo">
<property name="active" column="IS_ACTIVE" type="boolean"
insert="true" update="true" not-null="true" />

<property name="createdBy" column="CREATED_BY" type="java.lang.String"
insert="true" update="true" not-null="true" />

<property name="creationDate" column="CREATION_DATE" type="java.util.Date"
insert="true" update="true" not-null="true" />

<property name="lastUpdatedBy" column="LAST_UPDATED_BY" type="java.lang.String"
insert="true" update="true" not-null="true" />

<property name="lastUpdatedDate" column="LAST_UPDATED" type="java.util.Date"
insert="true" update="true" not-null="true" />
</component>

<set name="holidays"
lazy="true"
table="A4_HOLIDAY"
order-by="HOLIDAY_DATE asc"
cascade="all"
>
<key column="SITE_ID"/>
<composite-element class="com.learjet.aces.domain.site.Holiday">
<property name="holidayDate" column="HOLIDAY_DATE" not-null="true"/>
</composite-element>
</set>

</class>

</hibernate-mapping>


Code between sessionFactory.openSession() and session.close():
TestCase
--------
public void testClearHoliday() throws Exception {

Site site = Site.getDAO().findSiteByName("Wichita"); // this site is in the db
if (site.getHolidays().size()==0) { // there are no holidays yet
Holiday h = new Holiday();
h.setHolidayDate(Holiday.convertYYYYMMDD("20060101"));
site.addHoliday(h);
Exception here ---> site.saveOrUpdate(); // calls SiteDAO

Site dbSite = Site.getDAO().findSiteByName("Wichita");
assertEquals(1,dbSite.getHolidays().size());
}

BaseDAO (invoked from SiteDAO)
-------
public void update(Persistable entity) throws DataAccessException {
this.hibernateTemplate.update(entity); // disappears into Spring 1.2.6, Hibernate 3.1.3
}


Full stack trace of any exception that occurs:
DefaultFlushEntityEventListener.checkNaturalId(EntityPersister, Serializable, Object[], Object[], EntityMode, SessionImplementor) line: 76
DefaultFlushEntityEventListener.getValues(Object, EntityEntry, EntityMode, boolean, SessionImplementor) line: 155
DefaultFlushEntityEventListener.onFlushEntity(FlushEntityEvent) line: 106
DefaultFlushEventListener(AbstractFlushingEventListener).flushEntities(FlushEvent) line: 195
DefaultFlushEventListener(AbstractFlushingEventListener).flushEverythingToExecutions(FlushEvent) line: 76
DefaultFlushEventListener.onFlush(FlushEvent) line: 26
SessionImpl.flush() line: 985
HibernateTemplate(HibernateAccessor).flushIfNecessary(Session, boolean) line: 394
HibernateTemplate.execute(HibernateCallback, boolean) line: 366
HibernateTemplate.update(Object, LockMode) line: 655
HibernateTemplate.update(Object) line: 651
SiteDAO(BaseDAO).update(Persistable) line: 186
SiteDAO(BaseDAO).saveOrUpdate(Persistable) line: 156
Site.saveOrUpdate() line: 115
SyncHolidayTest.testClearHoliday() line: 44
NativeMethodAccessorImpl.invoke0(Method, Object, Object[]) line: not available [native method]
NativeMethodAccessorImpl.invoke(Object, Object[]) line: not available
DelegatingMethodAccessorImpl.invoke(Object, Object[]) line: not available
Method.invoke(Object, Object...) line: not available
SyncHolidayTest(TestCase).runTest() line: 154
SyncHolidayTest(TestCase).runBare() line: 127
TestResult$1.protect() line: 106
TestResult.runProtected(Test, Protectable) line: 124
TestResult.run(TestCase) line: 109
SyncHolidayTest(TestCase).run(TestResult) line: 118
RemoteTestRunner.runTests(String[], String) line: 478
RemoteTestRunner.run() line: 344
RemoteTestRunner.main(String[]) line: 196


Name and version of the database you are using:
Oracle 8.1.7 (not a problem here)

The generated SQL (show_sql=true):
Last SQL call came from
org.hibernate.event.def.DefaultFlushEntityEventListener line 71:
loaded = session.getPersistenceContext().getNaturalIdSnapshot( identifier, persister );

Hibernate: select site_.SITE_NAME as SITE3_0_ from A4_SITE site_ where site_.ID=?

It appears that the NaturalIdSnapshot only looks at the properties contained within the
<natural-id> tag, and in this case it will always be size 1.


Debug level Hibernate log excerpt:
n/a


Location of Hibernate bug
DefaultFlushEntityEventListener
-------------------------------
//$Id: DefaultFlushEntityEventListener.java 8751 2005-12-05 18:45:52Z steveebersole $
package org.hibernate.event.def;
...
line 62:
private void checkNaturalId(
EntityPersister persister,
Serializable identifier,
Object[] current,
Object[] loaded,
EntityMode entityMode,
SessionImplementor session) {
if ( persister.hasNaturalIdentifier() ) {
if ( loaded == null ) {
loaded = session.getPersistenceContext().getNaturalIdSnapshot( identifier, persister );
}
Type[] types = persister.getPropertyTypes();
int[] props = persister.getNaturalIdentifierProperties();
boolean[] updateable = persister.getPropertyUpdateability();
for ( int i=0; i<props.length; i++ ) {
int prop = props[i];
if ( !updateable[prop] ) {
bug here ---> if ( !types[prop].isEqual( current[prop], loaded[prop], entityMode ) ) {
throw new HibernateException(
"immutable natural identifier of an instance of " +
persister.getEntityName() +
" was altered"
);
}
}
}
}
}


When i==0, prop=1, updateable[1]=false, types[1]=String, current[1]="Wichita"
but loaded[1] is out of bounds
Should be loaded[i] instead, which gives loaded[0]="Wichita"

Values from Eclipse debugger:
----------------------------
updateable.size()=4, [0]=true, [1]=false, [2]=true, [3]=false
props.size()=1, props[0]=1
types.size()=4, [0]=Integer,[1]=String,[2]=Component,[3]=OrderedSet
current.size()=4, [0]=0, [1]="Wichita", [2]=AuditInfo, [3]=PersistentSet
loaded.size()=1, [0]="Wichita"
entityMode="pojo"


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

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.