From 78ad67cff61a5723c90aa0c1f92d9ec3d75240f4 Mon Sep 17 00:00:00 2001 From: Hardy Ferentschik Date: Wed, 13 Apr 2011 17:56:32 +0200 Subject: [PATCH] HHH-6114 Determining inheritance strategy and starting to process mapped properties --- .../source/annotations/ConfiguredClass.java | 40 ++++++-- .../annotations/ConfiguredClassHierarchy.java | 91 ++++++++++++++----- .../source/annotations/JPADotNames.java | 6 +- .../source/annotations/JandexHelper.java | 29 +++++- .../source/annotations/MappedProperty.java | 34 +++++++ .../ConfiguredClassHierarchyBuilderTest.java | 68 ++++++++++++++ 6 files changed, 232 insertions(+), 36 deletions(-) create mode 100644 hibernate-core/src/main/java/org/hibernate/metamodel/source/annotations/MappedProperty.java diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/source/annotations/ConfiguredClass.java b/hibernate-core/src/main/java/org/hibernate/metamodel/source/annotations/ConfiguredClass.java index a238a81bbc..af9834b5c4 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/source/annotations/ConfiguredClass.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/source/annotations/ConfiguredClass.java @@ -23,12 +23,11 @@ */ package org.hibernate.metamodel.source.annotations; -import javax.persistence.Entity; -import javax.persistence.MappedSuperclass; +import java.util.List; +import javax.persistence.AccessType; import org.jboss.jandex.AnnotationInstance; import org.jboss.jandex.ClassInfo; -import org.jboss.jandex.DotName; import org.hibernate.AnnotationException; @@ -39,19 +38,20 @@ import org.hibernate.AnnotationException; */ public class ConfiguredClass { private final ClassInfo classInfo; + private final AccessType classAccessType; private final ConfiguredClassHierarchy hierarchy; + private final boolean isMappedSuperClass; + private final List mappedProperties; public ConfiguredClass(ClassInfo info, ConfiguredClassHierarchy hierarchy) { this.classInfo = info; this.hierarchy = hierarchy; - init(); - } - private void init() { //@Entity and @MappedSuperclass on the same class leads to a NPE down the road AnnotationInstance jpaEntityAnnotation = JandexHelper.getSingleAnnotation( classInfo, JPADotNames.ENTITY ); - AnnotationInstance mappedSuperClassAnnotation = JandexHelper.getSingleAnnotation( classInfo, JPADotNames.MAPPED_SUPER_CLASS ); - AnnotationInstance hibernateEntityAnnotation = JandexHelper.getSingleAnnotation( classInfo, JPADotNames.HIBERNATE_ENTITY ); + AnnotationInstance mappedSuperClassAnnotation = JandexHelper.getSingleAnnotation( + classInfo, JPADotNames.MAPPED_SUPER_CLASS + ); if ( jpaEntityAnnotation != null && mappedSuperClassAnnotation != null ) { throw new AnnotationException( @@ -60,13 +60,19 @@ public class ConfiguredClass { ); } - + isMappedSuperClass = mappedSuperClassAnnotation != null; + classAccessType = determineClassAccessType( hierarchy.getDefaultAccessType() ); + mappedProperties = collectMappedProperties(); } public ClassInfo getClassInfo() { return classInfo; } + public boolean isMappedSuperClass() { + return isMappedSuperClass; + } + @Override public String toString() { final StringBuilder sb = new StringBuilder(); @@ -75,6 +81,22 @@ public class ConfiguredClass { sb.append( '}' ); 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 collectMappedProperties() { + return null; //To change body of created methods use File | Settings | File Templates. + } } diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/source/annotations/ConfiguredClassHierarchy.java b/hibernate-core/src/main/java/org/hibernate/metamodel/source/annotations/ConfiguredClassHierarchy.java index ae033fb7fc..2169706fac 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/source/annotations/ConfiguredClassHierarchy.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/source/annotations/ConfiguredClassHierarchy.java @@ -66,8 +66,7 @@ public class ConfiguredClassHierarchy implements Iterable { /** * @return An iterator iterating in top down manner over the configured classes in this hierarchy. */ - public Iterator iterator - () { + public Iterator iterator() { return configuredClasses.iterator(); } @@ -97,28 +96,7 @@ public class ConfiguredClassHierarchy implements Iterable { if ( idAnnotations == null || idAnnotations.size() == 0 ) { continue; } - - 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() ); - } - } - } + accessType = processIdAnnotations( idAnnotations ); } if ( accessType == null ) { @@ -128,8 +106,71 @@ public class ConfiguredClassHierarchy implements Iterable { return accessType; } + private AccessType processIdAnnotations(List idAnnotations) { + 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 " + hierarchyListString() ); + } + } + } + return accessType; + } + private InheritanceType determineInheritanceType() { - return null; //To change body of created methods use File | Settings | File Templates. + Iterator iter = iterator(); + InheritanceType inheritanceType = null; + while ( iter.hasNext() ) { + ConfiguredClass configuredClass = iter.next(); + ClassInfo info = configuredClass.getClassInfo(); + 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 hierarchy " + hierarchyListString() + ); + } + } + } + + if ( inheritanceType == null ) { + // default inheritance type is single table + inheritanceType = InheritanceType.SINGLE_TABLE; + } + + return inheritanceType; } private AccessType throwIdNotFoundAnnotationException() { diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/source/annotations/JPADotNames.java b/hibernate-core/src/main/java/org/hibernate/metamodel/source/annotations/JPADotNames.java index ebf05abdb9..2b8ffa225f 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/source/annotations/JPADotNames.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/source/annotations/JPADotNames.java @@ -23,8 +23,10 @@ */ package org.hibernate.metamodel.source.annotations; +import javax.persistence.Access; import javax.persistence.Entity; import javax.persistence.Id; +import javax.persistence.Inheritance; import javax.persistence.MappedSuperclass; import org.jboss.jandex.DotName; @@ -39,8 +41,10 @@ public interface JPADotNames { 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 ID = DotName.createSimple( Id.class.getName() ); + public static final DotName INHERITANCE = DotName.createSimple( Inheritance.class.getName() ); + public static final DotName ACCESS = DotName.createSimple( Access.class.getName() ); + public static final DotName ID = DotName.createSimple( Id.class.getName() ); } diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/source/annotations/JandexHelper.java b/hibernate-core/src/main/java/org/hibernate/metamodel/source/annotations/JandexHelper.java index 81cbcd72e2..7694420ae6 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/source/annotations/JandexHelper.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/source/annotations/JandexHelper.java @@ -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; import java.util.List; @@ -9,6 +32,8 @@ import org.jboss.jandex.DotName; import org.hibernate.AssertionFailure; /** + * Utility methods for working with the jandex annotation index. + * * @author Hardy Ferentschik */ public class JandexHelper { @@ -34,7 +59,9 @@ public class JandexHelper { } else { 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." ); } } diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/source/annotations/MappedProperty.java b/hibernate-core/src/main/java/org/hibernate/metamodel/source/annotations/MappedProperty.java new file mode 100644 index 0000000000..bc66cda893 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/source/annotations/MappedProperty.java @@ -0,0 +1,34 @@ +/* + * 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 { +} + + diff --git a/hibernate-core/src/test/java/org/hibernate/metamodel/source/annotations/ConfiguredClassHierarchyBuilderTest.java b/hibernate-core/src/test/java/org/hibernate/metamodel/source/annotations/ConfiguredClassHierarchyBuilderTest.java index 594945abad..71fe0b34fb 100644 --- a/hibernate-core/src/test/java/org/hibernate/metamodel/source/annotations/ConfiguredClassHierarchyBuilderTest.java +++ b/hibernate-core/src/test/java/org/hibernate/metamodel/source/annotations/ConfiguredClassHierarchyBuilderTest.java @@ -8,6 +8,8 @@ import javax.persistence.AccessType; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.Id; +import javax.persistence.Inheritance; +import javax.persistence.InheritanceType; import javax.persistence.MappedSuperclass; import org.jboss.jandex.ClassInfo; @@ -175,6 +177,72 @@ public class ConfiguredClassHierarchyBuilderTest extends BaseUnitTestCase { 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 ); + ConfiguredClassHierarchyBuilder builder = new ConfiguredClassHierarchyBuilder(); + Set hierarchies = builder.createEntityHierarchies( index ); + 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 ); + ConfiguredClassHierarchyBuilder builder = new ConfiguredClassHierarchyBuilder(); + Set hierarchies = builder.createEntityHierarchies( index ); + 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 builder = new ConfiguredClassHierarchyBuilder(); + builder.createEntityHierarchies( index ); + } + private Index indexForClass(Class... classes) { Indexer indexer = new Indexer(); for ( Class clazz : classes ) {