[hibernate-issues] [Hibernate-JIRA] Updated: (HHH-4967) Mapping a class more than once using entity names causes NullPointerException in org.hibernate.ejb.event.EJB3FlushEntityEventListener
Gunnar von der Beck (JIRA)
noreply at atlassian.com
Thu Mar 4 10:51:47 EST 2010
[ http://opensource.atlassian.com/projects/hibernate/browse/HHH-4967?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel ]
Gunnar von der Beck updated HHH-4967:
-------------------------------------
Attachment: hibernate-HHH-4967.zip
Attached Test Case.
Hint: The error occurs only if you have an entity listener attached.
Added corrected EJB3FlushEntityEventListener to test case as well.
> Mapping a class more than once using entity names causes NullPointerException in org.hibernate.ejb.event.EJB3FlushEntityEventListener
> -------------------------------------------------------------------------------------------------------------------------------------
>
> Key: HHH-4967
> URL: http://opensource.atlassian.com/projects/hibernate/browse/HHH-4967
> Project: Hibernate Core
> Issue Type: Bug
> Components: entity-manager
> Affects Versions: 3.3.2
> Environment: Hibernate Entity Manager 3.3.2.GA, JBoss EAP 4.3, Oracle 10
> Reporter: Gunnar von der Beck
> Attachments: hibernate-HHH-4967.zip
>
> Original Estimate: 30 minutes
> Remaining Estimate: 30 minutes
>
> Map a class more than once and use *entity names*, e.g. as follows:
> {code: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>
> <!-- Mapping for multilingual texts up to 4000 characters -->
> <class dynamic-insert="true" dynamic-update="true" optimistic-lock="version"
> name="mypackage.MultilingualText" table="multilingualtext"
> entity-name="MultilingualShort">
> ...
> <!-- the short text -->
> <property name="text" type="java.lang.String">
> <column name="text" sql-type="varchar2(4000)" />
> </property>
>
> </class>
> <!-- Mapping for multilingual texts holding more than 4000 characters -->
> <class dynamic-insert="true" dynamic-update="true" optimistic-lock="version"
> name="mypackage.MultilingualText" table="multilinguallongtext"
> entity-name="MultilingualLong">
>
> ...
> <!-- the long text -->
> <property name="text" type="java.lang.String">
> <column name="text" sql-type="clob" />
> </property>
> </class>
>
> </hibernate-mapping>
> {code}
> Use this entity names within a list / set / map /... as follows:
> {code:xml}
> <class name="myClass" ...>
> ...
> <!-- multilingual text field for up to 4000 characters -->
> <set access="field" cascade="all,delete-orphan" fetch="join" lazy="false"
> name="description" table="myclassdescription">
> <key>
> <column name="myclass_id" sql-type="varchar2(36)" />
> </key>
> <many-to-many unique="true" class="mypackage.MultilingualText"
> entity-name="MultilingualShort">
> <column name="text_id" sql-type="varchar2(36)"/>
> </many-to-many>
> </set>
> ...
> </class>
> {code}
> When modifying such a list / set / map and calling {{entityManager.merge(<myClass>)}} you get the following exception on {{entityManager.flush()}}:
> {noformat}
> java.lang.NullPointerException
> at org.hibernate.ejb.event.EJB3FlushEntityEventListener.copyState(EJB3FlushEntityEventListener.java:53)
> at org.hibernate.ejb.event.EJB3FlushEntityEventListener.invokeInterceptor(EJB3FlushEntityEventListener.java:42)
> at org.hibernate.event.def.DefaultFlushEntityEventListener.handleInterception(DefaultFlushEntityEventListener.java:308)
> at org.hibernate.event.def.DefaultFlushEntityEventListener.scheduleUpdate(DefaultFlushEntityEventListener.java:248)
> at org.hibernate.event.def.DefaultFlushEntityEventListener.onFlushEntity(DefaultFlushEntityEventListener.java:128)
> at org.hibernate.event.def.AbstractFlushingEventListener.flushEntities(AbstractFlushingEventListener.java:196)
> at org.hibernate.event.def.AbstractFlushingEventListener.flushEverythingToExecutions(AbstractFlushingEventListener.java:76)
> at org.hibernate.event.def.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:26)
> at org.hibernate.impl.SessionImpl.flush(SessionImpl.java:1000)
> at org.hibernate.ejb.AbstractEntityManagerImpl.flush(AbstractEntityManagerImpl.java:304)
> {noformat}
> *Solution:* correct the {{org.hibernate.ejb.event.EJB3FlushEntityEventListener}} as follows:
> {code:title=EJB3FlushEntityEventListener.java|borderStyle=solid}
> /**
> * Overrides the LifeCycle OnSave call to call the PreUpdate operation.
> * <p>
> * Error corrected version derived from {@code hibernate-entitymanager-3.3.2GA}.
> * See method {@code copyState}: considered entity name!
> */
> public class EJB3FlushEntityEventListener extends DefaultFlushEntityEventListener implements CallbackHandlerConsumer {
> /**
> * This class serial version UID.
> */
> private static final long serialVersionUID = 1L;
> private EntityCallbackHandler callbackHandler;
> public void setCallbackHandler(EntityCallbackHandler callbackHandler) {
> this.callbackHandler = callbackHandler;
> }
> public EJB3FlushEntityEventListener() {
> super();
> }
> public EJB3FlushEntityEventListener(EntityCallbackHandler callbackHandler) {
> super();
> this.callbackHandler = callbackHandler;
> }
> @Override
> protected boolean invokeInterceptor(
> SessionImplementor session, Object entity, EntityEntry entry, Object[] values, EntityPersister persister
> ) {
> boolean isDirty = false;
> if ( entry.getStatus() != Status.DELETED ) {
> if ( callbackHandler.preUpdate( entity ) ) {
> isDirty = copyState( entity, entry.getEntityName() ,persister.getPropertyTypes(), values, session.getFactory() );
> }
> }
> return super.invokeInterceptor( session, entity, entry, values, persister ) || isDirty;
> }
> /**
> * copy the entity state into the state array and return true if the state has changed
> */
> // modified parameter list: ++entityName
> private boolean copyState(Object entity, String entityName, Type[] types, Object[] state, SessionFactory sf) {
> // modified: consider entity names when resolving metadata!
> ClassMetadata metadata =
> (entityName != null ? sf.getClassMetadata(entityName) : sf.getClassMetadata(entity.getClass()));
> Object[] newState = metadata.getPropertyValues( entity, EntityMode.POJO );
> int size = newState.length;
> boolean isDirty = false;
> for ( int index = 0; index < size ; index++ ) {
> if ( !types[index].isEqual( state[index], newState[index], EntityMode.POJO ) ) {
> isDirty = true;
> state[index] = newState[index];
> }
> }
> return isDirty;
> }
> }
> {code}
> The significant change of the code is to consider the entity name when looking up {{ClassMetadata}} in method {{copyState()}}:
> {code}
> ClassMetadata metadata = (entityName != null ? sf.getClassMetadata(entityName) : sf.getClassMetadata(entity.getClass()));
> {code}
--
This message is automatically generated by JIRA.
-
If you think it was sent incorrectly contact one of the administrators: http://opensource.atlassian.com/projects/hibernate/secure/Administrators.jspa
-
For more information on JIRA, see: http://www.atlassian.com/software/jira
More information about the hibernate-issues
mailing list