HHH-6114 Determining the default access type of a class hierarchy
This commit is contained in:
parent
4d24c16b49
commit
be7b44c2f7
|
@ -70,12 +70,12 @@ public class AnnotationBinder {
|
||||||
ClassInfo classInfo = entity.getClassInfo();
|
ClassInfo classInfo = entity.getClassInfo();
|
||||||
|
|
||||||
//@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, ConfiguredClass.ENTITY );
|
AnnotationInstance jpaEntityAnnotation = JandexHelper.getSingleAnnotation( classInfo, JPADotNames.ENTITY );
|
||||||
AnnotationInstance mappedSuperClassAnnotation = JandexHelper.getSingleAnnotation(
|
AnnotationInstance mappedSuperClassAnnotation = JandexHelper.getSingleAnnotation(
|
||||||
classInfo, ConfiguredClass.MAPPED_SUPER_CLASS
|
classInfo, JPADotNames.MAPPED_SUPER_CLASS
|
||||||
);
|
);
|
||||||
AnnotationInstance hibernateEntityAnnotation = JandexHelper.getSingleAnnotation(
|
AnnotationInstance hibernateEntityAnnotation = JandexHelper.getSingleAnnotation(
|
||||||
classInfo, ConfiguredClass.HIBERNATE_ENTITY
|
classInfo, JPADotNames.HIBERNATE_ENTITY
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -38,10 +38,6 @@ import org.hibernate.AnnotationException;
|
||||||
* @author Hardy Ferentschik
|
* @author Hardy Ferentschik
|
||||||
*/
|
*/
|
||||||
public class ConfiguredClass {
|
public class ConfiguredClass {
|
||||||
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() );
|
|
||||||
|
|
||||||
private final ClassInfo classInfo;
|
private final ClassInfo classInfo;
|
||||||
private final ConfiguredClassHierarchy hierarchy;
|
private final ConfiguredClassHierarchy hierarchy;
|
||||||
|
|
||||||
|
@ -53,9 +49,9 @@ public class ConfiguredClass {
|
||||||
|
|
||||||
private void 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, ENTITY );
|
AnnotationInstance jpaEntityAnnotation = JandexHelper.getSingleAnnotation( classInfo, JPADotNames.ENTITY );
|
||||||
AnnotationInstance mappedSuperClassAnnotation = JandexHelper.getSingleAnnotation( classInfo, MAPPED_SUPER_CLASS );
|
AnnotationInstance mappedSuperClassAnnotation = JandexHelper.getSingleAnnotation( classInfo, JPADotNames.MAPPED_SUPER_CLASS );
|
||||||
AnnotationInstance hibernateEntityAnnotation = JandexHelper.getSingleAnnotation( classInfo, HIBERNATE_ENTITY );
|
AnnotationInstance hibernateEntityAnnotation = JandexHelper.getSingleAnnotation( classInfo, JPADotNames.HIBERNATE_ENTITY );
|
||||||
|
|
||||||
if ( jpaEntityAnnotation != null && mappedSuperClassAnnotation != null ) {
|
if ( jpaEntityAnnotation != null && mappedSuperClassAnnotation != null ) {
|
||||||
throw new AnnotationException(
|
throw new AnnotationException(
|
||||||
|
|
|
@ -26,16 +26,24 @@ package org.hibernate.metamodel.source.annotations;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import javax.persistence.AccessType;
|
||||||
|
import javax.persistence.InheritanceType;
|
||||||
|
|
||||||
|
import org.jboss.jandex.AnnotationInstance;
|
||||||
import org.jboss.jandex.ClassInfo;
|
import org.jboss.jandex.ClassInfo;
|
||||||
|
import org.jboss.jandex.FieldInfo;
|
||||||
|
import org.jboss.jandex.MethodInfo;
|
||||||
|
|
||||||
import org.hibernate.cfg.AccessType;
|
import org.hibernate.AnnotationException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Represents the inheritance structure of the configured classes within a class hierarchy.
|
||||||
|
*
|
||||||
* @author Hardy Ferentschik
|
* @author Hardy Ferentschik
|
||||||
*/
|
*/
|
||||||
public class ConfiguredClassHierarchy implements Iterable<ConfiguredClass> {
|
public class ConfiguredClassHierarchy implements Iterable<ConfiguredClass> {
|
||||||
private final AccessType defaultAccessType;
|
private final AccessType defaultAccessType;
|
||||||
|
private final InheritanceType inheritanceType;
|
||||||
private final List<ConfiguredClass> configuredClasses;
|
private final List<ConfiguredClass> configuredClasses;
|
||||||
|
|
||||||
ConfiguredClassHierarchy(List<ClassInfo> classes) {
|
ConfiguredClassHierarchy(List<ClassInfo> classes) {
|
||||||
|
@ -44,19 +52,28 @@ public class ConfiguredClassHierarchy implements Iterable<ConfiguredClass> {
|
||||||
configuredClasses.add( new ConfiguredClass( info, this ) );
|
configuredClasses.add( new ConfiguredClass( info, this ) );
|
||||||
}
|
}
|
||||||
defaultAccessType = determineDefaultAccessType();
|
defaultAccessType = determineDefaultAccessType();
|
||||||
|
inheritanceType = determineInheritanceType();
|
||||||
}
|
}
|
||||||
|
|
||||||
private AccessType determineDefaultAccessType() {
|
public AccessType getDefaultAccessType() {
|
||||||
return null;
|
return defaultAccessType;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
public InheritanceType getInheritanceType() {
|
||||||
public Iterator<ConfiguredClass> iterator() {
|
return inheritanceType;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return An iterator iterating in top down manner over the configured classes in this hierarchy.
|
||||||
|
*/
|
||||||
|
public Iterator<ConfiguredClass> iterator
|
||||||
|
() {
|
||||||
return configuredClasses.iterator();
|
return configuredClasses.iterator();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString
|
||||||
|
() {
|
||||||
final StringBuilder sb = new StringBuilder();
|
final StringBuilder sb = new StringBuilder();
|
||||||
sb.append( "ConfiguredClassHierarchy" );
|
sb.append( "ConfiguredClassHierarchy" );
|
||||||
sb.append( "{defaultAccessType=" ).append( defaultAccessType );
|
sb.append( "{defaultAccessType=" ).append( defaultAccessType );
|
||||||
|
@ -64,6 +81,78 @@ public class ConfiguredClassHierarchy implements Iterable<ConfiguredClass> {
|
||||||
sb.append( '}' );
|
sb.append( '}' );
|
||||||
return sb.toString();
|
return sb.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @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
|
||||||
|
* annotations.
|
||||||
|
*/
|
||||||
|
private AccessType determineDefaultAccessType() {
|
||||||
|
Iterator<ConfiguredClass> iter = iterator();
|
||||||
|
AccessType accessType = null;
|
||||||
|
while ( iter.hasNext() ) {
|
||||||
|
ConfiguredClass configuredClass = iter.next();
|
||||||
|
ClassInfo info = configuredClass.getClassInfo();
|
||||||
|
List<AnnotationInstance> idAnnotations = info.annotations().get( JPADotNames.ID );
|
||||||
|
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() );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( accessType == null ) {
|
||||||
|
return throwIdNotFoundAnnotationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
return accessType;
|
||||||
|
}
|
||||||
|
|
||||||
|
private InheritanceType determineInheritanceType() {
|
||||||
|
return null; //To change body of created methods use File | Settings | File Templates.
|
||||||
|
}
|
||||||
|
|
||||||
|
private AccessType throwIdNotFoundAnnotationException() {
|
||||||
|
StringBuilder builder = new StringBuilder();
|
||||||
|
builder.append( "Unable to find Id property for class hierarchy " );
|
||||||
|
builder.append( hierarchyListString() );
|
||||||
|
throw new AnnotationException( builder.toString() );
|
||||||
|
}
|
||||||
|
|
||||||
|
private String hierarchyListString() {
|
||||||
|
Iterator<ConfiguredClass> iter;
|
||||||
|
StringBuilder builder = new StringBuilder();
|
||||||
|
builder.append( "[" );
|
||||||
|
iter = iterator();
|
||||||
|
while ( iter.hasNext() ) {
|
||||||
|
builder.append( iter.next().getClassInfo().name().toString() );
|
||||||
|
if ( iter.hasNext() ) {
|
||||||
|
builder.append( ", " );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
builder.append( "]" );
|
||||||
|
return builder.toString();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,46 @@
|
||||||
|
/*
|
||||||
|
* 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 javax.persistence.Entity;
|
||||||
|
import javax.persistence.Id;
|
||||||
|
import javax.persistence.MappedSuperclass;
|
||||||
|
|
||||||
|
import org.jboss.jandex.DotName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Defines the dot names for the JPA annotations
|
||||||
|
*
|
||||||
|
* @author Hardy Ferentschik
|
||||||
|
*/
|
||||||
|
public interface JPADotNames {
|
||||||
|
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 ID = DotName.createSimple( Id.class.getName() );
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,7 @@ import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
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;
|
||||||
|
@ -19,6 +20,7 @@ import org.hibernate.AnnotationException;
|
||||||
import org.hibernate.testing.junit4.BaseUnitTestCase;
|
import org.hibernate.testing.junit4.BaseUnitTestCase;
|
||||||
|
|
||||||
import static junit.framework.Assert.assertEquals;
|
import static junit.framework.Assert.assertEquals;
|
||||||
|
import static junit.framework.Assert.assertTrue;
|
||||||
import static org.junit.Assert.assertFalse;
|
import static org.junit.Assert.assertFalse;
|
||||||
import static org.junit.Assert.fail;
|
import static org.junit.Assert.fail;
|
||||||
|
|
||||||
|
@ -65,6 +67,22 @@ public class ConfiguredClassHierarchyBuilderTest extends BaseUnitTestCase {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testMappedSuperClass() {
|
public void testMappedSuperClass() {
|
||||||
|
@MappedSuperclass
|
||||||
|
class MappedSuperClass {
|
||||||
|
@Id
|
||||||
|
@GeneratedValue
|
||||||
|
private int id;
|
||||||
|
}
|
||||||
|
|
||||||
|
class UnmappedSubClass extends MappedSuperClass {
|
||||||
|
private String unmappedProperty;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
class MappedSubClass extends UnmappedSubClass {
|
||||||
|
private String mappedProperty;
|
||||||
|
}
|
||||||
|
|
||||||
Index index = indexForClass( MappedSubClass.class, MappedSuperClass.class, UnmappedSubClass.class );
|
Index index = indexForClass( MappedSubClass.class, MappedSuperClass.class, UnmappedSubClass.class );
|
||||||
ConfiguredClassHierarchyBuilder builder = new ConfiguredClassHierarchyBuilder();
|
ConfiguredClassHierarchyBuilder builder = new ConfiguredClassHierarchyBuilder();
|
||||||
Set<ConfiguredClassHierarchy> hierarchies = builder.createEntityHierarchies( index );
|
Set<ConfiguredClassHierarchy> hierarchies = builder.createEntityHierarchies( index );
|
||||||
|
@ -82,11 +100,81 @@ public class ConfiguredClassHierarchyBuilderTest extends BaseUnitTestCase {
|
||||||
|
|
||||||
@Test(expected = AnnotationException.class)
|
@Test(expected = AnnotationException.class)
|
||||||
public void testEntityAndMappedSuperClassAnnotations() {
|
public void testEntityAndMappedSuperClassAnnotations() {
|
||||||
|
@Entity
|
||||||
|
@MappedSuperclass
|
||||||
|
class EntityAndMappedSuperClass {
|
||||||
|
}
|
||||||
|
|
||||||
Index index = indexForClass( EntityAndMappedSuperClass.class );
|
Index index = indexForClass( EntityAndMappedSuperClass.class );
|
||||||
ConfiguredClassHierarchyBuilder builder = new ConfiguredClassHierarchyBuilder();
|
ConfiguredClassHierarchyBuilder builder = new ConfiguredClassHierarchyBuilder();
|
||||||
builder.createEntityHierarchies( index );
|
builder.createEntityHierarchies( index );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test(expected = AnnotationException.class)
|
||||||
|
public void testNoIdAnnotation() {
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
class A {
|
||||||
|
String id;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
class B extends A {
|
||||||
|
}
|
||||||
|
|
||||||
|
Index index = indexForClass( B.class, A.class );
|
||||||
|
ConfiguredClassHierarchyBuilder builder = new ConfiguredClassHierarchyBuilder();
|
||||||
|
builder.createEntityHierarchies( index );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDefaultFieldAccess() {
|
||||||
|
@Entity
|
||||||
|
class A {
|
||||||
|
@Id
|
||||||
|
String id;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
class B extends A {
|
||||||
|
}
|
||||||
|
|
||||||
|
Index index = indexForClass( B.class, A.class );
|
||||||
|
ConfiguredClassHierarchyBuilder builder = new ConfiguredClassHierarchyBuilder();
|
||||||
|
Set<ConfiguredClassHierarchy> hierarchies = builder.createEntityHierarchies( index );
|
||||||
|
assertTrue( hierarchies.size() == 1 );
|
||||||
|
ConfiguredClassHierarchy hierarchy = hierarchies.iterator().next();
|
||||||
|
assertEquals( "Wrong default access type", AccessType.FIELD, hierarchy.getDefaultAccessType() );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDefaultPropertyAccess() {
|
||||||
|
@Entity
|
||||||
|
class A {
|
||||||
|
String id;
|
||||||
|
|
||||||
|
@Id
|
||||||
|
public String getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setId(String id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
class B extends A {
|
||||||
|
}
|
||||||
|
|
||||||
|
Index index = indexForClass( B.class, A.class );
|
||||||
|
ConfiguredClassHierarchyBuilder builder = new ConfiguredClassHierarchyBuilder();
|
||||||
|
Set<ConfiguredClassHierarchy> hierarchies = builder.createEntityHierarchies( index );
|
||||||
|
assertTrue( hierarchies.size() == 1 );
|
||||||
|
ConfiguredClassHierarchy hierarchy = hierarchies.iterator().next();
|
||||||
|
assertEquals( "Wrong default access type", AccessType.PROPERTY, hierarchy.getDefaultAccessType() );
|
||||||
|
}
|
||||||
|
|
||||||
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 ) {
|
||||||
|
@ -121,27 +209,6 @@ public class ConfiguredClassHierarchyBuilderTest extends BaseUnitTestCase {
|
||||||
public class B extends A {
|
public class B extends A {
|
||||||
private String name;
|
private String name;
|
||||||
}
|
}
|
||||||
|
|
||||||
@MappedSuperclass
|
|
||||||
public class MappedSuperClass {
|
|
||||||
@Id
|
|
||||||
@GeneratedValue
|
|
||||||
private int id;
|
|
||||||
}
|
|
||||||
|
|
||||||
public class UnmappedSubClass extends MappedSuperClass {
|
|
||||||
private String unmappedProperty;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Entity
|
|
||||||
public class MappedSubClass extends UnmappedSubClass {
|
|
||||||
private String mappedProperty;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Entity
|
|
||||||
@MappedSuperclass
|
|
||||||
public class EntityAndMappedSuperClass {
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue