-->
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: After Hibernate Upgrade unnecessary Updates due to inverse?!
PostPosted: Wed Dec 18, 2013 12:16 pm 
Newbie

Joined: Wed Dec 18, 2013 11:31 am
Posts: 2
Hi there!
We have upgraded from Hibernate 3.1 to 4.1.
After that, when I store a POJO with translations without changing anything, in debug-mode I suddenly got additional (unnecessary from my point of view) update statements on my POJO and its translations.

XML-Configurations:
<class name="com.infoniqa.pmp.common.InputField" table="GLINPFLD" lazy="false">
<cache usage="read-write" region="XXX"/>
<id name="id" column="INPFLDID">
<generator class="org.hibernate.id.UUIDHexGenerator"/>
</id>
<map name="translationsInternal" cascade="merge,delete-orphan" inverse="true" lazy="false">
<cache usage="read-write" region="XXX"/>
<key column="INPFLDID"/>
<map-key type="com.infoniqa.common.hibernate.LocaleUserType">
<column name="LNGCDE"/>
<column name="CTYCDE"/>
</map-key>
<one-to-many class="com.infoniqa.pmp.common.InputFieldTranslation"/>
</map>
</class>

<class name="com.infoniqa.pmp.common.InputFieldTranslation" table="GLINPFLDL" lazy="false">
<cache usage="read-write" region="XXX"/>
<id name="id" column="INPFLDLID">
<generator class="org.hibernate.id.UUIDHexGenerator"/>
</id>
<many-to-one name="localizedEntity" column="INPFLDID" class="com.infoniqa.pmp.common.InputField"/>
<property name="label" column="LABDSC"/>
<property name="description" column="FLDDSC"/>
<property name="languageCode" column="LNGCDE"/>
<property name="countryCode" column="CTYCDE"/>
<property name="textModified" column="TXTMOD" type="com.infoniqa.common.hibernate.BooleanType"/>
</class>

Between InputField and InputFieldTranslation there is a bidirectional one-to-many relationship. InputField contains its translations as a map.

SQL-Logging-Output:
update
GLINPFLD
set
DOCTYPID=?,
FLDMRK=?,
DBAFLD=?,
USRFLD=?,
FLDDEFID=?,
REPMRK=?,
ADDREP=?,
PARTYPID=?,
INDVAL=?,
PRESPA=?,
SUFSPA=?,
XPRTBL=?,
XPRFLD=?,
DEFVAL=?,
ORGCDEID=?,
CRTDAT=?,
CRTUSR=?,
CHGDAT=?,
CHGUSR=?,
RECSTS=?
where
INPFLDID=?

update
GLINPFLDL
set
LNGCDE=?,
CTYCDE=?
where
INPFLDLID=?

... some more updates for GLINPFLDL for each localization we have got (e.g. en_US, aso... so 10 as a whole).

In Hibernate 3.1, when InputField got .merged(), its translations-map was marked as detached and dirty and everything for updates has been prepared, BUT fairly deep in Hibernate there is the class AbstractCollectionPersister.java with its method insertRows():
public void insertRows(PersistentCollection collection, Serializable id, SessionImplementor session) throws HibernateException {
if ( !isInverse && isRowInsertEnabled() ) {
//here comes the stuff, which would execute unnecessary update statements
}}

Our translations-maps are inverse=true. Unnecessary update statements are only executed, if !isInverse => in Hibernate 3.1 we had no problems with unnecessary update statements.

However... in Hibernate 4.1 instead of AbstractCollectionPersister.java with insertRows() for one-to-many relationships the obviously new class OneToManyPersister.java with its method insertRows() is used:
public void insertRows(PersistentCollection collection, Serializable id, SessionImplementor session)
throws HibernateException {
super.insertRows( collection, id, session );
writeIndex( collection, collection.entries( this ), id, session );
}

=> no !isInverse any more => unnecessary updates appear and cost in special cases many performance.

I am no expert on hibernate. I habe no idea, why my translation-collection is marked as detached and dirty and why behavior has changed from Hibernate 3.1 to 4.1

Help from you would be VERY appreciated. Shall I change my xml-configuration? The only way so far to stop updates is to remove inverse, but according to one of my colleagues, we should not remove it.

Best wishes
Xanatos84


Top
 Profile  
 
 Post subject: Re: After Hibernate Upgrade unnecessary Updates due to inverse?!
PostPosted: Thu Dec 19, 2013 11:29 am 
Newbie

Joined: Wed Dec 18, 2013 11:31 am
Posts: 2
Hi there,
we have got a solution for at least already existing entities to avoid unnecessary update statements:
In our opinion, it is a bug in hibernate in PersistentMap.java Method clear():
public void clear() {
if ( isClearQueueEnabled() ) {
queueOperation( new Clear() );
}
else {
initialize( true );
if ( ! map.isEmpty() ) {
dirty();
map.clear();
}
}
}

... it happens, that map is cleared and dirty() is set, even though there is no change on map elements (our translations).

To outweigh this, we extended Hibernate's DefaultMergeEventListener and overrid method copyValues():
@Override
protected void copyValues(EntityPersister persister, Object entity, Object target, SessionImplementor source, Map copyCache) {
super.copyValues(persister, entity, target, source, copyCache);

// hack: maps are set dirty unnecessarily
// --> see org.hibernate.type.MapType.replaceElements()
// --> --> org.hibernate.collection.internal.PersistentMap.clear() -> dirty()
Type[] propertyTypes = persister.getPropertyTypes();
for (int i = 0; i < propertyTypes.length; i++) {
Type propertyType = propertyTypes[i];
if (propertyType instanceof MapType) {
Map sourceMap = (Map) persister.getPropertyValue(entity, i);
Map targetMap = (Map) persister.getPropertyValue(target, i);
if ((sourceMap instanceof PersistentMap) && (targetMap instanceof PersistentMap)) {
PersistentMap sourcePersistentMap = (PersistentMap) sourceMap;
PersistentMap targetPersistentMap = (PersistentMap) targetMap;
if (targetPersistentMap.isDirty() && !sourcePersistentMap.isDirty() &&
CollectionUtils.equals(sourcePersistentMap.keySet(), targetPersistentMap.keySet())) {
targetPersistentMap.clearDirty();
}
}
}
}
}

Hope this could help somebody facing same performance-problems. Of course we would appreciate your opinions on this solution.

Again, this only works fine for saving already existing objects. For new objects there would be unnecessary update-statemants right after its inserts!

Best wishes

Xanatos84


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.