Merge branch 'master' of github.com:hibernate/hibernate-core
This commit is contained in:
commit
74c6773c44
|
@ -53,8 +53,9 @@ public class AnnotationBinder {
|
||||||
|
|
||||||
public void bindMappedClasses(Index annotationIndex) {
|
public void bindMappedClasses(Index annotationIndex) {
|
||||||
// need to order our annotated entities into an order we can process
|
// need to order our annotated entities into an order we can process
|
||||||
ConfiguredClassHierarchyBuilder builder = new ConfiguredClassHierarchyBuilder();
|
Set<ConfiguredClassHierarchy> hierarchies = ConfiguredClassHierarchyBuilder.createEntityHierarchies(
|
||||||
Set<ConfiguredClassHierarchy> hierarchies = builder.createEntityHierarchies( annotationIndex );
|
annotationIndex, metadata.getServiceRegistry()
|
||||||
|
);
|
||||||
|
|
||||||
// now we process each hierarchy one at the time
|
// now we process each hierarchy one at the time
|
||||||
for ( ConfiguredClassHierarchy hierarchy : hierarchies ) {
|
for ( ConfiguredClassHierarchy hierarchy : hierarchies ) {
|
||||||
|
@ -75,7 +76,7 @@ public class AnnotationBinder {
|
||||||
classInfo, JPADotNames.MAPPED_SUPER_CLASS
|
classInfo, JPADotNames.MAPPED_SUPER_CLASS
|
||||||
);
|
);
|
||||||
AnnotationInstance hibernateEntityAnnotation = JandexHelper.getSingleAnnotation(
|
AnnotationInstance hibernateEntityAnnotation = JandexHelper.getSingleAnnotation(
|
||||||
classInfo, JPADotNames.HIBERNATE_ENTITY
|
classInfo, HibernateDotNames.ENTITY
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -23,14 +23,26 @@
|
||||||
*/
|
*/
|
||||||
package org.hibernate.metamodel.source.annotations;
|
package org.hibernate.metamodel.source.annotations;
|
||||||
|
|
||||||
import javax.persistence.Entity;
|
import java.lang.reflect.Field;
|
||||||
import javax.persistence.MappedSuperclass;
|
import java.lang.reflect.Member;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
import javax.persistence.AccessType;
|
||||||
|
|
||||||
import org.jboss.jandex.AnnotationInstance;
|
import org.jboss.jandex.AnnotationInstance;
|
||||||
|
import org.jboss.jandex.AnnotationTarget;
|
||||||
import org.jboss.jandex.ClassInfo;
|
import org.jboss.jandex.ClassInfo;
|
||||||
import org.jboss.jandex.DotName;
|
import org.jboss.jandex.FieldInfo;
|
||||||
|
import org.jboss.jandex.MethodInfo;
|
||||||
|
|
||||||
import org.hibernate.AnnotationException;
|
import org.hibernate.AnnotationException;
|
||||||
|
import org.hibernate.HibernateException;
|
||||||
|
import org.hibernate.service.ServiceRegistry;
|
||||||
|
import org.hibernate.service.classloading.spi.ClassLoaderService;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents an entity, mapped superclass or component configured via annotations/xml.
|
* Represents an entity, mapped superclass or component configured via annotations/xml.
|
||||||
|
@ -39,19 +51,21 @@ import org.hibernate.AnnotationException;
|
||||||
*/
|
*/
|
||||||
public class ConfiguredClass {
|
public class ConfiguredClass {
|
||||||
private final ClassInfo classInfo;
|
private final ClassInfo classInfo;
|
||||||
|
private final Class<?> clazz;
|
||||||
|
private final AccessType classAccessType;
|
||||||
private final ConfiguredClassHierarchy hierarchy;
|
private final ConfiguredClassHierarchy hierarchy;
|
||||||
|
private final boolean isMappedSuperClass;
|
||||||
|
private final List<MappedProperty> mappedProperties;
|
||||||
|
|
||||||
public ConfiguredClass(ClassInfo info, ConfiguredClassHierarchy hierarchy) {
|
public ConfiguredClass(ClassInfo info, ConfiguredClassHierarchy hierarchy, ServiceRegistry serviceRegistry) {
|
||||||
this.classInfo = info;
|
this.classInfo = info;
|
||||||
this.hierarchy = hierarchy;
|
this.hierarchy = hierarchy;
|
||||||
init();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void init() {
|
|
||||||
//@Entity and @MappedSuperclass on the same class leads to a NPE down the road
|
//@Entity and @MappedSuperclass on the same class leads to a NPE down the road
|
||||||
AnnotationInstance jpaEntityAnnotation = JandexHelper.getSingleAnnotation( classInfo, JPADotNames.ENTITY );
|
AnnotationInstance jpaEntityAnnotation = JandexHelper.getSingleAnnotation( classInfo, JPADotNames.ENTITY );
|
||||||
AnnotationInstance mappedSuperClassAnnotation = JandexHelper.getSingleAnnotation( classInfo, JPADotNames.MAPPED_SUPER_CLASS );
|
AnnotationInstance mappedSuperClassAnnotation = JandexHelper.getSingleAnnotation(
|
||||||
AnnotationInstance hibernateEntityAnnotation = JandexHelper.getSingleAnnotation( classInfo, JPADotNames.HIBERNATE_ENTITY );
|
classInfo, JPADotNames.MAPPED_SUPER_CLASS
|
||||||
|
);
|
||||||
|
|
||||||
if ( jpaEntityAnnotation != null && mappedSuperClassAnnotation != null ) {
|
if ( jpaEntityAnnotation != null && mappedSuperClassAnnotation != null ) {
|
||||||
throw new AnnotationException(
|
throw new AnnotationException(
|
||||||
|
@ -60,13 +74,22 @@ public class ConfiguredClass {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.clazz = serviceRegistry.getService( ClassLoaderService.class ).classForName( info.toString() );
|
||||||
|
isMappedSuperClass = mappedSuperClassAnnotation != null;
|
||||||
|
classAccessType = determineClassAccessType( hierarchy.getDefaultAccessType() );
|
||||||
|
mappedProperties = collectMappedProperties();
|
||||||
|
// make sure the properties are ordered by property name
|
||||||
|
Collections.sort( mappedProperties );
|
||||||
}
|
}
|
||||||
|
|
||||||
public ClassInfo getClassInfo() {
|
public ClassInfo getClassInfo() {
|
||||||
return classInfo;
|
return classInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isMappedSuperClass() {
|
||||||
|
return isMappedSuperClass;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
final StringBuilder sb = new StringBuilder();
|
final StringBuilder sb = new StringBuilder();
|
||||||
|
@ -75,6 +98,137 @@ public class ConfiguredClass {
|
||||||
sb.append( '}' );
|
sb.append( '}' );
|
||||||
return sb.toString();
|
return sb.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private AccessType determineClassAccessType(AccessType hierarchyAccessType) {
|
||||||
|
// default to the hierarchy access type to start with
|
||||||
|
AccessType accessType = hierarchyAccessType;
|
||||||
|
|
||||||
|
AnnotationInstance accessAnnotation = JandexHelper.getSingleAnnotation( classInfo, JPADotNames.ACCESS );
|
||||||
|
if ( accessAnnotation != null ) {
|
||||||
|
accessType = Enum.valueOf( AccessType.class, accessAnnotation.value( "value" ).asEnum() );
|
||||||
|
}
|
||||||
|
|
||||||
|
return accessType;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<MappedProperty> collectMappedProperties() {
|
||||||
|
Set<String> transientFieldNames = new HashSet<String>();
|
||||||
|
Set<String> transientMethodNames = new HashSet<String>();
|
||||||
|
populateTransientFieldAndMethodLists( transientFieldNames, transientMethodNames );
|
||||||
|
|
||||||
|
List<Member> classMembers = new ArrayList<Member>();
|
||||||
|
Set<String> explicitlyConfiguredAccessMemberNames;
|
||||||
|
if ( AccessType.FIELD.equals( classAccessType ) ) {
|
||||||
|
explicitlyConfiguredAccessMemberNames = addExplicitAccessMembers( classMembers, MethodInfo.class );
|
||||||
|
Field fields[] = clazz.getDeclaredFields();
|
||||||
|
Field.setAccessible( fields, true );
|
||||||
|
for ( Field field : fields ) {
|
||||||
|
if ( !transientFieldNames.contains( field.getName() )
|
||||||
|
&& !explicitlyConfiguredAccessMemberNames.contains( field.getName() ) ) {
|
||||||
|
classMembers.add( field );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
explicitlyConfiguredAccessMemberNames = addExplicitAccessMembers( classMembers, FieldInfo.class );
|
||||||
|
Method[] methods = clazz.getDeclaredMethods();
|
||||||
|
Method.setAccessible( methods, true );
|
||||||
|
for ( Method method : methods ) {
|
||||||
|
if ( !transientMethodNames.contains( method.getName() )
|
||||||
|
&& !explicitlyConfiguredAccessMemberNames.contains( ReflectionHelper.getPropertyName( method ) ) ) {
|
||||||
|
classMembers.add( method );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
List<MappedProperty> properties = new ArrayList<MappedProperty>();
|
||||||
|
return properties;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Set<String> addExplicitAccessMembers(List<Member> classMembers, Class<? extends AnnotationTarget> targetClass) {
|
||||||
|
Set<String> explicitAccessMembers = new HashSet<String>();
|
||||||
|
|
||||||
|
List<AnnotationInstance> accessAnnotations = classInfo.annotations().get( JPADotNames.ACCESS );
|
||||||
|
if ( accessAnnotations == null ) {
|
||||||
|
return explicitAccessMembers;
|
||||||
|
}
|
||||||
|
|
||||||
|
for ( AnnotationInstance accessAnnotation : accessAnnotations ) {
|
||||||
|
// at this stage we are only interested at annotations defined on fields and methods
|
||||||
|
AnnotationTarget target = accessAnnotation.target();
|
||||||
|
if ( !target.getClass().equals( targetClass ) ) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
AccessType accessType = Enum.valueOf( AccessType.class, accessAnnotation.value().asEnum() );
|
||||||
|
|
||||||
|
if ( target instanceof MethodInfo && MethodInfo.class.equals( targetClass ) ) {
|
||||||
|
// annotating a field with @AccessType(PROPERTY) has not effect
|
||||||
|
if ( !AccessType.PROPERTY.equals( accessType ) ) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
Method m;
|
||||||
|
try {
|
||||||
|
m = clazz.getMethod( ( (MethodInfo) target ).name() );
|
||||||
|
}
|
||||||
|
catch ( NoSuchMethodException e ) {
|
||||||
|
throw new HibernateException(
|
||||||
|
"Unable to load method "
|
||||||
|
+ ( (MethodInfo) target ).name()
|
||||||
|
+ " of class " + clazz.getName()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
classMembers.add( m );
|
||||||
|
explicitAccessMembers.add( ReflectionHelper.getPropertyName( m ) );
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( target instanceof FieldInfo && FieldInfo.class.equals( targetClass ) ) {
|
||||||
|
// annotating a method w/ @AccessType(FIELD) has no effect
|
||||||
|
if ( !AccessType.FIELD.equals( accessType ) ) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
Field f;
|
||||||
|
try {
|
||||||
|
f = clazz.getField( ( (FieldInfo) target ).name() );
|
||||||
|
}
|
||||||
|
catch ( NoSuchFieldException e ) {
|
||||||
|
throw new HibernateException(
|
||||||
|
"Unable to load field "
|
||||||
|
+ ( (FieldInfo) target ).name()
|
||||||
|
+ " of class " + clazz.getName()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
classMembers.add( f );
|
||||||
|
explicitAccessMembers.add( f.getName() );
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return explicitAccessMembers;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Populates the sets of transient field and method names.
|
||||||
|
*
|
||||||
|
* @param transientFieldNames Set to populate with the field names explicitly marked as @Transient
|
||||||
|
* @param transientMethodNames set to populate with the method names explicitly marked as @Transient
|
||||||
|
*/
|
||||||
|
private void populateTransientFieldAndMethodLists(Set<String> transientFieldNames, Set<String> transientMethodNames) {
|
||||||
|
List<AnnotationInstance> transientMembers = classInfo.annotations().get( JPADotNames.TRANSIENT );
|
||||||
|
if ( transientMembers == null ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for ( AnnotationInstance transientMember : transientMembers ) {
|
||||||
|
AnnotationTarget target = transientMember.target();
|
||||||
|
if ( target instanceof FieldInfo ) {
|
||||||
|
transientFieldNames.add( ( (FieldInfo) target ).name() );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
transientMethodNames.add( ( (MethodInfo) target ).name() );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -35,6 +35,7 @@ import org.jboss.jandex.FieldInfo;
|
||||||
import org.jboss.jandex.MethodInfo;
|
import org.jboss.jandex.MethodInfo;
|
||||||
|
|
||||||
import org.hibernate.AnnotationException;
|
import org.hibernate.AnnotationException;
|
||||||
|
import org.hibernate.service.ServiceRegistry;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents the inheritance structure of the configured classes within a class hierarchy.
|
* Represents the inheritance structure of the configured classes within a class hierarchy.
|
||||||
|
@ -46,13 +47,14 @@ public class ConfiguredClassHierarchy implements Iterable<ConfiguredClass> {
|
||||||
private final InheritanceType inheritanceType;
|
private final InheritanceType inheritanceType;
|
||||||
private final List<ConfiguredClass> configuredClasses;
|
private final List<ConfiguredClass> configuredClasses;
|
||||||
|
|
||||||
ConfiguredClassHierarchy(List<ClassInfo> classes) {
|
ConfiguredClassHierarchy(List<ClassInfo> classes, ServiceRegistry serviceRegistry) {
|
||||||
|
defaultAccessType = determineDefaultAccessType( classes );
|
||||||
|
inheritanceType = determineInheritanceType( classes );
|
||||||
|
|
||||||
configuredClasses = new ArrayList<ConfiguredClass>();
|
configuredClasses = new ArrayList<ConfiguredClass>();
|
||||||
for ( ClassInfo info : classes ) {
|
for ( ClassInfo info : classes ) {
|
||||||
configuredClasses.add( new ConfiguredClass( info, this ) );
|
configuredClasses.add( new ConfiguredClass( info, this, serviceRegistry ) );
|
||||||
}
|
}
|
||||||
defaultAccessType = determineDefaultAccessType();
|
|
||||||
inheritanceType = determineInheritanceType();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public AccessType getDefaultAccessType() {
|
public AccessType getDefaultAccessType() {
|
||||||
|
@ -66,8 +68,7 @@ public class ConfiguredClassHierarchy implements Iterable<ConfiguredClass> {
|
||||||
/**
|
/**
|
||||||
* @return An iterator iterating in top down manner over the configured classes in this hierarchy.
|
* @return An iterator iterating in top down manner over the configured classes in this hierarchy.
|
||||||
*/
|
*/
|
||||||
public Iterator<ConfiguredClass> iterator
|
public Iterator<ConfiguredClass> iterator() {
|
||||||
() {
|
|
||||||
return configuredClasses.iterator();
|
return configuredClasses.iterator();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -83,72 +84,112 @@ public class ConfiguredClassHierarchy implements Iterable<ConfiguredClass> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @param classes the classes in the hierarchy
|
||||||
|
*
|
||||||
* @return Returns the default access type for the configured class hierarchy independent of explicit
|
* @return Returns the default access type for the configured class hierarchy independent of explicit
|
||||||
* {@code AccessType} annotations. The default access type is determined by the placement of the
|
* {@code AccessType} annotations. The default access type is determined by the placement of the
|
||||||
* annotations.
|
* annotations.
|
||||||
*/
|
*/
|
||||||
private AccessType determineDefaultAccessType() {
|
private AccessType determineDefaultAccessType(List<ClassInfo> classes) {
|
||||||
Iterator<ConfiguredClass> iter = iterator();
|
|
||||||
AccessType accessType = null;
|
AccessType accessType = null;
|
||||||
while ( iter.hasNext() ) {
|
for ( ClassInfo info : classes ) {
|
||||||
ConfiguredClass configuredClass = iter.next();
|
|
||||||
ClassInfo info = configuredClass.getClassInfo();
|
|
||||||
List<AnnotationInstance> idAnnotations = info.annotations().get( JPADotNames.ID );
|
List<AnnotationInstance> idAnnotations = info.annotations().get( JPADotNames.ID );
|
||||||
if ( idAnnotations == null || idAnnotations.size() == 0 ) {
|
if ( idAnnotations == null || idAnnotations.size() == 0 ) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
accessType = processIdAnnotations( idAnnotations );
|
||||||
for ( AnnotationInstance annotation : idAnnotations ) {
|
|
||||||
AccessType tmpAccessType;
|
|
||||||
if ( annotation.target() instanceof FieldInfo ) {
|
|
||||||
tmpAccessType = AccessType.FIELD;
|
|
||||||
}
|
|
||||||
else if ( annotation.target() instanceof MethodInfo ) {
|
|
||||||
tmpAccessType = AccessType.PROPERTY;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
throw new AnnotationException( "Invalid placement of @Id annotation" );
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( accessType == null ) {
|
|
||||||
accessType = tmpAccessType;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
if ( !accessType.equals( tmpAccessType ) ) {
|
|
||||||
throw new AnnotationException( "Inconsistent placement of @Id annotation within hierarchy " + hierarchyListString() );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( accessType == null ) {
|
if ( accessType == null ) {
|
||||||
return throwIdNotFoundAnnotationException();
|
return throwIdNotFoundAnnotationException( classes );
|
||||||
}
|
}
|
||||||
|
|
||||||
return accessType;
|
return accessType;
|
||||||
}
|
}
|
||||||
|
|
||||||
private InheritanceType determineInheritanceType() {
|
private AccessType processIdAnnotations(List<AnnotationInstance> idAnnotations) {
|
||||||
return null; //To change body of created methods use File | Settings | File Templates.
|
AccessType accessType = null;
|
||||||
|
for ( AnnotationInstance annotation : idAnnotations ) {
|
||||||
|
AccessType tmpAccessType;
|
||||||
|
if ( annotation.target() instanceof FieldInfo ) {
|
||||||
|
tmpAccessType = AccessType.FIELD;
|
||||||
|
}
|
||||||
|
else if ( annotation.target() instanceof MethodInfo ) {
|
||||||
|
tmpAccessType = AccessType.PROPERTY;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
throw new AnnotationException( "Invalid placement of @Id annotation" );
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( accessType == null ) {
|
||||||
|
accessType = tmpAccessType;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if ( !accessType.equals( tmpAccessType ) ) {
|
||||||
|
throw new AnnotationException( "Inconsistent placement of @Id annotation within hierarchy " );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return accessType;
|
||||||
}
|
}
|
||||||
|
|
||||||
private AccessType throwIdNotFoundAnnotationException() {
|
private InheritanceType determineInheritanceType(List<ClassInfo> classes) {
|
||||||
|
InheritanceType inheritanceType = null;
|
||||||
|
for ( ClassInfo info : classes ) {
|
||||||
|
AnnotationInstance inheritanceAnnotation = JandexHelper.getSingleAnnotation(
|
||||||
|
info, JPADotNames.INHERITANCE
|
||||||
|
);
|
||||||
|
if ( inheritanceAnnotation == null ) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
InheritanceType tmpInheritanceType = Enum.valueOf(
|
||||||
|
InheritanceType.class, inheritanceAnnotation.value( "strategy" ).asEnum()
|
||||||
|
);
|
||||||
|
if ( tmpInheritanceType == null ) {
|
||||||
|
// default inheritance type is single table
|
||||||
|
inheritanceType = InheritanceType.SINGLE_TABLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( inheritanceType == null ) {
|
||||||
|
inheritanceType = tmpInheritanceType;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if ( !inheritanceType.equals( tmpInheritanceType ) ) {
|
||||||
|
throw new AnnotationException(
|
||||||
|
"Multiple incompatible instances of @Inheritance specified within classes "
|
||||||
|
+ hierarchyListString( classes )
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( inheritanceType == null ) {
|
||||||
|
// default inheritance type is single table
|
||||||
|
inheritanceType = InheritanceType.SINGLE_TABLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return inheritanceType;
|
||||||
|
}
|
||||||
|
|
||||||
|
private AccessType throwIdNotFoundAnnotationException(List<ClassInfo> classes) {
|
||||||
StringBuilder builder = new StringBuilder();
|
StringBuilder builder = new StringBuilder();
|
||||||
builder.append( "Unable to find Id property for class hierarchy " );
|
builder.append( "Unable to find Id property for class hierarchy " );
|
||||||
builder.append( hierarchyListString() );
|
builder.append( hierarchyListString( classes ) );
|
||||||
throw new AnnotationException( builder.toString() );
|
throw new AnnotationException( builder.toString() );
|
||||||
}
|
}
|
||||||
|
|
||||||
private String hierarchyListString() {
|
private String hierarchyListString(List<ClassInfo> classes) {
|
||||||
Iterator<ConfiguredClass> iter;
|
|
||||||
StringBuilder builder = new StringBuilder();
|
StringBuilder builder = new StringBuilder();
|
||||||
builder.append( "[" );
|
builder.append( "[" );
|
||||||
iter = iterator();
|
|
||||||
while ( iter.hasNext() ) {
|
int count = 0;
|
||||||
builder.append( iter.next().getClassInfo().name().toString() );
|
for ( ClassInfo info : classes ) {
|
||||||
if ( iter.hasNext() ) {
|
builder.append( info.name().toString() );
|
||||||
|
if ( count < classes.size() ) {
|
||||||
builder.append( ", " );
|
builder.append( ", " );
|
||||||
}
|
}
|
||||||
|
count++;
|
||||||
}
|
}
|
||||||
builder.append( "]" );
|
builder.append( "]" );
|
||||||
return builder.toString();
|
return builder.toString();
|
||||||
|
|
|
@ -34,7 +34,8 @@ import org.jboss.jandex.ClassInfo;
|
||||||
import org.jboss.jandex.DotName;
|
import org.jboss.jandex.DotName;
|
||||||
import org.jboss.jandex.Index;
|
import org.jboss.jandex.Index;
|
||||||
|
|
||||||
import org.hibernate.internal.util.ReflectHelper;
|
import org.hibernate.service.ServiceRegistry;
|
||||||
|
import org.hibernate.service.classloading.spi.ClassLoaderService;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Given a annotation index build a list of class hierarchies.
|
* Given a annotation index build a list of class hierarchies.
|
||||||
|
@ -43,7 +44,17 @@ import org.hibernate.internal.util.ReflectHelper;
|
||||||
*/
|
*/
|
||||||
public class ConfiguredClassHierarchyBuilder {
|
public class ConfiguredClassHierarchyBuilder {
|
||||||
|
|
||||||
public Set<ConfiguredClassHierarchy> createEntityHierarchies(Index index) {
|
/**
|
||||||
|
* This methods pre-processes the annotated entities from the index and put them into a structure which can
|
||||||
|
* bound to the Hibernate metamodel.
|
||||||
|
*
|
||||||
|
* @param index The annotation index
|
||||||
|
* @param serviceRegistry The service registry
|
||||||
|
*
|
||||||
|
* @return a set of {@code ConfiguredClassHierarchy}s. One for each configured "leaf" entity.
|
||||||
|
*/
|
||||||
|
public static Set<ConfiguredClassHierarchy> createEntityHierarchies(Index index, ServiceRegistry serviceRegistry) {
|
||||||
|
ClassLoaderService classLoaderService = serviceRegistry.getService( ClassLoaderService.class );
|
||||||
Map<ClassInfo, List<ClassInfo>> processedClassInfos = new HashMap<ClassInfo, List<ClassInfo>>();
|
Map<ClassInfo, List<ClassInfo>> processedClassInfos = new HashMap<ClassInfo, List<ClassInfo>>();
|
||||||
|
|
||||||
for ( ClassInfo info : index.getKnownClasses() ) {
|
for ( ClassInfo info : index.getKnownClasses() ) {
|
||||||
|
@ -52,7 +63,7 @@ public class ConfiguredClassHierarchyBuilder {
|
||||||
}
|
}
|
||||||
List<ClassInfo> configuredClassList = new ArrayList<ClassInfo>();
|
List<ClassInfo> configuredClassList = new ArrayList<ClassInfo>();
|
||||||
ClassInfo tmpClassInfo = info;
|
ClassInfo tmpClassInfo = info;
|
||||||
Class<?> clazz = classForName( tmpClassInfo );
|
Class<?> clazz = classLoaderService.classForName( tmpClassInfo.toString() );
|
||||||
while ( clazz != null && !clazz.equals( Object.class ) ) {
|
while ( clazz != null && !clazz.equals( Object.class ) ) {
|
||||||
tmpClassInfo = index.getClassByName( DotName.createSimple( clazz.getName() ) );
|
tmpClassInfo = index.getClassByName( DotName.createSimple( clazz.getName() ) );
|
||||||
clazz = clazz.getSuperclass();
|
clazz = clazz.getSuperclass();
|
||||||
|
@ -73,14 +84,13 @@ public class ConfiguredClassHierarchyBuilder {
|
||||||
processedClassInfos.put( tmpClassInfo, configuredClassList );
|
processedClassInfos.put( tmpClassInfo, configuredClassList );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Set<ConfiguredClassHierarchy> hierarchies = new HashSet<ConfiguredClassHierarchy>();
|
Set<ConfiguredClassHierarchy> hierarchies = new HashSet<ConfiguredClassHierarchy>();
|
||||||
List<List<ClassInfo>> processedList = new ArrayList<List<ClassInfo>>();
|
List<List<ClassInfo>> processedList = new ArrayList<List<ClassInfo>>();
|
||||||
for ( List<ClassInfo> classInfoList : processedClassInfos.values() ) {
|
for ( List<ClassInfo> classInfoList : processedClassInfos.values() ) {
|
||||||
if ( !processedList.contains( classInfoList ) ) {
|
if ( !processedList.contains( classInfoList ) ) {
|
||||||
hierarchies.add( new ConfiguredClassHierarchy( classInfoList ) );
|
hierarchies.add( new ConfiguredClassHierarchy( classInfoList, serviceRegistry ) );
|
||||||
processedList.add( classInfoList );
|
processedList.add( classInfoList );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -88,7 +98,7 @@ public class ConfiguredClassHierarchyBuilder {
|
||||||
return hierarchies;
|
return hierarchies;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean existsHierarchyWithClassInfoAsLeaf(Map<ClassInfo, List<ClassInfo>> processedClassInfos, ClassInfo tmpClassInfo) {
|
private static boolean existsHierarchyWithClassInfoAsLeaf(Map<ClassInfo, List<ClassInfo>> processedClassInfos, ClassInfo tmpClassInfo) {
|
||||||
if ( !processedClassInfos.containsKey( tmpClassInfo ) ) {
|
if ( !processedClassInfos.containsKey( tmpClassInfo ) ) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -96,17 +106,6 @@ public class ConfiguredClassHierarchyBuilder {
|
||||||
List<ClassInfo> classInfoList = processedClassInfos.get( tmpClassInfo );
|
List<ClassInfo> classInfoList = processedClassInfos.get( tmpClassInfo );
|
||||||
return classInfoList.get( classInfoList.size() - 1 ).equals( tmpClassInfo );
|
return classInfoList.get( classInfoList.size() - 1 ).equals( tmpClassInfo );
|
||||||
}
|
}
|
||||||
|
|
||||||
private Class<?> classForName(ClassInfo info) {
|
|
||||||
Class<?> clazz = null;
|
|
||||||
try {
|
|
||||||
clazz = ReflectHelper.classForName( info.toString() );
|
|
||||||
}
|
|
||||||
catch ( ClassNotFoundException e ) {
|
|
||||||
// todo what to do here!?i
|
|
||||||
}
|
|
||||||
return clazz;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,43 @@
|
||||||
|
/*
|
||||||
|
* Hibernate, Relational Persistence for Idiomatic Java
|
||||||
|
*
|
||||||
|
* Copyright (c) 2011, Red Hat Inc. or third-party contributors as
|
||||||
|
* indicated by the @author tags or express copyright attribution
|
||||||
|
* statements applied by the authors. All third-party contributions are
|
||||||
|
* distributed under license by Red Hat Inc.
|
||||||
|
*
|
||||||
|
* This copyrighted material is made available to anyone wishing to use, modify,
|
||||||
|
* copy, or redistribute it subject to the terms and conditions of the GNU
|
||||||
|
* Lesser General Public License, as published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||||
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
|
||||||
|
* for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License
|
||||||
|
* along with this distribution; if not, write to:
|
||||||
|
* Free Software Foundation, Inc.
|
||||||
|
* 51 Franklin Street, Fifth Floor
|
||||||
|
* Boston, MA 02110-1301 USA
|
||||||
|
*/
|
||||||
|
package org.hibernate.metamodel.source.annotations;
|
||||||
|
|
||||||
|
import org.jboss.jandex.DotName;
|
||||||
|
|
||||||
|
import org.hibernate.annotations.Entity;
|
||||||
|
import org.hibernate.annotations.FetchProfile;
|
||||||
|
import org.hibernate.annotations.FetchProfiles;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Defines the dot names for the Hibernate specific annotations.
|
||||||
|
*
|
||||||
|
* @author Hardy Ferentschik
|
||||||
|
*/
|
||||||
|
public interface HibernateDotNames {
|
||||||
|
public static final DotName ENTITY = DotName.createSimple( Entity.class.getName() );
|
||||||
|
public static final DotName FETCH_PROFILES = DotName.createSimple( FetchProfiles.class.getName() );
|
||||||
|
public static final DotName FETCH_PROFILE = DotName.createSimple( FetchProfile.class.getName() );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -23,9 +23,12 @@
|
||||||
*/
|
*/
|
||||||
package org.hibernate.metamodel.source.annotations;
|
package org.hibernate.metamodel.source.annotations;
|
||||||
|
|
||||||
|
import javax.persistence.Access;
|
||||||
import javax.persistence.Entity;
|
import javax.persistence.Entity;
|
||||||
import javax.persistence.Id;
|
import javax.persistence.Id;
|
||||||
|
import javax.persistence.Inheritance;
|
||||||
import javax.persistence.MappedSuperclass;
|
import javax.persistence.MappedSuperclass;
|
||||||
|
import javax.persistence.Transient;
|
||||||
|
|
||||||
import org.jboss.jandex.DotName;
|
import org.jboss.jandex.DotName;
|
||||||
|
|
||||||
|
@ -36,11 +39,12 @@ import org.jboss.jandex.DotName;
|
||||||
*/
|
*/
|
||||||
public interface JPADotNames {
|
public interface JPADotNames {
|
||||||
public static final DotName ENTITY = DotName.createSimple( Entity.class.getName() );
|
public static final DotName ENTITY = DotName.createSimple( Entity.class.getName() );
|
||||||
public static final DotName HIBERNATE_ENTITY = DotName.createSimple( org.hibernate.annotations.Entity.class.getName() );
|
|
||||||
public static final DotName MAPPED_SUPER_CLASS = DotName.createSimple( MappedSuperclass.class.getName() );
|
public static final DotName MAPPED_SUPER_CLASS = DotName.createSimple( MappedSuperclass.class.getName() );
|
||||||
|
public static final DotName INHERITANCE = DotName.createSimple( Inheritance.class.getName() );
|
||||||
|
|
||||||
public static final DotName ID = DotName.createSimple( Id.class.getName() );
|
public static final DotName ID = DotName.createSimple( Id.class.getName() );
|
||||||
|
public static final DotName ACCESS = DotName.createSimple( Access.class.getName() );
|
||||||
|
public static final DotName TRANSIENT = DotName.createSimple( Transient.class.getName() );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,26 @@
|
||||||
|
/*
|
||||||
|
* Hibernate, Relational Persistence for Idiomatic Java
|
||||||
|
*
|
||||||
|
* Copyright (c) 2011, Red Hat Inc. or third-party contributors as
|
||||||
|
* indicated by the @author tags or express copyright attribution
|
||||||
|
* statements applied by the authors. All third-party contributions are
|
||||||
|
* distributed under license by Red Hat Inc.
|
||||||
|
*
|
||||||
|
* This copyrighted material is made available to anyone wishing to use, modify,
|
||||||
|
* copy, or redistribute it subject to the terms and conditions of the GNU
|
||||||
|
* Lesser General Public License, as published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||||
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
|
||||||
|
* for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License
|
||||||
|
* along with this distribution; if not, write to:
|
||||||
|
* Free Software Foundation, Inc.
|
||||||
|
* 51 Franklin Street, Fifth Floor
|
||||||
|
* Boston, MA 02110-1301 USA
|
||||||
|
*/
|
||||||
package org.hibernate.metamodel.source.annotations;
|
package org.hibernate.metamodel.source.annotations;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -9,6 +32,8 @@ import org.jboss.jandex.DotName;
|
||||||
import org.hibernate.AssertionFailure;
|
import org.hibernate.AssertionFailure;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Utility methods for working with the jandex annotation index.
|
||||||
|
*
|
||||||
* @author Hardy Ferentschik
|
* @author Hardy Ferentschik
|
||||||
*/
|
*/
|
||||||
public class JandexHelper {
|
public class JandexHelper {
|
||||||
|
@ -34,7 +59,9 @@ public class JandexHelper {
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
throw new AssertionFailure(
|
throw new AssertionFailure(
|
||||||
"There should be only one annotation of type " + annotationName + " defined on" + classInfo.name()
|
"Found more than one instance of the annotation "
|
||||||
|
+ annotationList.get( 0 ).name().toString()
|
||||||
|
+ ". Expected was one."
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,48 @@
|
||||||
|
/*
|
||||||
|
* Hibernate, Relational Persistence for Idiomatic Java
|
||||||
|
*
|
||||||
|
* Copyright (c) 2011, Red Hat Inc. or third-party contributors as
|
||||||
|
* indicated by the @author tags or express copyright attribution
|
||||||
|
* statements applied by the authors. All third-party contributions are
|
||||||
|
* distributed under license by Red Hat Inc.
|
||||||
|
*
|
||||||
|
* This copyrighted material is made available to anyone wishing to use, modify,
|
||||||
|
* copy, or redistribute it subject to the terms and conditions of the GNU
|
||||||
|
* Lesser General Public License, as published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||||
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
|
||||||
|
* for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License
|
||||||
|
* along with this distribution; if not, write to:
|
||||||
|
* Free Software Foundation, Inc.
|
||||||
|
* 51 Franklin Street, Fifth Floor
|
||||||
|
* Boston, MA 02110-1301 USA
|
||||||
|
*/
|
||||||
|
package org.hibernate.metamodel.source.annotations;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represent a mapped property (explicitly or implicitly mapped).
|
||||||
|
*
|
||||||
|
* @author Hardy Ferentschik
|
||||||
|
*/
|
||||||
|
public class MappedProperty implements Comparable<MappedProperty> {
|
||||||
|
private final String name;
|
||||||
|
|
||||||
|
MappedProperty(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int compareTo(MappedProperty mappedProperty) {
|
||||||
|
return name.compareTo( mappedProperty.getName() );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,48 @@
|
||||||
|
package org.hibernate.metamodel.source.annotations;
|
||||||
|
|
||||||
|
import java.beans.Introspector;
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.lang.reflect.Member;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Some helper methods for reflection tasks
|
||||||
|
*
|
||||||
|
* @author Hardy Ferentschik
|
||||||
|
*/
|
||||||
|
public class ReflectionHelper {
|
||||||
|
private ReflectionHelper() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Process bean properties getter by applying the JavaBean naming conventions.
|
||||||
|
*
|
||||||
|
* @param member the member for which to get the property name.
|
||||||
|
*
|
||||||
|
* @return The bean method name with the "is" or "get" prefix stripped off, <code>null</code>
|
||||||
|
* the method name id not according to the JavaBeans standard.
|
||||||
|
*/
|
||||||
|
public static String getPropertyName(Member member) {
|
||||||
|
String name = null;
|
||||||
|
|
||||||
|
if ( member instanceof Field ) {
|
||||||
|
name = member.getName();
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( member instanceof Method ) {
|
||||||
|
String methodName = member.getName();
|
||||||
|
if ( methodName.startsWith( "is" ) ) {
|
||||||
|
name = Introspector.decapitalize( methodName.substring( 2 ) );
|
||||||
|
}
|
||||||
|
else if ( methodName.startsWith( "has" ) ) {
|
||||||
|
name = Introspector.decapitalize( methodName.substring( 3 ) );
|
||||||
|
}
|
||||||
|
else if ( methodName.startsWith( "get" ) ) {
|
||||||
|
name = Introspector.decapitalize( methodName.substring( 3 ) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,21 +2,28 @@ package org.hibernate.metamodel.source.annotations;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import javax.persistence.AccessType;
|
import javax.persistence.AccessType;
|
||||||
import javax.persistence.Entity;
|
import javax.persistence.Entity;
|
||||||
import javax.persistence.GeneratedValue;
|
import javax.persistence.GeneratedValue;
|
||||||
import javax.persistence.Id;
|
import javax.persistence.Id;
|
||||||
|
import javax.persistence.Inheritance;
|
||||||
|
import javax.persistence.InheritanceType;
|
||||||
import javax.persistence.MappedSuperclass;
|
import javax.persistence.MappedSuperclass;
|
||||||
|
|
||||||
import org.jboss.jandex.ClassInfo;
|
import org.jboss.jandex.ClassInfo;
|
||||||
import org.jboss.jandex.DotName;
|
import org.jboss.jandex.DotName;
|
||||||
import org.jboss.jandex.Index;
|
import org.jboss.jandex.Index;
|
||||||
import org.jboss.jandex.Indexer;
|
import org.jboss.jandex.Indexer;
|
||||||
|
import org.junit.After;
|
||||||
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import org.hibernate.AnnotationException;
|
import org.hibernate.AnnotationException;
|
||||||
|
import org.hibernate.metamodel.source.Metadata;
|
||||||
|
import org.hibernate.service.internal.BasicServiceRegistryImpl;
|
||||||
import org.hibernate.testing.junit4.BaseUnitTestCase;
|
import org.hibernate.testing.junit4.BaseUnitTestCase;
|
||||||
|
|
||||||
import static junit.framework.Assert.assertEquals;
|
import static junit.framework.Assert.assertEquals;
|
||||||
|
@ -29,11 +36,24 @@ import static org.junit.Assert.fail;
|
||||||
*/
|
*/
|
||||||
public class ConfiguredClassHierarchyBuilderTest extends BaseUnitTestCase {
|
public class ConfiguredClassHierarchyBuilderTest extends BaseUnitTestCase {
|
||||||
|
|
||||||
|
private BasicServiceRegistryImpl serviceRegistry;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp() {
|
||||||
|
serviceRegistry = new BasicServiceRegistryImpl( Collections.emptyMap() );
|
||||||
|
}
|
||||||
|
|
||||||
|
@After
|
||||||
|
public void tearDown() {
|
||||||
|
serviceRegistry.destroy();
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSingleEntity() {
|
public void testSingleEntity() {
|
||||||
Index index = indexForClass( Foo.class );
|
Index index = indexForClass( Foo.class );
|
||||||
ConfiguredClassHierarchyBuilder builder = new ConfiguredClassHierarchyBuilder();
|
Set<ConfiguredClassHierarchy> hierarchies = ConfiguredClassHierarchyBuilder.createEntityHierarchies(
|
||||||
Set<ConfiguredClassHierarchy> hierarchies = builder.createEntityHierarchies( index );
|
index, serviceRegistry
|
||||||
|
);
|
||||||
assertEquals( "There should be only one hierarchy", 1, hierarchies.size() );
|
assertEquals( "There should be only one hierarchy", 1, hierarchies.size() );
|
||||||
|
|
||||||
Iterator<ConfiguredClass> iter = hierarchies.iterator().next().iterator();
|
Iterator<ConfiguredClass> iter = hierarchies.iterator().next().iterator();
|
||||||
|
@ -45,8 +65,9 @@ public class ConfiguredClassHierarchyBuilderTest extends BaseUnitTestCase {
|
||||||
@Test
|
@Test
|
||||||
public void testSimpleInheritance() {
|
public void testSimpleInheritance() {
|
||||||
Index index = indexForClass( B.class, A.class );
|
Index index = indexForClass( B.class, A.class );
|
||||||
ConfiguredClassHierarchyBuilder builder = new ConfiguredClassHierarchyBuilder();
|
Set<ConfiguredClassHierarchy> hierarchies = ConfiguredClassHierarchyBuilder.createEntityHierarchies(
|
||||||
Set<ConfiguredClassHierarchy> hierarchies = builder.createEntityHierarchies( index );
|
index, serviceRegistry
|
||||||
|
);
|
||||||
assertEquals( "There should be only one hierarchy", 1, hierarchies.size() );
|
assertEquals( "There should be only one hierarchy", 1, hierarchies.size() );
|
||||||
|
|
||||||
Iterator<ConfiguredClass> iter = hierarchies.iterator().next().iterator();
|
Iterator<ConfiguredClass> iter = hierarchies.iterator().next().iterator();
|
||||||
|
@ -60,8 +81,9 @@ public class ConfiguredClassHierarchyBuilderTest extends BaseUnitTestCase {
|
||||||
@Test
|
@Test
|
||||||
public void testMultipleHierarchies() {
|
public void testMultipleHierarchies() {
|
||||||
Index index = indexForClass( B.class, A.class, Foo.class );
|
Index index = indexForClass( B.class, A.class, Foo.class );
|
||||||
ConfiguredClassHierarchyBuilder builder = new ConfiguredClassHierarchyBuilder();
|
Set<ConfiguredClassHierarchy> hierarchies = ConfiguredClassHierarchyBuilder.createEntityHierarchies(
|
||||||
Set<ConfiguredClassHierarchy> hierarchies = builder.createEntityHierarchies( index );
|
index, serviceRegistry
|
||||||
|
);
|
||||||
assertEquals( "There should be only one hierarchy", 2, hierarchies.size() );
|
assertEquals( "There should be only one hierarchy", 2, hierarchies.size() );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -84,8 +106,9 @@ public class ConfiguredClassHierarchyBuilderTest extends BaseUnitTestCase {
|
||||||
}
|
}
|
||||||
|
|
||||||
Index index = indexForClass( MappedSubClass.class, MappedSuperClass.class, UnmappedSubClass.class );
|
Index index = indexForClass( MappedSubClass.class, MappedSuperClass.class, UnmappedSubClass.class );
|
||||||
ConfiguredClassHierarchyBuilder builder = new ConfiguredClassHierarchyBuilder();
|
Set<ConfiguredClassHierarchy> hierarchies = ConfiguredClassHierarchyBuilder.createEntityHierarchies(
|
||||||
Set<ConfiguredClassHierarchy> hierarchies = builder.createEntityHierarchies( index );
|
index, serviceRegistry
|
||||||
|
);
|
||||||
assertEquals( "There should be only one hierarchy", 1, hierarchies.size() );
|
assertEquals( "There should be only one hierarchy", 1, hierarchies.size() );
|
||||||
|
|
||||||
Iterator<ConfiguredClass> iter = hierarchies.iterator().next().iterator();
|
Iterator<ConfiguredClass> iter = hierarchies.iterator().next().iterator();
|
||||||
|
@ -106,8 +129,7 @@ public class ConfiguredClassHierarchyBuilderTest extends BaseUnitTestCase {
|
||||||
}
|
}
|
||||||
|
|
||||||
Index index = indexForClass( EntityAndMappedSuperClass.class );
|
Index index = indexForClass( EntityAndMappedSuperClass.class );
|
||||||
ConfiguredClassHierarchyBuilder builder = new ConfiguredClassHierarchyBuilder();
|
ConfiguredClassHierarchyBuilder.createEntityHierarchies( index, serviceRegistry );
|
||||||
builder.createEntityHierarchies( index );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test(expected = AnnotationException.class)
|
@Test(expected = AnnotationException.class)
|
||||||
|
@ -123,8 +145,7 @@ public class ConfiguredClassHierarchyBuilderTest extends BaseUnitTestCase {
|
||||||
}
|
}
|
||||||
|
|
||||||
Index index = indexForClass( B.class, A.class );
|
Index index = indexForClass( B.class, A.class );
|
||||||
ConfiguredClassHierarchyBuilder builder = new ConfiguredClassHierarchyBuilder();
|
ConfiguredClassHierarchyBuilder.createEntityHierarchies( index, serviceRegistry );
|
||||||
builder.createEntityHierarchies( index );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -140,8 +161,9 @@ public class ConfiguredClassHierarchyBuilderTest extends BaseUnitTestCase {
|
||||||
}
|
}
|
||||||
|
|
||||||
Index index = indexForClass( B.class, A.class );
|
Index index = indexForClass( B.class, A.class );
|
||||||
ConfiguredClassHierarchyBuilder builder = new ConfiguredClassHierarchyBuilder();
|
Set<ConfiguredClassHierarchy> hierarchies = ConfiguredClassHierarchyBuilder.createEntityHierarchies(
|
||||||
Set<ConfiguredClassHierarchy> hierarchies = builder.createEntityHierarchies( index );
|
index, serviceRegistry
|
||||||
|
);
|
||||||
assertTrue( hierarchies.size() == 1 );
|
assertTrue( hierarchies.size() == 1 );
|
||||||
ConfiguredClassHierarchy hierarchy = hierarchies.iterator().next();
|
ConfiguredClassHierarchy hierarchy = hierarchies.iterator().next();
|
||||||
assertEquals( "Wrong default access type", AccessType.FIELD, hierarchy.getDefaultAccessType() );
|
assertEquals( "Wrong default access type", AccessType.FIELD, hierarchy.getDefaultAccessType() );
|
||||||
|
@ -168,13 +190,81 @@ public class ConfiguredClassHierarchyBuilderTest extends BaseUnitTestCase {
|
||||||
}
|
}
|
||||||
|
|
||||||
Index index = indexForClass( B.class, A.class );
|
Index index = indexForClass( B.class, A.class );
|
||||||
ConfiguredClassHierarchyBuilder builder = new ConfiguredClassHierarchyBuilder();
|
Set<ConfiguredClassHierarchy> hierarchies = ConfiguredClassHierarchyBuilder.createEntityHierarchies(
|
||||||
Set<ConfiguredClassHierarchy> hierarchies = builder.createEntityHierarchies( index );
|
index, serviceRegistry
|
||||||
|
);
|
||||||
assertTrue( hierarchies.size() == 1 );
|
assertTrue( hierarchies.size() == 1 );
|
||||||
ConfiguredClassHierarchy hierarchy = hierarchies.iterator().next();
|
ConfiguredClassHierarchy hierarchy = hierarchies.iterator().next();
|
||||||
assertEquals( "Wrong default access type", AccessType.PROPERTY, hierarchy.getDefaultAccessType() );
|
assertEquals( "Wrong default access type", AccessType.PROPERTY, hierarchy.getDefaultAccessType() );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDefaultInheritanceStrategy() {
|
||||||
|
@Entity
|
||||||
|
class A {
|
||||||
|
@Id
|
||||||
|
String id;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
class B extends A {
|
||||||
|
}
|
||||||
|
|
||||||
|
Index index = indexForClass( B.class, A.class );
|
||||||
|
Set<ConfiguredClassHierarchy> hierarchies = ConfiguredClassHierarchyBuilder.createEntityHierarchies(
|
||||||
|
index, serviceRegistry
|
||||||
|
);
|
||||||
|
assertTrue( hierarchies.size() == 1 );
|
||||||
|
ConfiguredClassHierarchy hierarchy = hierarchies.iterator().next();
|
||||||
|
assertEquals( "Wrong inheritance type", InheritanceType.SINGLE_TABLE, hierarchy.getInheritanceType() );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testExplicitInheritanceStrategy() {
|
||||||
|
@MappedSuperclass
|
||||||
|
class MappedSuperClass {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
@Inheritance(strategy = InheritanceType.JOINED)
|
||||||
|
class A extends MappedSuperClass {
|
||||||
|
@Id
|
||||||
|
String id;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
class B extends A {
|
||||||
|
}
|
||||||
|
|
||||||
|
Index index = indexForClass( B.class, MappedSuperClass.class, A.class );
|
||||||
|
Set<ConfiguredClassHierarchy> hierarchies = ConfiguredClassHierarchyBuilder.createEntityHierarchies(
|
||||||
|
index, serviceRegistry
|
||||||
|
);
|
||||||
|
assertTrue( hierarchies.size() == 1 );
|
||||||
|
ConfiguredClassHierarchy hierarchy = hierarchies.iterator().next();
|
||||||
|
assertEquals( "Wrong inheritance type", InheritanceType.JOINED, hierarchy.getInheritanceType() );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = AnnotationException.class)
|
||||||
|
public void testMultipleConflictingInheritanceDefinitions() {
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
@Inheritance(strategy = InheritanceType.JOINED)
|
||||||
|
class A {
|
||||||
|
String id;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
|
||||||
|
class B extends A {
|
||||||
|
}
|
||||||
|
|
||||||
|
Index index = indexForClass( B.class, A.class );
|
||||||
|
ConfiguredClassHierarchyBuilder.createEntityHierarchies( index, serviceRegistry );
|
||||||
|
}
|
||||||
|
|
||||||
private Index indexForClass(Class<?>... classes) {
|
private Index indexForClass(Class<?>... classes) {
|
||||||
Indexer indexer = new Indexer();
|
Indexer indexer = new Indexer();
|
||||||
for ( Class<?> clazz : classes ) {
|
for ( Class<?> clazz : classes ) {
|
||||||
|
|
|
@ -64,6 +64,7 @@ import org.jboss.logging.Logger;
|
||||||
* @author Tomasz Bech
|
* @author Tomasz Bech
|
||||||
* @author Stephanie Pau at Markit Group Plc
|
* @author Stephanie Pau at Markit Group Plc
|
||||||
* @author Hernán Chanfreau
|
* @author Hernán Chanfreau
|
||||||
|
* @author Lukasz Antoniak (lukasz dot antoniak at gmail dot com)
|
||||||
*/
|
*/
|
||||||
public final class AuditMetadataGenerator {
|
public final class AuditMetadataGenerator {
|
||||||
|
|
||||||
|
@ -331,7 +332,8 @@ public final class AuditMetadataGenerator {
|
||||||
// Checking if there is a discriminator column
|
// Checking if there is a discriminator column
|
||||||
if (pc.getDiscriminator() != null) {
|
if (pc.getDiscriminator() != null) {
|
||||||
Element discriminator_element = class_mapping.addElement("discriminator");
|
Element discriminator_element = class_mapping.addElement("discriminator");
|
||||||
MetadataTools.addColumns(discriminator_element, pc.getDiscriminator().getColumnIterator());
|
// Database column or SQL formula allowed to distinguish entity types
|
||||||
|
MetadataTools.addColumnsOrFormulas(discriminator_element, pc.getDiscriminator().getColumnIterator());
|
||||||
discriminator_element.addAttribute("type", pc.getDiscriminator().getType().getName());
|
discriminator_element.addAttribute("type", pc.getDiscriminator().getType().getName());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -29,9 +29,11 @@ import org.dom4j.Document;
|
||||||
import org.dom4j.Element;
|
import org.dom4j.Element;
|
||||||
import org.hibernate.envers.tools.StringTools;
|
import org.hibernate.envers.tools.StringTools;
|
||||||
import org.hibernate.mapping.Column;
|
import org.hibernate.mapping.Column;
|
||||||
|
import org.hibernate.mapping.Formula;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Adam Warski (adam at warski dot org)
|
* @author Adam Warski (adam at warski dot org)
|
||||||
|
* @author Lukasz Antoniak (lukasz dot antoniak at gmail dot com)
|
||||||
*/
|
*/
|
||||||
public class MetadataTools {
|
public class MetadataTools {
|
||||||
public static Element addNativelyGeneratedId(Element parent, String name, String type) {
|
public static Element addNativelyGeneratedId(Element parent, String name, String type) {
|
||||||
|
@ -182,12 +184,22 @@ public class MetadataTools {
|
||||||
|
|
||||||
public static void addColumns(Element any_mapping, Iterator<Column> columns) {
|
public static void addColumns(Element any_mapping, Iterator<Column> columns) {
|
||||||
while (columns.hasNext()) {
|
while (columns.hasNext()) {
|
||||||
Column column = columns.next();
|
addColumn(any_mapping, columns.next());
|
||||||
addColumn(any_mapping, column.getName(), column.getLength(), column.getScale(), column.getPrecision(),
|
|
||||||
column.getSqlType(), column.getCustomRead(), column.getCustomWrite());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds <code>column</code> element with the following attributes (unless empty): <code>name</code>,
|
||||||
|
* <code>length</code>, <code>scale</code>, <code>precision</code>, <code>sql-type</code>, <code>read</code>
|
||||||
|
* and <code>write</code>.
|
||||||
|
* @param any_mapping Parent element.
|
||||||
|
* @param column Column descriptor.
|
||||||
|
*/
|
||||||
|
public static void addColumn(Element any_mapping, Column column) {
|
||||||
|
addColumn(any_mapping, column.getName(), column.getLength(), column.getScale(), column.getPrecision(),
|
||||||
|
column.getSqlType(), column.getCustomRead(), column.getCustomWrite());
|
||||||
|
}
|
||||||
|
|
||||||
@SuppressWarnings({"unchecked"})
|
@SuppressWarnings({"unchecked"})
|
||||||
private static void changeNamesInColumnElement(Element element, ColumnNameIterator columnNameIterator) {
|
private static void changeNamesInColumnElement(Element element, ColumnNameIterator columnNameIterator) {
|
||||||
Iterator<Element> properties = element.elementIterator();
|
Iterator<Element> properties = element.elementIterator();
|
||||||
|
@ -228,6 +240,32 @@ public class MetadataTools {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds <code>formula</code> element.
|
||||||
|
* @param element Parent element.
|
||||||
|
* @param formula Formula descriptor.
|
||||||
|
*/
|
||||||
|
public static void addFormula(Element element, Formula formula) {
|
||||||
|
element.addElement("formula").setText(formula.getText());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds all <code>column</code> or <code>formula</code> elements.
|
||||||
|
* @param element Parent element.
|
||||||
|
* @param columnIterator Iterator pointing at {@link org.hibernate.mapping.Column} and/or
|
||||||
|
* {@link org.hibernate.mapping.Formula} objects.
|
||||||
|
*/
|
||||||
|
public static void addColumnsOrFormulas(Element element, Iterator columnIterator) {
|
||||||
|
while (columnIterator.hasNext()) {
|
||||||
|
Object o = columnIterator.next();
|
||||||
|
if (o instanceof Column) {
|
||||||
|
addColumn(element, (Column) o);
|
||||||
|
} else if (o instanceof Formula) {
|
||||||
|
addFormula(element, (Formula) o);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An iterator over column names.
|
* An iterator over column names.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -0,0 +1,59 @@
|
||||||
|
package org.hibernate.envers.test.integration.inheritance.single.discriminatorformula;
|
||||||
|
|
||||||
|
import org.hibernate.envers.Audited;
|
||||||
|
|
||||||
|
import javax.persistence.DiscriminatorValue;
|
||||||
|
import javax.persistence.Entity;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Lukasz Antoniak (lukasz dot antoniak at gmail dot com)
|
||||||
|
*/
|
||||||
|
@Entity
|
||||||
|
@DiscriminatorValue(ClassTypeEntity.CHILD_TYPE)
|
||||||
|
@Audited
|
||||||
|
public class ChildEntity extends ParentEntity {
|
||||||
|
private String specificData;
|
||||||
|
|
||||||
|
public ChildEntity() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public ChildEntity(Long typeId, String data, String specificData) {
|
||||||
|
super(typeId, data);
|
||||||
|
this.specificData = specificData;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ChildEntity(Long id, Long typeId, String data, String specificData) {
|
||||||
|
super(id, typeId, data);
|
||||||
|
this.specificData = specificData;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (this == o) return true;
|
||||||
|
if (!(o instanceof ChildEntity)) return false;
|
||||||
|
if (!super.equals(o)) return false;
|
||||||
|
|
||||||
|
ChildEntity that = (ChildEntity) o;
|
||||||
|
|
||||||
|
if (specificData != null ? !specificData.equals(that.specificData) : that.specificData != null) return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int hashCode() {
|
||||||
|
int result = super.hashCode();
|
||||||
|
result = 31 * result + (specificData != null ? specificData.hashCode() : 0);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String toString() {
|
||||||
|
return "ChildEntity(id = " + id + ", typeId = " + typeId + ", data = " + data + ", specificData = " + specificData + ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSpecificData() {
|
||||||
|
return specificData;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSpecificData(String specificData) {
|
||||||
|
this.specificData = specificData;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,59 @@
|
||||||
|
package org.hibernate.envers.test.integration.inheritance.single.discriminatorformula;
|
||||||
|
|
||||||
|
import javax.persistence.Entity;
|
||||||
|
import javax.persistence.GeneratedValue;
|
||||||
|
import javax.persistence.Id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Lukasz Antoniak (lukasz dot antoniak at gmail dot com)
|
||||||
|
*/
|
||||||
|
@Entity
|
||||||
|
public class ClassTypeEntity {
|
||||||
|
public static final String PARENT_TYPE = "Parent";
|
||||||
|
public static final String CHILD_TYPE = "Child";
|
||||||
|
|
||||||
|
@Id
|
||||||
|
@GeneratedValue
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
private String type;
|
||||||
|
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (this == o) return true;
|
||||||
|
if (!(o instanceof ClassTypeEntity)) return false;
|
||||||
|
|
||||||
|
ClassTypeEntity that = (ClassTypeEntity) o;
|
||||||
|
|
||||||
|
if (id != null ? !id.equals(that.id) : that.id != null) return false;
|
||||||
|
if (type != null ? !type.equals(that.type) : that.type != null) return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int hashCode() {
|
||||||
|
int result = super.hashCode();
|
||||||
|
result = 31 * result + (id != null ? id.hashCode() : 0);
|
||||||
|
result = 31 * result + (type != null ? type.hashCode() : 0);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String toString() {
|
||||||
|
return "ClassTypeEntity(id = " + id + ", type = " + type + ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
public Long getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setId(Long id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getType() {
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setType(String type) {
|
||||||
|
this.type = type;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,131 @@
|
||||||
|
package org.hibernate.envers.test.integration.inheritance.single.discriminatorformula;
|
||||||
|
|
||||||
|
import org.hibernate.ejb.Ejb3Configuration;
|
||||||
|
import org.hibernate.envers.test.AbstractEntityTest;
|
||||||
|
import org.hibernate.envers.test.Priority;
|
||||||
|
import org.hibernate.mapping.Formula;
|
||||||
|
import org.hibernate.mapping.PersistentClass;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import javax.persistence.EntityManager;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Lukasz Antoniak (lukasz dot antoniak at gmail dot com)
|
||||||
|
*/
|
||||||
|
public class DiscriminatorFormulaTest extends AbstractEntityTest {
|
||||||
|
private PersistentClass parentAudit = null;
|
||||||
|
private ChildEntity childVer1 = null;
|
||||||
|
private ChildEntity childVer2 = null;
|
||||||
|
private ParentEntity parentVer1 = null;
|
||||||
|
private ParentEntity parentVer2 = null;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void configure(Ejb3Configuration cfg) {
|
||||||
|
cfg.addAnnotatedClass(ClassTypeEntity.class);
|
||||||
|
cfg.addAnnotatedClass(ParentEntity.class);
|
||||||
|
cfg.addAnnotatedClass(ChildEntity.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Priority(10)
|
||||||
|
public void initData() {
|
||||||
|
parentAudit = getCfg().getClassMapping("org.hibernate.envers.test.integration.inheritance.single.discriminatorformula.ParentEntity_AUD");
|
||||||
|
|
||||||
|
EntityManager em = getEntityManager();
|
||||||
|
|
||||||
|
// Child entity type
|
||||||
|
em.getTransaction().begin();
|
||||||
|
ClassTypeEntity childType = new ClassTypeEntity();
|
||||||
|
childType.setType(ClassTypeEntity.CHILD_TYPE);
|
||||||
|
em.persist(childType);
|
||||||
|
Long childTypeId = childType.getId();
|
||||||
|
em.getTransaction().commit();
|
||||||
|
|
||||||
|
// Parent entity type
|
||||||
|
em.getTransaction().begin();
|
||||||
|
ClassTypeEntity parentType = new ClassTypeEntity();
|
||||||
|
parentType.setType(ClassTypeEntity.PARENT_TYPE);
|
||||||
|
em.persist(parentType);
|
||||||
|
Long parentTypeId = parentType.getId();
|
||||||
|
em.getTransaction().commit();
|
||||||
|
|
||||||
|
// Child Rev 1
|
||||||
|
em.getTransaction().begin();
|
||||||
|
ChildEntity child = new ChildEntity(childTypeId, "Child data", "Child specific data");
|
||||||
|
em.persist(child);
|
||||||
|
Long childId = child.getId();
|
||||||
|
em.getTransaction().commit();
|
||||||
|
|
||||||
|
// Parent Rev 2
|
||||||
|
em.getTransaction().begin();
|
||||||
|
ParentEntity parent = new ParentEntity(parentTypeId, "Parent data");
|
||||||
|
em.persist(parent);
|
||||||
|
Long parentId = parent.getId();
|
||||||
|
em.getTransaction().commit();
|
||||||
|
|
||||||
|
// Child Rev 3
|
||||||
|
em.getTransaction().begin();
|
||||||
|
child = em.find(ChildEntity.class, childId);
|
||||||
|
child.setData("Child data modified");
|
||||||
|
em.getTransaction().commit();
|
||||||
|
|
||||||
|
// Parent Rev 4
|
||||||
|
em.getTransaction().begin();
|
||||||
|
parent = em.find(ParentEntity.class, parentId);
|
||||||
|
parent.setData("Parent data modified");
|
||||||
|
em.getTransaction().commit();
|
||||||
|
|
||||||
|
childVer1 = new ChildEntity(childId, childTypeId, "Child data", "Child specific data");
|
||||||
|
childVer2 = new ChildEntity(childId, childTypeId, "Child data modified", "Child specific data");
|
||||||
|
parentVer1 = new ParentEntity(parentId, parentTypeId, "Parent data");
|
||||||
|
parentVer2 = new ParentEntity(parentId, parentTypeId, "Parent data modified");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDiscriminatorFormulaInAuditTable() {
|
||||||
|
assert parentAudit.getDiscriminator().hasFormula();
|
||||||
|
Iterator iterator = parentAudit.getDiscriminator().getColumnIterator();
|
||||||
|
while (iterator.hasNext()) {
|
||||||
|
Object o = iterator.next();
|
||||||
|
if (o instanceof Formula) {
|
||||||
|
Formula formula = (Formula) o;
|
||||||
|
assert formula.getText().equals(ParentEntity.DISCRIMINATOR_QUERY);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assert false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRevisionsCounts() {
|
||||||
|
assert Arrays.asList(1, 3).equals(getAuditReader().getRevisions(ChildEntity.class, childVer1.getId()));
|
||||||
|
assert Arrays.asList(2, 4).equals(getAuditReader().getRevisions(ParentEntity.class, parentVer1.getId()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testHistoryOfParent() {
|
||||||
|
assert getAuditReader().find(ParentEntity.class, parentVer1.getId(), 2).equals(parentVer1);
|
||||||
|
assert getAuditReader().find(ParentEntity.class, parentVer2.getId(), 4).equals(parentVer2);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testHistoryOfChild() {
|
||||||
|
assert getAuditReader().find(ChildEntity.class, childVer1.getId(), 1).equals(childVer1);
|
||||||
|
assert getAuditReader().find(ChildEntity.class, childVer2.getId(), 3).equals(childVer2);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testPolymorphicQuery() {
|
||||||
|
assert getAuditReader().createQuery().forEntitiesAtRevision(ChildEntity.class, 1).getSingleResult().equals(childVer1);
|
||||||
|
assert getAuditReader().createQuery().forEntitiesAtRevision(ParentEntity.class, 1).getSingleResult().equals(childVer1);
|
||||||
|
|
||||||
|
List childEntityRevisions = getAuditReader().createQuery().forRevisionsOfEntity(ChildEntity.class, true, false).getResultList();
|
||||||
|
assert Arrays.asList(childVer1, childVer2).equals(childEntityRevisions);
|
||||||
|
|
||||||
|
List parentEntityRevisions = getAuditReader().createQuery().forRevisionsOfEntity(ParentEntity.class, true, false).getResultList();
|
||||||
|
assert Arrays.asList(childVer1, parentVer1, childVer2, parentVer2).equals(parentEntityRevisions);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,91 @@
|
||||||
|
package org.hibernate.envers.test.integration.inheritance.single.discriminatorformula;
|
||||||
|
|
||||||
|
import org.hibernate.annotations.DiscriminatorFormula;
|
||||||
|
import org.hibernate.envers.Audited;
|
||||||
|
|
||||||
|
import javax.persistence.DiscriminatorValue;
|
||||||
|
import javax.persistence.Entity;
|
||||||
|
import javax.persistence.GeneratedValue;
|
||||||
|
import javax.persistence.Id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Lukasz Antoniak (lukasz dot antoniak at gmail dot com)
|
||||||
|
*/
|
||||||
|
@Entity
|
||||||
|
@DiscriminatorFormula(ParentEntity.DISCRIMINATOR_QUERY)
|
||||||
|
@DiscriminatorValue(ClassTypeEntity.PARENT_TYPE)
|
||||||
|
@Audited
|
||||||
|
public class ParentEntity {
|
||||||
|
public static final String DISCRIMINATOR_QUERY = "(SELECT c.type FROM ClassTypeEntity c WHERE c.id = typeId)";
|
||||||
|
|
||||||
|
@Id
|
||||||
|
@GeneratedValue
|
||||||
|
protected Long id;
|
||||||
|
|
||||||
|
protected Long typeId;
|
||||||
|
|
||||||
|
protected String data;
|
||||||
|
|
||||||
|
public ParentEntity() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public ParentEntity(Long typeId, String data) {
|
||||||
|
this.typeId = typeId;
|
||||||
|
this.data = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ParentEntity(Long id, Long typeId, String data) {
|
||||||
|
this.id = id;
|
||||||
|
this.typeId = typeId;
|
||||||
|
this.data = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (this == o) return true;
|
||||||
|
if (!(o instanceof ParentEntity)) return false;
|
||||||
|
|
||||||
|
ParentEntity that = (ParentEntity) o;
|
||||||
|
|
||||||
|
if (id != null ? !id.equals(that.id) : that.id != null) return false;
|
||||||
|
if (typeId != null ? !typeId.equals(that.id) : that.typeId != null) return false;
|
||||||
|
if (data != null ? !data.equals(that.data) : that.data != null) return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int hashCode() {
|
||||||
|
int result;
|
||||||
|
result = (id != null ? id.hashCode() : 0);
|
||||||
|
result = 31 * result + (typeId != null ? typeId.hashCode() : 0);
|
||||||
|
result = 31 * result + (data != null ? data.hashCode() : 0);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String toString() {
|
||||||
|
return "ParentEntity(id = " + id + ", typeId = " + typeId + ", data = " + data + ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
public Long getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setId(Long id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Long getTypeId() {
|
||||||
|
return typeId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTypeId(Long typeId) {
|
||||||
|
this.typeId = typeId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getData() {
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setData(String data) {
|
||||||
|
this.data = data;
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue