Dear Forum,
We are currently facing an 'issue' with new entities and the findDirty interceptor - and from our perspective it looks like a bug.
(well it might be also a feature ...). Thus any expertise and comments on this topic is appreciated.
Our environment:
Hibernate 4.2.7 (using JPA API 2.x)
The issue: We have two entities UserBO and InternalOrganizationBO
The relationsship between the two objects is shown below.
Both objects have a boolean flag 'flushUpdates'.
This 'flushUpdate' flag is evaluated in our custom findDirty implementation (overwrite of EmptyInterceptor.findDirty).
If 'flushUpdate' is false the findDirty returns an empty array.
What we are doing: 1. We get the UserBO object from the database (thus in managed state)
2. We create a new InternalOrganizationBO object
3. We set the flushUpdates flag for the UserBO and InternalOrganizationBO to false
4. We set the InternalOrganizationBO object for the User
5. Call the entityManager.flush method
What we think is a bug: The entityManager.flush method is triggering a
SQL INSERT for the InternalOrganizationBO
ALTHOUGH the findDirty method returns for both objects an empty array AND we didn't
call an explcit persist for the InternalOrganizationBO.
(By the way: the 'findDirty' seems to work fine when both objects are already in the database and in a managed stated)
So the questions: Is this really how the Hibernate framework should behave?
Shouldn't the 'empty array' returned from findDirty prevent an insert?
Are there other options to prevent Hibernate to do the insert?
(prefered option: only when entityManager.persist is explicitly called or the UserBO object is marked as dirty)
---- Source Code - Entity Classes
Code:
@Entity
@SqlResultSetMapping(....)
@Table(schema="...", name="...")
@IdClass(UserBO_PK.class)
public class UserBO extends AbstractBO implements Serializable {
...
@ManyToOne(fetch=FetchType.LAZY, cascade={CascadeType.MERGE, CascadeType.DETACH, CascadeType.PERSIST})
@JoinColumn(name=UserBO.INTERNALORGANIZATION_ID)
protected InternalOrganizationBO internalOrganization;
...
@Transient
protected boolean flushUpdates = false;
public boolean isFlushUpdates() {
return flushUpdates;
}
...
}
Code:
@Entity
@SqlResultSetMapping(
name="InternalOrganizationBO",
entities = @EntityResult(entityClass=InternalOrganizationBO.class),
columns = { @ColumnResult (name="externalName")})
@Table(schema="...", name="...")
@IdClass(InternalOrganizationBO_PK.class)
public class InternalOrganizationBO extends AbstractBO implements Serializable {
...
@OneToMany(mappedBy="internalOrganization", fetch=FetchType.LAZY, cascade={CascadeType.MERGE, CascadeType.DETACH, CascadeType.PERSIST})
protected List<MTMUserBO> users;
...
@Transient
protected boolean flushUpdates = false;
public boolean isFlushUpdates() {
return flushUpdates;
}
...
}
---- SourceCode Test Case:
Code:
//load user objects
UserBO user = entityManager.find(UserBO.class, new UserBO_PK("testUser"));
//create internal organization
intOrg01 = new InternalOrganizationBO(intOrg01_id);
intOrg01.setCode(intOrg01_code);
intOrg01.setBusinessUnit(BusinessUnitEnum.empty);
//set all flush updates to false
intOrg01.setFlushUpdates(false);
user01.setFlushUpdates(false);
//add intOrg to user
user01.setInternalOrganization(intOrg01);
//flush entityManager
//THIS triggers an insert of intOrg ! (no update to the User table)
entityManager.flush();
---- our custom impl of findDirty (working fine - verified)
Code:
@Override
public int[] findDirty(Object entity, Serializable id, Object[] currentState, Object[] previousState,
String[] propertyNames, Type[] types) {
/**
* logic logic
*/
if (entity instanceof AbstractBO) {
if (((AbstractBO) entity).isFlushUpdates()) {
return null;
} else {
logger.info("Ignoring flush for object: " + entity.getClass().getSimpleName() + ", "
+ entity.toString());
return new int[0];
}
}
}