Merge pull request #244 from lukasz-antoniak/HHH-4439

HHH-4439 - Override auditing behavior of properties defined in @MappedSuperclass
This commit is contained in:
lukasz-antoniak 2012-01-09 12:43:45 -08:00
commit 0efd1dd886
18 changed files with 1139 additions and 37 deletions

View File

@ -292,8 +292,10 @@
</para> </para>
<para> <para>
If you'd like to override auditing behaviour of some fields/properties in an embedded component, you can use If you'd like to override auditing behaviour of some fields/properties inherited from
the <literal>@AuditOverride(s)</literal> annotation on the usage site of the component. <interfacename>@Mappedsuperclass</interfacename> or in an embedded component, you can
apply the <literal>@AuditOverride(s)</literal> annotation on the subtype or usage site
of the component.
</para> </para>
<para> <para>
@ -311,9 +313,10 @@
<para> <para>
If you'd like to audit properties of a superclass of an entity, which are not explicitly audited (which If you'd like to audit properties of a superclass of an entity, which are not explicitly audited (which
don't have the <literal>@Audited</literal> annotation on any properties or on the class), don't have the <literal>@Audited</literal> annotation on any properties or on the class), you can list the
you can list the superclasses in the <literal>auditParents</literal> attribute of the superclasses in the <literal>auditParents</literal> attribute of the <interfacename>@Audited</interfacename>
<interfacename>@Audited</interfacename> annotation. annotation. Please note that <literal>auditParents</literal> feature has been deprecated. Use
<literal>@AuditOverride(forClass = SomeEntity.class, isAudited = true/false)</literal> instead.
</para> </para>
</section> </section>

View File

@ -1,6 +1,7 @@
package org.hibernate.envers; package org.hibernate.envers;
import java.lang.annotation.Retention; import java.lang.annotation.Retention;
import java.lang.annotation.Target; import java.lang.annotation.Target;
import javax.persistence.MappedSuperclass;
import static java.lang.annotation.ElementType.FIELD; import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.ElementType.METHOD; import static java.lang.annotation.ElementType.METHOD;
@ -9,9 +10,11 @@ import static java.lang.annotation.RetentionPolicy.RUNTIME;
/** /**
* The {@code AuditingOverride} annotation is used to override the auditing * The {@code AuditingOverride} annotation is used to override the auditing
* behavior of a field (or property) inside an embedded component. * behavior of a superclass or single property inherited from {@link MappedSuperclass}
* type, or attribute inside an embedded component.
* *
* @author Erik-Berndt Scheper * @author Erik-Berndt Scheper
* @author Lukasz Antoniak (lukasz dot antoniak at gmail dot com)
* @see javax.persistence.Embedded * @see javax.persistence.Embedded
* @see javax.persistence.Embeddable * @see javax.persistence.Embeddable
* @see javax.persistence.MappedSuperclass * @see javax.persistence.MappedSuperclass
@ -23,10 +26,11 @@ import static java.lang.annotation.RetentionPolicy.RUNTIME;
public @interface AuditOverride { public @interface AuditOverride {
/** /**
* @return <strong>Required</strong> Name of the field (or property) whose mapping * @return Name of the field (or property) whose mapping is being overridden. Allows empty value if
* is being overridden. * {@link AuditOverride} is used to change auditing behavior of all attributes inherited from
* {@link MappedSuperclass} type.
*/ */
String name(); String name() default "";
/** /**
* @return Indicates if the field (or property) is audited; defaults to {@code true}. * @return Indicates if the field (or property) is audited; defaults to {@code true}.
@ -38,4 +42,11 @@ public @interface AuditOverride {
* is ignored if {@link #isAudited()} equals to {@code false}. * is ignored if {@link #isAudited()} equals to {@code false}.
*/ */
AuditJoinTable auditJoinTable() default @AuditJoinTable; AuditJoinTable auditJoinTable() default @AuditJoinTable;
/**
* @return Specifies class which field (or property) mapping is being overridden. <strong>Required</strong> if
* {@link AuditOverride} is used to change auditing behavior of attributes inherited from {@link MappedSuperclass}
* type.
*/
Class forClass() default void.class;
} }

View File

@ -41,7 +41,7 @@ public @interface Audited {
/** /**
* @return Specifies if the entity that is the target of the relation should be audited or not. If not, then when * @return Specifies if the entity that is the target of the relation should be audited or not. If not, then when
* reading a historic version an audited entity, the realtion will always point to the "current" entity. * reading a historic version an audited entity, the relation will always point to the "current" entity.
* This is useful for dictionary-like entities, which don't change and don't need to be audited. * This is useful for dictionary-like entities, which don't change and don't need to be audited.
*/ */
RelationTargetAuditMode targetAuditMode() default RelationTargetAuditMode.AUDITED; RelationTargetAuditMode targetAuditMode() default RelationTargetAuditMode.AUDITED;
@ -55,6 +55,8 @@ public @interface Audited {
* *
* If a parent type lists any of its parent types using this attribute, all properties in the specified classes * If a parent type lists any of its parent types using this attribute, all properties in the specified classes
* will also be audited. * will also be audited.
*
* @deprecated Use {@code @AuditOverride(forClass=SomeEntity.class)} instead.
*/ */
Class[] auditParents() default {}; Class[] auditParents() default {};
} }

View File

@ -1,6 +1,7 @@
package org.hibernate.envers.configuration.metadata.reader; package org.hibernate.envers.configuration.metadata.reader;
import java.lang.annotation.Annotation; import java.lang.annotation.Annotation;
import java.util.HashSet; import java.util.Arrays;
import java.util.Collections;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@ -20,8 +21,11 @@ import org.hibernate.envers.AuditOverrides;
import org.hibernate.envers.Audited; import org.hibernate.envers.Audited;
import org.hibernate.envers.ModificationStore; import org.hibernate.envers.ModificationStore;
import org.hibernate.envers.NotAudited; import org.hibernate.envers.NotAudited;
import org.hibernate.envers.RelationTargetAuditMode;
import org.hibernate.envers.configuration.GlobalConfiguration; import org.hibernate.envers.configuration.GlobalConfiguration;
import org.hibernate.envers.tools.MappingTools; import org.hibernate.envers.tools.MappingTools;
import org.hibernate.envers.tools.StringTools;
import org.hibernate.envers.tools.Tools;
import org.hibernate.mapping.Component; import org.hibernate.mapping.Component;
import org.hibernate.mapping.Property; import org.hibernate.mapping.Property;
import org.hibernate.mapping.Value; import org.hibernate.mapping.Value;
@ -53,6 +57,12 @@ public class AuditedPropertiesReader {
// Mapping class field to corresponding <properties> element. // Mapping class field to corresponding <properties> element.
private final Map<String, String> propertiesGroupMapping; private final Map<String, String> propertiesGroupMapping;
private final Set<XProperty> overriddenAuditedProperties;
private final Set<XProperty> overriddenNotAuditedProperties;
private final Set<XClass> overriddenAuditedClasses;
private final Set<XClass> overriddenNotAuditedClasses;
public AuditedPropertiesReader(ModificationStore defaultStore, public AuditedPropertiesReader(ModificationStore defaultStore,
PersistentPropertiesSource persistentPropertiesSource, PersistentPropertiesSource persistentPropertiesSource,
AuditedPropertiesHolder auditedPropertiesHolder, AuditedPropertiesHolder auditedPropertiesHolder,
@ -69,44 +79,106 @@ public class AuditedPropertiesReader {
propertyAccessedPersistentProperties = newHashSet(); propertyAccessedPersistentProperties = newHashSet();
fieldAccessedPersistentProperties = newHashSet(); fieldAccessedPersistentProperties = newHashSet();
propertiesGroupMapping = newHashMap(); propertiesGroupMapping = newHashMap();
overriddenAuditedProperties = newHashSet();
overriddenNotAuditedProperties = newHashSet();
overriddenAuditedClasses = newHashSet();
overriddenNotAuditedClasses = newHashSet();
} }
public void read() { public void read() {
// First reading the access types for the persistent properties. // First reading the access types for the persistent properties.
readPersistentPropertiesAccess(); readPersistentPropertiesAccess();
// Retrieve classes that are explicitly marked for auditing process by any superclass of currently mapped // Retrieve classes and properties that are explicitly marked for auditing process by any superclass
// entity or itself. // of currently mapped entity or itself.
XClass clazz = persistentPropertiesSource.getXClass(); XClass clazz = persistentPropertiesSource.getXClass();
Set<XClass> declaredAuditedSuperclasses = new HashSet<XClass>(); readAuditOverrides(clazz);
doGetDeclaredAuditedSuperclasses(clazz, declaredAuditedSuperclasses);
// Adding all properties from the given class. // Adding all properties from the given class.
addPropertiesFromClass(clazz, declaredAuditedSuperclasses); addPropertiesFromClass(clazz);
} }
/** /**
* Recursively constructs a set of classes that have been declared for auditing process. * Recursively constructs sets of audited and not audited properties and classes which behavior has been overridden
* using {@link AuditOverride} annotation.
* @param clazz Class that is being processed. Currently mapped entity shall be passed during first invocation. * @param clazz Class that is being processed. Currently mapped entity shall be passed during first invocation.
* @param declaredAuditedSuperclasses Total collection of classes listed in {@link Audited#auditParents()} property
* by any superclass starting with class specified as the first argument.
*/ */
@SuppressWarnings("unchecked") private void readAuditOverrides(XClass clazz) {
private void doGetDeclaredAuditedSuperclasses(XClass clazz, Set<XClass> declaredAuditedSuperclasses) { /* TODO: Code to remove with @Audited.auditParents - start. */
Audited allClassAudited = clazz.getAnnotation(Audited.class); Audited allClassAudited = clazz.getAnnotation(Audited.class);
if (allClassAudited != null && allClassAudited.auditParents().length > 0) { if (allClassAudited != null && allClassAudited.auditParents().length > 0) {
for (Class c : allClassAudited.auditParents()) { for (Class c : allClassAudited.auditParents()) {
XClass parentClass = reflectionManager.toXClass(c); XClass parentClass = reflectionManager.toXClass(c);
checkSuperclass(clazz, parentClass); checkSuperclass(clazz, parentClass);
declaredAuditedSuperclasses.add(parentClass); if (!overriddenNotAuditedClasses.contains(parentClass)) {
// If the class has not been marked as not audited by the subclass.
overriddenAuditedClasses.add(parentClass);
}
}
}
/* TODO: Code to remove with @Audited.auditParents - finish. */
List<AuditOverride> auditOverrides = computeAuditOverrides(clazz);
for (AuditOverride auditOverride : auditOverrides) {
if (auditOverride.forClass() != void.class) {
XClass overrideClass = reflectionManager.toXClass(auditOverride.forClass());
checkSuperclass(clazz, overrideClass);
String propertyName = auditOverride.name();
if (!StringTools.isEmpty(propertyName)) {
// Override @Audited annotation on property level.
XProperty property = getProperty(overrideClass, propertyName);
if (auditOverride.isAudited()) {
if (!overriddenNotAuditedProperties.contains(property)) {
// If the property has not been marked as not audited by the subclass.
overriddenAuditedProperties.add(property);
}
} else {
if (!overriddenAuditedProperties.contains(property)) {
// If the property has not been marked as audited by the subclass.
overriddenNotAuditedProperties.add(property);
}
}
} else {
// Override @Audited annotation on class level.
if (auditOverride.isAudited()) {
if (!overriddenNotAuditedClasses.contains(overrideClass)) {
// If the class has not been marked as not audited by the subclass.
overriddenAuditedClasses.add(overrideClass);
}
} else {
if (!overriddenAuditedClasses.contains(overrideClass)) {
// If the class has not been marked as audited by the subclass.
overriddenNotAuditedClasses.add(overrideClass);
}
}
}
} }
} }
XClass superclass = clazz.getSuperclass(); XClass superclass = clazz.getSuperclass();
if (!clazz.isInterface() && !Object.class.getName().equals(superclass.getName())) { if (!clazz.isInterface() && !Object.class.getName().equals(superclass.getName())) {
doGetDeclaredAuditedSuperclasses(superclass, declaredAuditedSuperclasses); readAuditOverrides(superclass);
} }
} }
/**
* @param clazz Source class.
* @return List of @AuditOverride annotations applied at class level.
*/
private List<AuditOverride> computeAuditOverrides(XClass clazz) {
AuditOverrides auditOverrides = clazz.getAnnotation(AuditOverrides.class);
AuditOverride auditOverride = clazz.getAnnotation(AuditOverride.class);
if (auditOverrides == null && auditOverride != null) {
return Arrays.asList(auditOverride);
} else if (auditOverrides != null && auditOverride == null) {
return Arrays.asList(auditOverrides.value());
} else if (auditOverrides != null && auditOverride != null) {
throw new MappingException("@AuditOverrides annotation should encapsulate all @AuditOverride declarations. " +
"Please revise Envers annotations applied to class " + clazz.getName() + ".");
}
return Collections.EMPTY_LIST;
}
/** /**
* Checks whether one class is assignable from another. If not {@link MappingException} is thrown. * Checks whether one class is assignable from another. If not {@link MappingException} is thrown.
* @param child Subclass. * @param child Subclass.
@ -115,10 +187,25 @@ public class AuditedPropertiesReader {
private void checkSuperclass(XClass child, XClass parent) { private void checkSuperclass(XClass child, XClass parent) {
if (!parent.isAssignableFrom(child)) { if (!parent.isAssignableFrom(child)) {
throw new MappingException("Class " + parent.getName() + " is not assignable from " + child.getName() + ". " + throw new MappingException("Class " + parent.getName() + " is not assignable from " + child.getName() + ". " +
"Please revise @Audited.auditParents value in " + child.getName() + " type."); "Please revise Envers annotations applied to " + child.getName() + " type.");
} }
} }
/**
* Checks whether class contains property with a given name. If not {@link MappingException} is thrown.
* @param clazz Class.
* @param propertyName Property name.
* @return Property object.
*/
private XProperty getProperty(XClass clazz, String propertyName) {
XProperty property = Tools.getProperty(clazz, propertyName);
if (property == null) {
throw new MappingException("Property '" + propertyName + "' not found in class " + clazz.getName() + ". " +
"Please revise Envers annotations applied to class " + persistentPropertiesSource.getXClass() + ".");
}
return property;
}
private void readPersistentPropertiesAccess() { private void readPersistentPropertiesAccess() {
Iterator<Property> propertyIter = persistentPropertiesSource.getPropertyIterator(); Iterator<Property> propertyIter = persistentPropertiesSource.getPropertyIterator();
while (propertyIter.hasNext()) { while (propertyIter.hasNext()) {
@ -151,19 +238,26 @@ public class AuditedPropertiesReader {
/** /**
* @param clazz Class which properties are currently being added. * @param clazz Class which properties are currently being added.
* @param declaredAuditedSuperclasses Collection of superclasses that have been explicitly declared to be audited.
* @return {@link Audited} annotation of specified class. If processed type hasn't been explicitly marked, method * @return {@link Audited} annotation of specified class. If processed type hasn't been explicitly marked, method
* checks whether given class exists in collection passed as the second argument. In case of success, * checks whether given class exists in {@link AuditedPropertiesReader#overriddenAuditedClasses} collection.
* {@link Audited} configuration of currently mapped entity is returned, otherwise {@code null}. * In case of success, {@link Audited} configuration of currently mapped entity is returned, otherwise
* {@code null}. If processed type exists in {@link AuditedPropertiesReader#overriddenNotAuditedClasses}
* collection, the result is also {@code null}.
*/ */
private Audited computeAuditConfiguration(XClass clazz, Set<XClass> declaredAuditedSuperclasses) { private Audited computeAuditConfiguration(XClass clazz) {
Audited allClassAudited = clazz.getAnnotation(Audited.class); Audited allClassAudited = clazz.getAnnotation(Audited.class);
// If processed class is not explicitly marked with @Audited annotation, check whether auditing is // If processed class is not explicitly marked with @Audited annotation, check whether auditing is
// forced by any of its child entities configuration (@Audited.auditParents). // forced by any of its child entities configuration (@AuditedOverride.forClass).
if (allClassAudited == null && declaredAuditedSuperclasses.contains(clazz)) { if (allClassAudited == null && overriddenAuditedClasses.contains(clazz)) {
// Declared audited parent copies @Audited.modStore and @Audited.targetAuditMode configuration from // Declared audited parent copies @Audited.modStore and @Audited.targetAuditMode configuration from
// currently mapped entity. // currently mapped entity.
allClassAudited = persistentPropertiesSource.getXClass().getAnnotation(Audited.class); allClassAudited = persistentPropertiesSource.getXClass().getAnnotation(Audited.class);
if (allClassAudited == null) {
// If parent class declares @Audited on the field/property level.
allClassAudited = DEFAULT_AUDITED;
}
} else if (allClassAudited != null && overriddenNotAuditedClasses.contains(clazz)) {
return null;
} }
return allClassAudited; return allClassAudited;
} }
@ -171,11 +265,9 @@ public class AuditedPropertiesReader {
/** /**
* Recursively adds all audited properties of entity class and its superclasses. * Recursively adds all audited properties of entity class and its superclasses.
* @param clazz Currently processed class. * @param clazz Currently processed class.
* @param declaredAuditedSuperclasses Collection of classes that are declared to be audited
* (see {@link Audited#auditParents()}).
*/ */
private void addPropertiesFromClass(XClass clazz, Set<XClass> declaredAuditedSuperclasses) { private void addPropertiesFromClass(XClass clazz) {
Audited allClassAudited = computeAuditConfiguration(clazz, declaredAuditedSuperclasses); Audited allClassAudited = computeAuditConfiguration(clazz);
//look in the class //look in the class
addFromProperties(clazz.getDeclaredProperties("field"), "field", fieldAccessedPersistentProperties, allClassAudited); addFromProperties(clazz.getDeclaredProperties("field"), "field", fieldAccessedPersistentProperties, allClassAudited);
@ -184,7 +276,7 @@ public class AuditedPropertiesReader {
if(allClassAudited != null || !auditedPropertiesHolder.isEmpty()) { if(allClassAudited != null || !auditedPropertiesHolder.isEmpty()) {
XClass superclazz = clazz.getSuperclass(); XClass superclazz = clazz.getSuperclass();
if (!clazz.isInterface() && !"java.lang.Object".equals(superclazz.getName())) { if (!clazz.isInterface() && !"java.lang.Object".equals(superclazz.getName())) {
addPropertiesFromClass(superclazz, declaredAuditedSuperclasses); addPropertiesFromClass(superclazz);
} }
} }
} }
@ -283,7 +375,7 @@ public class AuditedPropertiesReader {
// check if a property is declared as not audited to exclude it // check if a property is declared as not audited to exclude it
// useful if a class is audited but some properties should be excluded // useful if a class is audited but some properties should be excluded
NotAudited unVer = property.getAnnotation(NotAudited.class); NotAudited unVer = property.getAnnotation(NotAudited.class);
if (unVer != null) { if ((unVer != null && !overriddenAuditedProperties.contains(property)) || overriddenNotAuditedProperties.contains(property)) {
return false; return false;
} else { } else {
// if the optimistic locking field has to be unversioned and the current property // if the optimistic locking field has to be unversioned and the current property
@ -322,7 +414,11 @@ public class AuditedPropertiesReader {
PropertyAuditingData propertyData, Audited allClassAudited) { PropertyAuditingData propertyData, Audited allClassAudited) {
// Checking if this property is explicitly audited or if all properties are. // Checking if this property is explicitly audited or if all properties are.
Audited aud = (property.isAnnotationPresent(Audited.class)) ? (property.getAnnotation(Audited.class)) : allClassAudited; Audited aud = (property.isAnnotationPresent(Audited.class)) ? (property.getAnnotation(Audited.class)) : allClassAudited;
//Audited aud = property.getAnnotation(Audited.class); if (aud == null && overriddenAuditedProperties.contains(property) && !overriddenNotAuditedProperties.contains(property)) {
// Assigning @Audited defaults. If anyone needs to customize those values in the future,
// appropriate fields shall be added to @AuditOverride annotation.
aud = DEFAULT_AUDITED;
}
if (aud != null) { if (aud != null) {
propertyData.setStore(aud.modStore()); propertyData.setStore(aud.modStore());
propertyData.setRelationTargetAuditMode(aud.targetAuditMode()); propertyData.setRelationTargetAuditMode(aud.targetAuditMode());
@ -407,6 +503,13 @@ public class AuditedPropertiesReader {
return true; return true;
} }
private static Audited DEFAULT_AUDITED = new Audited() {
public ModificationStore modStore() { return ModificationStore.FULL; }
public RelationTargetAuditMode targetAuditMode() { return RelationTargetAuditMode.AUDITED; }
public Class[] auditParents() { return new Class[0]; }
public Class<? extends Annotation> annotationType() { return this.getClass(); }
};
private static AuditJoinTable DEFAULT_AUDIT_JOIN_TABLE = new AuditJoinTable() { private static AuditJoinTable DEFAULT_AUDIT_JOIN_TABLE = new AuditJoinTable() {
public String name() { return ""; } public String name() { return ""; }
public String schema() { return ""; } public String schema() { return ""; }

View File

@ -35,6 +35,8 @@ import java.util.Set;
import javassist.util.proxy.ProxyFactory; import javassist.util.proxy.ProxyFactory;
import org.hibernate.Session; import org.hibernate.Session;
import org.hibernate.annotations.common.reflection.XClass;
import org.hibernate.annotations.common.reflection.XProperty;
import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.engine.spi.SessionImplementor; import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.persister.entity.EntityPersister; import org.hibernate.persister.entity.EntityPersister;
@ -215,4 +217,32 @@ public class Tools {
} }
return ret; return ret;
} }
/**
* @param clazz Source class.
* @param propertyName Property name.
* @return Property object or {@code null} if none with expected name has been found.
*/
public static XProperty getProperty(XClass clazz, String propertyName) {
XProperty property = getProperty(clazz, propertyName, "field");
if (property == null) {
property = getProperty(clazz, propertyName, "property");
}
return property;
}
/**
* @param clazz Source class.
* @param propertyName Property name.
* @param accessType Expected access type. Legal values are <i>field</i> and <i>property</i>.
* @return Property object or {@code null} if none with expected name and access type has been found.
*/
public static XProperty getProperty(XClass clazz, String propertyName, String accessType) {
for (XProperty property : clazz.getDeclaredProperties(accessType)) {
if (propertyName.equals(property.getName())) {
return property;
}
}
return null;
}
} }

View File

@ -0,0 +1,78 @@
package org.hibernate.envers.test.integration.superclass.auditoverride;
import org.hibernate.ejb.Ejb3Configuration;
import org.hibernate.envers.test.AbstractEntityTest;
import org.hibernate.envers.test.Priority;
import org.hibernate.mapping.Column;
import org.hibernate.mapping.Table;
import org.hibernate.testing.TestForIssue;
import org.junit.Assert;
import org.junit.Test;
import javax.persistence.EntityManager;
/**
* @author Lukasz Antoniak (lukasz dot antoniak at gmail dot com)
*/
@TestForIssue(jiraKey = "HHH-4439")
public class AuditClassOverrideTest extends AbstractEntityTest {
private Integer classAuditedEntityId = null;
private Integer classNotAuditedEntityId = null;
private Table classAuditedTable = null;
private Table classNotAuditedTable = null;
@Override
public void configure(Ejb3Configuration cfg) {
cfg.addAnnotatedClass(ClassOverrideAuditedEntity.class);
cfg.addAnnotatedClass(ClassOverrideNotAuditedEntity.class);
}
@Test
@Priority(10)
public void initData() {
EntityManager em = getEntityManager();
// Revision 1
em.getTransaction().begin();
ClassOverrideAuditedEntity classOverrideAuditedEntity = new ClassOverrideAuditedEntity("data 1", 1, "data 2");
em.persist(classOverrideAuditedEntity);
em.getTransaction().commit();
classAuditedEntityId = classOverrideAuditedEntity.getId();
// Revision 2
em.getTransaction().begin();
ClassOverrideNotAuditedEntity classOverrideNotAuditedEntity = new ClassOverrideNotAuditedEntity("data 1", 1, "data 2");
em.persist(classOverrideNotAuditedEntity);
em.getTransaction().commit();
classNotAuditedEntityId = classOverrideNotAuditedEntity.getId();
classAuditedTable = getCfg().getClassMapping("org.hibernate.envers.test.integration.superclass.auditoverride.ClassOverrideAuditedEntity_AUD").getTable();
classNotAuditedTable = getCfg().getClassMapping("org.hibernate.envers.test.integration.superclass.auditoverride.ClassOverrideNotAuditedEntity_AUD").getTable();
}
@Test
public void testAuditedProperty() {
Assert.assertNotNull(classAuditedTable.getColumn(new Column("number1")));
Assert.assertNotNull(classAuditedTable.getColumn(new Column("str1")));
Assert.assertNotNull(classAuditedTable.getColumn(new Column("str2")));
Assert.assertNotNull(classNotAuditedTable.getColumn(new Column("str2")));
}
@Test
public void testNotAuditedProperty() {
Assert.assertNull(classNotAuditedTable.getColumn(new Column("number1")));
Assert.assertNull(classNotAuditedTable.getColumn(new Column("str1")));
}
@Test
public void testHistoryOfClassOverrideAuditedEntity() {
ClassOverrideAuditedEntity ver1 = new ClassOverrideAuditedEntity("data 1", 1, classAuditedEntityId, "data 2");
Assert.assertEquals(ver1, getAuditReader().find(ClassOverrideAuditedEntity.class, classAuditedEntityId, 1));
}
@Test
public void testHistoryOfClassOverrideNotAuditedEntity() {
ClassOverrideNotAuditedEntity ver1 = new ClassOverrideNotAuditedEntity(null, null, classNotAuditedEntityId, "data 2");
Assert.assertEquals(ver1, getAuditReader().find(ClassOverrideNotAuditedEntity.class, classNotAuditedEntityId, 2));
}
}

View File

@ -0,0 +1,99 @@
package org.hibernate.envers.test.integration.superclass.auditoverride;
import org.hibernate.ejb.Ejb3Configuration;
import org.hibernate.envers.test.AbstractEntityTest;
import org.hibernate.envers.test.Priority;
import org.hibernate.mapping.Column;
import org.hibernate.mapping.Table;
import org.hibernate.testing.TestForIssue;
import org.junit.Assert;
import org.junit.Test;
import javax.persistence.EntityManager;
/**
* @author Lukasz Antoniak (lukasz dot antoniak at gmail dot com)
*/
@TestForIssue(jiraKey = "HHH-4439")
public class AuditPropertyOverrideTest extends AbstractEntityTest {
private Integer propertyEntityId = null;
private Integer transitiveEntityId = null;
private Integer auditedEntityId = null;
private Table propertyTable = null;
private Table transitiveTable = null;
private Table auditedTable = null;
@Override
public void configure(Ejb3Configuration cfg) {
cfg.addAnnotatedClass(PropertyOverrideEntity.class);
cfg.addAnnotatedClass(TransitiveOverrideEntity.class);
cfg.addAnnotatedClass(AuditedSpecialEntity.class);
}
@Test
@Priority(10)
public void initData() {
EntityManager em = getEntityManager();
// Revision 1
em.getTransaction().begin();
PropertyOverrideEntity propertyEntity = new PropertyOverrideEntity("data 1", 1, "data 2");
em.persist(propertyEntity);
em.getTransaction().commit();
propertyEntityId = propertyEntity.getId();
// Revision 2
em.getTransaction().begin();
TransitiveOverrideEntity transitiveEntity = new TransitiveOverrideEntity("data 1", 1, "data 2", 2, "data 3");
em.persist(transitiveEntity);
em.getTransaction().commit();
transitiveEntityId = transitiveEntity.getId();
// Revision 3
em.getTransaction().begin();
AuditedSpecialEntity auditedEntity = new AuditedSpecialEntity("data 1", 1, "data 2");
em.persist(auditedEntity);
em.getTransaction().commit();
auditedEntityId = auditedEntity.getId();
propertyTable = getCfg().getClassMapping("org.hibernate.envers.test.integration.superclass.auditoverride.PropertyOverrideEntity_AUD").getTable();
transitiveTable = getCfg().getClassMapping("org.hibernate.envers.test.integration.superclass.auditoverride.TransitiveOverrideEntity_AUD").getTable();
auditedTable = getCfg().getClassMapping("org.hibernate.envers.test.integration.superclass.auditoverride.AuditedSpecialEntity_AUD").getTable();
}
@Test
public void testNotAuditedProperty() {
Assert.assertNull(propertyTable.getColumn(new Column("str1")));
}
@Test
public void testAuditedProperty() {
Assert.assertNotNull(propertyTable.getColumn(new Column("number1")));
Assert.assertNotNull(transitiveTable.getColumn(new Column("number2")));
Assert.assertNotNull(auditedTable.getColumn(new Column("str1")));
}
@Test
public void testTransitiveAuditedProperty() {
Assert.assertNotNull(transitiveTable.getColumn(new Column("number1")));
Assert.assertNotNull(transitiveTable.getColumn(new Column("str1")));
}
@Test
public void testHistoryOfPropertyOverrideEntity() {
PropertyOverrideEntity ver1 = new PropertyOverrideEntity(null, 1, propertyEntityId, "data 2");
Assert.assertEquals(ver1, getAuditReader().find(PropertyOverrideEntity.class, propertyEntityId, 1));
}
@Test
public void testHistoryOfTransitiveOverrideEntity() {
TransitiveOverrideEntity ver1 = new TransitiveOverrideEntity("data 1", 1, transitiveEntityId, "data 2", 2, "data 3");
Assert.assertEquals(ver1, getAuditReader().find(TransitiveOverrideEntity.class, transitiveEntityId, 2));
}
@Test
public void testHistoryOfAuditedSpecialEntity() {
AuditedSpecialEntity ver1 = new AuditedSpecialEntity("data 1", null, auditedEntityId, "data 2");
Assert.assertEquals(ver1, getAuditReader().find(AuditedSpecialEntity.class, auditedEntityId, 3));
}
}

View File

@ -0,0 +1,86 @@
package org.hibernate.envers.test.integration.superclass.auditoverride;
import org.hibernate.envers.Audited;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.MappedSuperclass;
import java.io.Serializable;
/**
* @author Lukasz Antoniak (lukasz dot antoniak at gmail dot com)
*/
@Audited
@MappedSuperclass
public class AuditedBaseEntity implements Serializable {
@Id
@GeneratedValue
private Integer id;
private String str1;
private Integer number1;
public AuditedBaseEntity() {
}
public AuditedBaseEntity(String str1, Integer number1, Integer id) {
this.id = id;
this.str1 = str1;
this.number1 = number1;
}
public AuditedBaseEntity(String str1, Integer number1) {
this.str1 = str1;
this.number1 = number1;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getStr1() {
return str1;
}
public void setStr1(String str1) {
this.str1 = str1;
}
public Integer getNumber1() {
return number1;
}
public void setNumber1(Integer number1) {
this.number1 = number1;
}
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof AuditedBaseEntity)) return false;
AuditedBaseEntity that = (AuditedBaseEntity) o;
if (id != null ? !id.equals(that.id) : that.id != null) return false;
if (number1 != null ? !number1.equals(that.number1) : that.number1 != null) return false;
if (str1 != null ? !str1.equals(that.str1) : that.str1 != null) return false;
return true;
}
public int hashCode() {
int result;
result = (id != null ? id.hashCode() : 0);
result = 31 * result + (str1 != null ? str1.hashCode() : 0);
result = 31 * result + (number1 != null ? number1.hashCode() : 0);
return result;
}
public String toString() {
return "AuditedBaseEntity(id = " + id + ", str1 = " + str1 + ", number1 = " + number1 + ")";
}
}

View File

@ -0,0 +1,63 @@
package org.hibernate.envers.test.integration.superclass.auditoverride;
import org.hibernate.envers.AuditOverride;
import org.hibernate.envers.AuditOverrides;
import org.hibernate.envers.Audited;
import javax.persistence.Entity;
/**
* @author Lukasz Antoniak (lukasz dot antoniak at gmail dot com)
*/
@Entity
@AuditOverrides({@AuditOverride(forClass = NotAuditedBaseEntity.class, name = "str1", isAudited = true)})
public class AuditedSpecialEntity extends NotAuditedBaseEntity {
@Audited
private String str2;
public AuditedSpecialEntity() {
}
public AuditedSpecialEntity(String str1, Integer number, String str2) {
super(str1, number);
this.str2 = str2;
}
public AuditedSpecialEntity(String str1, Integer number, Integer id, String str2) {
super(str1, number, id);
this.str2 = str2;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof AuditedSpecialEntity)) return false;
if (!super.equals(o)) return false;
AuditedSpecialEntity that = (AuditedSpecialEntity) o;
if (str2 != null ? !str2.equals(that.str2) : that.str2 != null) return false;
return true;
}
@Override
public int hashCode() {
int result = super.hashCode();
result = 31 * result + (str2 != null ? str2.hashCode() : 0);
return result;
}
@Override
public String toString() {
return "AuditedSpecialEntity(" + super.toString() + ", str2 = " + str2 + ")";
}
public String getStr2() {
return str2;
}
public void setStr2(String str2) {
this.str2 = str2;
}
}

View File

@ -0,0 +1,88 @@
package org.hibernate.envers.test.integration.superclass.auditoverride;
import org.hibernate.envers.Audited;
import org.hibernate.envers.NotAudited;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.MappedSuperclass;
import java.io.Serializable;
/**
* @author Lukasz Antoniak (lukasz dot antoniak at gmail dot com)
*/
@MappedSuperclass
public class BaseEntity implements Serializable {
@Id
@GeneratedValue
private Integer id;
@Audited
private String str1;
@NotAudited
private Integer number1;
public BaseEntity() {
}
public BaseEntity(String str1, Integer number1, Integer id) {
this.id = id;
this.str1 = str1;
this.number1 = number1;
}
public BaseEntity(String str1, Integer number1) {
this.str1 = str1;
this.number1 = number1;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getStr1() {
return str1;
}
public void setStr1(String str1) {
this.str1 = str1;
}
public Integer getNumber1() {
return number1;
}
public void setNumber1(Integer number1) {
this.number1 = number1;
}
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof BaseEntity)) return false;
BaseEntity that = (BaseEntity) o;
if (id != null ? !id.equals(that.id) : that.id != null) return false;
if (number1 != null ? !number1.equals(that.number1) : that.number1 != null) return false;
if (str1 != null ? !str1.equals(that.str1) : that.str1 != null) return false;
return true;
}
public int hashCode() {
int result;
result = (id != null ? id.hashCode() : 0);
result = 31 * result + (str1 != null ? str1.hashCode() : 0);
result = 31 * result + (number1 != null ? number1.hashCode() : 0);
return result;
}
public String toString() {
return "BaseEntity(id = " + id + ", str1 = " + str1 + ", number1 = " + number1 + ")";
}
}

View File

@ -0,0 +1,63 @@
package org.hibernate.envers.test.integration.superclass.auditoverride;
import org.hibernate.envers.AuditOverride;
import org.hibernate.envers.AuditOverrides;
import org.hibernate.envers.Audited;
import javax.persistence.Entity;
/**
* @author Lukasz Antoniak (lukasz.antoniak at gmail dot com)
*/
@Entity
@AuditOverrides({@AuditOverride(forClass = NotAuditedBaseEntity.class, isAudited = true)})
public class ClassOverrideAuditedEntity extends NotAuditedBaseEntity {
@Audited
private String str2;
public ClassOverrideAuditedEntity() {
}
public ClassOverrideAuditedEntity(String str1, Integer number, String str2) {
super(str1, number);
this.str2 = str2;
}
public ClassOverrideAuditedEntity(String str1, Integer number, Integer id, String str2) {
super(str1, number, id);
this.str2 = str2;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof ClassOverrideAuditedEntity)) return false;
if (!super.equals(o)) return false;
ClassOverrideAuditedEntity that = (ClassOverrideAuditedEntity) o;
if (str2 != null ? !str2.equals(that.str2) : that.str2 != null) return false;
return true;
}
@Override
public int hashCode() {
int result = super.hashCode();
result = 31 * result + (str2 != null ? str2.hashCode() : 0);
return result;
}
@Override
public String toString() {
return "ClassOverrideAuditedEntity(" + super.toString() + ", str2 = " + str2 + ")";
}
public String getStr2() {
return str2;
}
public void setStr2(String str2) {
this.str2 = str2;
}
}

View File

@ -0,0 +1,63 @@
package org.hibernate.envers.test.integration.superclass.auditoverride;
import org.hibernate.envers.AuditOverride;
import org.hibernate.envers.AuditOverrides;
import org.hibernate.envers.Audited;
import javax.persistence.Entity;
/**
* @author Lukasz Antoniak (lukasz.antoniak at gmail dot com)
*/
@Entity
@AuditOverrides({@AuditOverride(forClass = AuditedBaseEntity.class, isAudited = false)})
public class ClassOverrideNotAuditedEntity extends AuditedBaseEntity {
@Audited
private String str2;
public ClassOverrideNotAuditedEntity() {
}
public ClassOverrideNotAuditedEntity(String str1, Integer number, String str2) {
super(str1, number);
this.str2 = str2;
}
public ClassOverrideNotAuditedEntity(String str1, Integer number, Integer id, String str2) {
super(str1, number, id);
this.str2 = str2;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof ClassOverrideNotAuditedEntity)) return false;
if (!super.equals(o)) return false;
ClassOverrideNotAuditedEntity that = (ClassOverrideNotAuditedEntity) o;
if (str2 != null ? !str2.equals(that.str2) : that.str2 != null) return false;
return true;
}
@Override
public int hashCode() {
int result = super.hashCode();
result = 31 * result + (str2 != null ? str2.hashCode() : 0);
return result;
}
@Override
public String toString() {
return "ClassOverrideNotAuditedEntity(" + super.toString() + ", str2 = " + str2 + ")";
}
public String getStr2() {
return str2;
}
public void setStr2(String str2) {
this.str2 = str2;
}
}

View File

@ -0,0 +1,80 @@
package org.hibernate.envers.test.integration.superclass.auditoverride;
import org.hibernate.envers.AuditOverride;
import org.hibernate.envers.AuditOverrides;
import org.hibernate.envers.Audited;
import org.hibernate.envers.NotAudited;
import javax.persistence.MappedSuperclass;
/**
* @author Lukasz Antoniak (lukasz dot antoniak at gmail dot com)
*/
@MappedSuperclass
@AuditOverrides({@AuditOverride(forClass = BaseEntity.class, name = "str1", isAudited = false),
@AuditOverride(forClass = BaseEntity.class, name = "number1", isAudited = true)})
public class ExtendedBaseEntity extends BaseEntity {
@Audited
private String str2;
@NotAudited
private Integer number2;
public ExtendedBaseEntity() {
}
public ExtendedBaseEntity(String str1, Integer number1, Integer id, String str2, Integer number2) {
super(str1, number1, id);
this.str2 = str2;
this.number2 = number2;
}
public ExtendedBaseEntity(String str1, Integer number1, String str2, Integer number2) {
super(str1, number1);
this.str2 = str2;
this.number2 = number2;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof ExtendedBaseEntity)) return false;
if (!super.equals(o)) return false;
ExtendedBaseEntity that = (ExtendedBaseEntity) o;
if (number2 != null ? !number2.equals(that.number2) : that.number2 != null) return false;
if (str2 != null ? !str2.equals(that.str2) : that.str2 != null) return false;
return true;
}
@Override
public int hashCode() {
int result = super.hashCode();
result = 31 * result + (str2 != null ? str2.hashCode() : 0);
result = 31 * result + (number2 != null ? number2.hashCode() : 0);
return result;
}
@Override
public String toString() {
return "ExtendedBaseEntity(" + super.toString() + ", str2 = " + str2 + ", number2 = " + number2 + ")";
}
public String getStr2() {
return str2;
}
public void setStr2(String str2) {
this.str2 = str2;
}
public Integer getNumber2() {
return number2;
}
public void setNumber2(Integer number2) {
this.number2 = number2;
}
}

View File

@ -0,0 +1,64 @@
package org.hibernate.envers.test.integration.superclass.auditoverride;
import org.hibernate.envers.AuditOverride;
import org.hibernate.envers.AuditOverrides;
import org.hibernate.envers.Audited;
import javax.persistence.Entity;
/**
* @author Lukasz Antoniak (lukasz dot antoniak at gmail dot com)
*/
@Entity
@Audited
@AuditOverrides({@AuditOverride(forClass = AuditedBaseEntity.class, isAudited = false),
@AuditOverride(forClass = AuditedBaseEntity.class, name = "number1", isAudited = true)})
public class MixedOverrideEntity extends AuditedBaseEntity {
private String str2;
public MixedOverrideEntity() {
}
public MixedOverrideEntity(String str1, Integer number, String str2) {
super(str1, number);
this.str2 = str2;
}
public MixedOverrideEntity(String str1, Integer number, Integer id, String str2) {
super(str1, number, id);
this.str2 = str2;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof MixedOverrideEntity)) return false;
if (!super.equals(o)) return false;
MixedOverrideEntity that = (MixedOverrideEntity) o;
if (str2 != null ? !str2.equals(that.str2) : that.str2 != null) return false;
return true;
}
@Override
public int hashCode() {
int result = super.hashCode();
result = 31 * result + (str2 != null ? str2.hashCode() : 0);
return result;
}
@Override
public String toString() {
return "MixedOverrideEntity(" + super.toString() + ", str2 = " + str2 + ")";
}
public String getStr2() {
return str2;
}
public void setStr2(String str2) {
this.str2 = str2;
}
}

View File

@ -0,0 +1,58 @@
package org.hibernate.envers.test.integration.superclass.auditoverride;
import org.hibernate.ejb.Ejb3Configuration;
import org.hibernate.envers.test.AbstractEntityTest;
import org.hibernate.envers.test.Priority;
import org.hibernate.mapping.Column;
import org.hibernate.mapping.Table;
import org.hibernate.testing.TestForIssue;
import org.junit.Assert;
import org.junit.Test;
import javax.persistence.EntityManager;
/**
* @author Lukasz Antoniak (lukasz dot antoniak at gmail dot com)
*/
@TestForIssue(jiraKey = "HHH-4439")
public class MixedOverrideTest extends AbstractEntityTest {
private Integer mixedEntityId = null;
private Table mixedTable = null;
@Override
public void configure(Ejb3Configuration cfg) {
cfg.addAnnotatedClass(MixedOverrideEntity.class);
}
@Test
@Priority(10)
public void initData() {
EntityManager em = getEntityManager();
// Revision 1
em.getTransaction().begin();
MixedOverrideEntity mixedEntity = new MixedOverrideEntity("data 1", 1, "data 2");
em.persist(mixedEntity);
em.getTransaction().commit();
mixedEntityId = mixedEntity.getId();
mixedTable = getCfg().getClassMapping("org.hibernate.envers.test.integration.superclass.auditoverride.MixedOverrideEntity_AUD").getTable();
}
@Test
public void testAuditedProperty() {
Assert.assertNotNull(mixedTable.getColumn(new Column("number1")));
Assert.assertNotNull(mixedTable.getColumn(new Column("str2")));
}
@Test
public void testNotAuditedProperty() {
Assert.assertNull(mixedTable.getColumn(new Column("str1")));
}
@Test
public void testHistoryOfMixedEntity() {
MixedOverrideEntity ver1 = new MixedOverrideEntity(null, 1, mixedEntityId, "data 2");
Assert.assertEquals(ver1, getAuditReader().find(MixedOverrideEntity.class, mixedEntityId, 1));
}
}

View File

@ -0,0 +1,83 @@
package org.hibernate.envers.test.integration.superclass.auditoverride;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.MappedSuperclass;
import java.io.Serializable;
/**
* @author Lukasz Antoniak (lukasz dot antoniak at gmail dot com)
*/
@MappedSuperclass
public class NotAuditedBaseEntity implements Serializable {
@Id
@GeneratedValue
private Integer id;
private String str1;
private Integer number1;
public NotAuditedBaseEntity() {
}
public NotAuditedBaseEntity(String str1, Integer number1, Integer id) {
this.id = id;
this.str1 = str1;
this.number1 = number1;
}
public NotAuditedBaseEntity(String str1, Integer number1) {
this.str1 = str1;
this.number1 = number1;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getStr1() {
return str1;
}
public void setStr1(String str1) {
this.str1 = str1;
}
public Integer getNumber1() {
return number1;
}
public void setNumber1(Integer number1) {
this.number1 = number1;
}
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof NotAuditedBaseEntity)) return false;
NotAuditedBaseEntity that = (NotAuditedBaseEntity) o;
if (id != null ? !id.equals(that.id) : that.id != null) return false;
if (number1 != null ? !number1.equals(that.number1) : that.number1 != null) return false;
if (str1 != null ? !str1.equals(that.str1) : that.str1 != null) return false;
return true;
}
public int hashCode() {
int result;
result = (id != null ? id.hashCode() : 0);
result = 31 * result + (str1 != null ? str1.hashCode() : 0);
result = 31 * result + (number1 != null ? number1.hashCode() : 0);
return result;
}
public String toString() {
return "NotAuditedBaseEntity(id = " + id + ", str1 = " + str1 + ", number1 = " + number1 + ")";
}
}

View File

@ -0,0 +1,64 @@
package org.hibernate.envers.test.integration.superclass.auditoverride;
import org.hibernate.envers.AuditOverride;
import org.hibernate.envers.AuditOverrides;
import org.hibernate.envers.Audited;
import javax.persistence.Entity;
/**
* @author Lukasz Antoniak (lukasz dot antoniak at gmail dot com)
*/
@Entity
@Audited
@AuditOverrides({@AuditOverride(forClass = BaseEntity.class, name = "str1", isAudited = false),
@AuditOverride(forClass = BaseEntity.class, name = "number1", isAudited = true)})
public class PropertyOverrideEntity extends BaseEntity {
private String str2;
public PropertyOverrideEntity() {
}
public PropertyOverrideEntity(String str1, Integer number1, String str2) {
super(str1, number1);
this.str2 = str2;
}
public PropertyOverrideEntity(String str1, Integer number1, Integer id, String str2) {
super(str1, number1, id);
this.str2 = str2;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof PropertyOverrideEntity)) return false;
if (!super.equals(o)) return false;
PropertyOverrideEntity that = (PropertyOverrideEntity) o;
if (str2 != null ? !str2.equals(that.str2) : that.str2 != null) return false;
return true;
}
@Override
public int hashCode() {
int result = super.hashCode();
result = 31 * result + (str2 != null ? str2.hashCode() : 0);
return result;
}
@Override
public String toString() {
return "PropertyOverrideEntity(" + super.toString() + ", str2 = " + str2 + ")";
}
public String getStr2() {
return str2;
}
public void setStr2(String str2) {
this.str2 = str2;
}
}

View File

@ -0,0 +1,64 @@
package org.hibernate.envers.test.integration.superclass.auditoverride;
import org.hibernate.envers.AuditOverride;
import org.hibernate.envers.AuditOverrides;
import org.hibernate.envers.Audited;
import javax.persistence.Entity;
/**
* @author Lukasz Antoniak (lukasz dot antoniak at gmail dot com)
*/
@Entity
@Audited
@AuditOverrides({@AuditOverride(forClass = BaseEntity.class, name = "str1", isAudited = true),
@AuditOverride(forClass = ExtendedBaseEntity.class, name = "number2", isAudited = true)})
public class TransitiveOverrideEntity extends ExtendedBaseEntity {
private String str3;
public TransitiveOverrideEntity() {
}
public TransitiveOverrideEntity(String str1, Integer number1, Integer id, String str2, Integer number2, String str3) {
super(str1, number1, id, str2, number2);
this.str3 = str3;
}
public TransitiveOverrideEntity(String str1, Integer number1, String str2, Integer number2, String str3) {
super(str1, number1, str2, number2);
this.str3 = str3;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof TransitiveOverrideEntity)) return false;
if (!super.equals(o)) return false;
TransitiveOverrideEntity that = (TransitiveOverrideEntity) o;
if (str3 != null ? !str3.equals(that.str3) : that.str3 != null) return false;
return true;
}
@Override
public int hashCode() {
int result = super.hashCode();
result = 31 * result + (str3 != null ? str3.hashCode() : 0);
return result;
}
@Override
public String toString() {
return "TransitiveOverrideEntity(" + super.toString() + ", str3 = " + str3 + ")";
}
public String getStr3() {
return str3;
}
public void setStr3(String str3) {
this.str3 = str3;
}
}