targetAuditMode=RelationTargetAuditMode.NOT_AUDITED should allow for missing entity targets
Description
causes
is a fix for
relates to
Activity
Chris Cranford February 17, 2020 at 8:59 PM
It did but unfortunately what is now wip/6.0
is not the same branch in which I worked on before I moved to the DBZ team. I will re-prepare the fix against master and submit a new PR.
Former user February 13, 2020 at 9:51 PM
@Chris Cranford, it doesn't look like the fix made it to 6.0.alpha1...
Steve Ebersole December 6, 2018 at 3:43 PM
Preparing Alpha1 release
Chris Cranford April 12, 2017 at 5:05 PM
Upon testing this in action, I believe I'd rather see a few slight changes.
First, the RelationTargetNotFoundAction
enum will maintain 3 values rather than 2:
public enum RelationTargetNotFoundAction {
// Use the default identified by EnversSettings#GLOBAL_RELATION_NOT_FOUND_LEGACY_FLAG.
DEFAULT,
// Throw an error should one occur if the target relation doesn't exist.
ERROR,
// Ignore the error should one occur if the target relation doesn't exist.
IGNORE
}
Secondly, the Audited
annotation will use a different default:
public @interface Audited {
/**
* Specifies if the entity that is the target isn't found how should we react.
* The default is to ignore the failure if the target entity doesn't exist.
*/
@Incubating
RelationTargetNotFoundAction targetNotFoundAction() default RelationTargetNotFoundAction.DEFAULT;
}
Lastly, the configuration property:
/**
* Specifies whether the legacy not-found behavior of throwing an EntityNotFoundException should be used.
* Defaults to {@code true}.
*/
String GLOBAL_RELATION_NOT_FOUND_LEGACY_FLAG = "org.hibernate.envers.global_relation_not_found_legacy_flag";
What this leads to is that users upgrading to 6.0 will see no change in behavior. The EntityNotFoundException
will continue to be thrown without users having to make a single change to their classes, annotations, or configurations. This allows easy backward compatibility but flexibility for users to engage in the new behavior as needed.
So in order to actually ignore these EntityNotFoundException cases, users have several options.
The first is to enable the feature globally. Users can do that by changing the org.hibernate.envers.global_relation_not_found_legacy_flag
to false
. In other words, the default enum value of DEFAULT
directly translates to whether legacy behavior is ON or OFF based on the configuration setting.
The second option is to enable the feature for a specific entity class by specifying the targetNotFoundAction
attribute at the class level's @Audited
annotation. This means if the property uses the DEFAULT
value but the class specifies IGNORE
or ERROR
, the properties will use the value defined at the class level. The only exception is for cases where your property defines itself explicitly as IGNORE
or ERROR
and then the class-level value won't have precedence.
And obviously the third is to enable the feature by specifying the targetNotFoundAction
attribute at the property level's @Audited
annotation as mentioned above.
Users can also mix-n-match the global configuration property & annotation values as needed to address their individual needs.
Chris Cranford April 11, 2017 at 7:49 PM
I have also decided to add the following configuration setting:
/**
* Globally specifies whether to gracefully handle missing to-one relations.
* Defaults to {@code true}.
*
* By specifying this flag as {@code false}, you restore legacy behavior without having to
* change all your {@code Audited} annotations which use this feature by default.
*
* @since 6.0
*/
String GLOBAL_RELATION_NOT_FOUND_FLAG = "org.hibernate.envers.global_relation_not_found_flag";
By default, Envers will assume the IGNORE behavior. This means any @Audited
to-one property will be handled to be ignored if not found. But some users may prefer the legacy behavior without having to modify all their @Audited
annotations and rather be explicit about the cases where they'd like IGNORE to be used. For those situations, users can set this global configuration to false
to invert the default behavior to do precisely that.
If you use targetAuditMode=RelationTargetAuditMode.NOT_AUDITED on a relation and delete the (non-audited) target entity, queries for audits that contain links to the deleted target entity will throw an exception. Where it is thrown depends on how the source entity is implemented: if it implements hashCode() and includes the target entity in the hash, it'll be deep within the query itself.
Here's a concrete example:
class Foo { @Audited(withModifiedFlag=true) String interestingField; @Audited(targetAuditMode=RelationTargetAuditMode.NOT_AUDITED) Bar myBar; public boolean equals(Object obj) { // compares interestingField and myBar } public int hashCode() { // hashes interestingField and myBar } } class Bar { ... }
Now suppose we issue three transactions:
Transaction 1 creates and persists a Foo and a Bar.
Transaction 2 deletes both the Foo and the Bar.
Transaction 3 queries for all revisions.
The query will throw an exception like so:
javax.persistence.EntityNotFoundException: Unable to find Bar with id 1 at org.hibernate.ejb.Ejb3Configuration$Ejb3EntityNotFoundDelegate.handleEntityNotFound(Ejb3Configuration.java:155) at org.hibernate.proxy.AbstractLazyInitializer.checkTargetState(AbstractLazyInitializer.java:171) at org.hibernate.proxy.AbstractLazyInitializer.initialize(AbstractLazyInitializer.java:160) at org.hibernate.proxy.AbstractLazyInitializer.getImplementation(AbstractLazyInitializer.java:195) at org.hibernate.proxy.pojo.javassist.JavassistLazyInitializer.invoke(JavassistLazyInitializer.java:185) ... at org.hibernate.envers.tools.Triple.hashCode(Triple.java:74) at java.util.HashMap.put(HashMap.java:372) at org.hibernate.envers.reader.FirstLevelCache.putOnEntityNameCache(FirstLevelCache.java:87) at org.hibernate.envers.entities.EntityInstantiator.createInstanceFromVersionsEntity(EntityInstantiator.java:104) at org.hibernate.envers.query.impl.RevisionsOfEntityQuery.list(RevisionsOfEntityQuery.java:134) at org.hibernate.envers.query.impl.AbstractAuditQuery.getResultList(AbstractAuditQuery.java:105) ...
It would be nice to fail gracefully in the face of non-existent entity targets, by nullifying the relation, perhaps.