HHH-6173 Some initial refactorings to start processing @Embedded and @Embeddable

This commit is contained in:
Hardy Ferentschik 2011-06-16 18:40:26 +02:00
parent 7700719306
commit 1e88107f3e
10 changed files with 223 additions and 61 deletions

View File

@ -24,7 +24,7 @@
package org.hibernate.metamodel.domain;
/**
* Desribes an attribute.
* Describes an attribute.
*
* @author Steve Ebersole
*/

View File

@ -120,7 +120,6 @@ public class AnnotationBinder implements Binder {
return classLoaderService;
}
@Override
public void bindIndependentMetadata(MetadataSources sources) {
TypeDefBinder.bind( metadata, index );

View File

@ -0,0 +1,56 @@
/*
* 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.entity;
import org.jboss.jandex.Index;
import org.hibernate.service.ServiceRegistry;
import org.hibernate.service.classloading.spi.ClassLoaderService;
/**
* Helper class for keeping some context information needed during the processing of mapped classes.
*
* @author Hardy Ferentschik
*/
public class AnnotationBindingContext {
private final ServiceRegistry serviceRegistry;
private final Index index;
private ClassLoaderService classLoaderService;
public AnnotationBindingContext(Index index, ServiceRegistry serviceRegistry) {
this.index = index;
this.serviceRegistry = serviceRegistry;
}
public ClassLoaderService classLoaderService() {
if ( classLoaderService == null ) {
classLoaderService = serviceRegistry.getService( ClassLoaderService.class );
}
return classLoaderService;
}
}

View File

@ -41,16 +41,16 @@ import org.hibernate.metamodel.source.annotations.util.JandexHelper;
* @author Hardy Ferentschik
*/
public class AssociationAttribute extends SimpleAttribute {
private final AssociationType associationType;
private final AttributeType associationType;
private final boolean ignoreNotFound;
private final String referencedEntityType;
private final Set<CascadeType> cascadeTypes;
public static AssociationAttribute createAssociationAttribute(String name, String type, AssociationType associationType, Map<DotName, List<AnnotationInstance>> annotations) {
public static AssociationAttribute createAssociationAttribute(String name, String type, AttributeType associationType, Map<DotName, List<AnnotationInstance>> annotations) {
return new AssociationAttribute( name, type, associationType, annotations );
}
private AssociationAttribute(String name, String type, AssociationType associationType, Map<DotName, List<AnnotationInstance>> annotations) {
private AssociationAttribute(String name, String type, AttributeType associationType, Map<DotName, List<AnnotationInstance>> annotations) {
super( name, type, annotations, false );
this.associationType = associationType;
this.ignoreNotFound = ignoreNotFound();
@ -72,7 +72,7 @@ public class AssociationAttribute extends SimpleAttribute {
return referencedEntityType;
}
public AssociationType getAssociationType() {
public AttributeType getAssociationType() {
return associationType;
}

View File

@ -28,18 +28,21 @@ import org.jboss.jandex.DotName;
import org.hibernate.metamodel.source.annotations.JPADotNames;
/**
* An enum defining the type of mapped attribute.
*
* @author Hardy Ferentschik
*/
public enum AssociationType {
NO_ASSOCIATION( null ),
public enum AttributeType {
BASIC( null ),
ONE_TO_ONE( JPADotNames.ONE_TO_ONE ),
ONE_TO_MANY( JPADotNames.ONE_TO_MANY ),
MANY_TO_ONE( JPADotNames.MANY_TO_ONE ),
MANY_TO_MANY( JPADotNames.MANY_TO_MANY );
MANY_TO_MANY( JPADotNames.MANY_TO_MANY ),
EMBEDDED( JPADotNames.EMBEDDED );
private final DotName annotationDotName;
AssociationType(DotName annotationDotName) {
AttributeType(DotName annotationDotName) {
this.annotationDotName = annotationDotName;
}

View File

@ -29,6 +29,7 @@ import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Collections;
import java.util.EnumMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
@ -55,8 +56,6 @@ import org.hibernate.metamodel.binding.InheritanceType;
import org.hibernate.metamodel.source.annotations.JPADotNames;
import org.hibernate.metamodel.source.annotations.util.JandexHelper;
import org.hibernate.metamodel.source.annotations.util.ReflectionHelper;
import org.hibernate.service.ServiceRegistry;
import org.hibernate.service.classloading.spi.ClassLoaderService;
/**
* Represents an entity, mapped superclass or component configured via annotations/xml.
@ -68,7 +67,15 @@ public class ConfiguredClass {
* The parent of this configured class or {@code null} in case this configured class is the root of a hierarchy.
*/
private final ConfiguredClass parent;
/**
* The Jandex class info for this configured class. Provides access to the annotation defined on this configured class.
*/
private final ClassInfo classInfo;
/**
* The actual java type.
*/
private final Class<?> clazz;
private final boolean isRoot;
@ -83,19 +90,24 @@ public class ConfiguredClass {
private final IdType idType;
private final Map<String, MappedAttribute> mappedAttributes;
private final Set<String> transientFieldNames = new HashSet<String>();
private final Set<String> transientMethodNames = new HashSet<String>();
private final AnnotationBindingContext context;
public ConfiguredClass(ClassInfo info,
ConfiguredClass parent,
AccessType hierarchyAccessType,
InheritanceType inheritanceType,
ServiceRegistry serviceRegistry,
ResolvedTypeWithMembers resolvedType) {
ResolvedTypeWithMembers resolvedType,
AnnotationBindingContext context) {
this.context = context;
this.classInfo = info;
this.parent = parent;
this.isRoot = parent == null;
this.hierarchyAccessType = hierarchyAccessType;
this.inheritanceType = inheritanceType;
this.clazz = serviceRegistry.getService( ClassLoaderService.class ).classForName( info.toString() );
this.clazz = context.classLoaderService().classForName( info.toString() );
this.configuredClassType = determineType();
this.classAccessType = determineClassAccessType();
@ -104,6 +116,9 @@ public class ConfiguredClass {
this.hasOwnTable = definesItsOwnTable();
this.primaryTableName = determinePrimaryTableName();
// find transient field and method names
findTransientFieldAndMethodNames();
List<MappedAttribute> simpleProps = collectAttributes( resolvedType );
// make sure the properties are ordered by property name
Collections.sort( simpleProps );
@ -213,11 +228,6 @@ public class ConfiguredClass {
* @return A list of the persistent properties of this configured class
*/
private List<MappedAttribute> collectAttributes(ResolvedTypeWithMembers resolvedTypes) {
// create sets of transient field and method names
Set<String> transientFieldNames = new HashSet<String>();
Set<String> transientMethodNames = new HashSet<String>();
populateTransientFieldAndMethodLists( transientFieldNames, transientMethodNames );
// use the class mate library to generic types
ResolvedTypeWithMembers resolvedType = null;
for ( HierarchicType hierarchicType : resolvedTypes.allTypesAndOverrides() ) {
@ -381,15 +391,19 @@ public class ConfiguredClass {
);
MappedAttribute attribute;
AssociationType associationType = determineAssociationType( annotations );
switch ( associationType ) {
case NO_ASSOCIATION: {
AttributeType attributeType = determineAttributeType( annotations );
switch ( attributeType ) {
case BASIC: {
attribute = SimpleAttribute.createSimpleAttribute( name, ( (Class) type ).getName(), annotations );
break;
}
case EMBEDDED: {
throw new HibernateException( "foo" );
}
// TODO handle the different association types
default: {
attribute = AssociationAttribute.createAssociationAttribute(
name, ( (Class) type ).getName(), associationType, annotations
name, ( (Class) type ).getName(), attributeType, annotations
);
}
}
@ -397,26 +411,47 @@ public class ConfiguredClass {
return attribute;
}
private AssociationType determineAssociationType(Map<DotName, List<AnnotationInstance>> annotations) {
AnnotationInstance oneToOne = JandexHelper.getSingleAnnotation( annotations, JPADotNames.ONE_TO_ONE );
AnnotationInstance oneToMany = JandexHelper.getSingleAnnotation( annotations, JPADotNames.ONE_TO_MANY );
AnnotationInstance manyToOne = JandexHelper.getSingleAnnotation( annotations, JPADotNames.MANY_TO_ONE );
AnnotationInstance manyToMany = JandexHelper.getSingleAnnotation( annotations, JPADotNames.MANY_TO_MANY );
/**
* Given the annotations defined on a persistent attribute this methods determines the attribute type.
*
* @param annotations the annotations defined on the persistent attribute
*
* @return an instance of the {@code AttributeType} enum
*/
private AttributeType determineAttributeType(Map<DotName, List<AnnotationInstance>> annotations) {
EnumMap<AttributeType, AnnotationInstance> discoveredAttributeTypes =
new EnumMap<AttributeType, AnnotationInstance>( AttributeType.class );
if ( oneToOne == null && oneToMany == null && manyToOne == null && manyToMany == null ) {
return AssociationType.NO_ASSOCIATION;
AnnotationInstance oneToOne = JandexHelper.getSingleAnnotation( annotations, JPADotNames.ONE_TO_ONE );
if ( oneToOne != null ) {
discoveredAttributeTypes.put( AttributeType.ONE_TO_ONE, oneToOne );
}
else if ( oneToOne != null && oneToMany == null && manyToOne == null && manyToMany == null ) {
return AssociationType.ONE_TO_ONE;
AnnotationInstance oneToMany = JandexHelper.getSingleAnnotation( annotations, JPADotNames.ONE_TO_MANY );
if ( oneToMany != null ) {
discoveredAttributeTypes.put( AttributeType.ONE_TO_MANY, oneToMany );
}
else if ( oneToOne == null && oneToMany != null && manyToOne == null && manyToMany == null ) {
return AssociationType.ONE_TO_MANY;
AnnotationInstance manyToOne = JandexHelper.getSingleAnnotation( annotations, JPADotNames.MANY_TO_ONE );
if ( manyToOne != null ) {
discoveredAttributeTypes.put( AttributeType.MANY_TO_ONE, manyToOne );
}
else if ( oneToOne == null && oneToMany == null && manyToOne != null && manyToMany == null ) {
return AssociationType.MANY_TO_ONE;
AnnotationInstance manyToMany = JandexHelper.getSingleAnnotation( annotations, JPADotNames.MANY_TO_MANY );
if ( manyToMany != null ) {
discoveredAttributeTypes.put( AttributeType.MANY_TO_MANY, manyToMany );
}
else if ( oneToOne == null && oneToMany == null && manyToOne == null && manyToMany != null ) {
return AssociationType.MANY_TO_MANY;
AnnotationInstance embedded = JandexHelper.getSingleAnnotation( annotations, JPADotNames.EMBEDDED );
if ( embedded != null ) {
discoveredAttributeTypes.put( AttributeType.EMBEDDED, embedded );
}
if ( discoveredAttributeTypes.size() == 0 ) {
return AttributeType.BASIC;
}
else if ( discoveredAttributeTypes.size() == 1 ) {
return discoveredAttributeTypes.keySet().iterator().next();
}
else {
throw new AnnotationException( "More than one association type configured for property " + getName() + " of class " + getName() );
@ -435,11 +470,8 @@ public class ConfiguredClass {
/**
* 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) {
private void findTransientFieldAndMethodNames() {
List<AnnotationInstance> transientMembers = classInfo.annotations().get( JPADotNames.TRANSIENT );
if ( transientMembers == null ) {
return;
@ -524,5 +556,3 @@ public class ConfiguredClass {
return IdType.NONE;
}
}

View File

@ -39,11 +39,10 @@ import org.hibernate.metamodel.binding.InheritanceType;
import org.hibernate.metamodel.source.annotations.JPADotNames;
import org.hibernate.metamodel.source.annotations.util.JandexHelper;
import org.hibernate.metamodel.source.annotations.util.ReflectionHelper;
import org.hibernate.service.ServiceRegistry;
import org.hibernate.service.classloading.spi.ClassLoaderService;
/**
* Represents the inheritance structure of the configured classes within a class hierarchy.
* Contains information about the access and inheritance type for all classes within a class hierarchy.
*
* @author Hardy Ferentschik
*/
@ -52,23 +51,23 @@ public class ConfiguredClassHierarchy implements Iterable<ConfiguredClass> {
private final InheritanceType inheritanceType;
private final List<ConfiguredClass> configuredClasses;
public static ConfiguredClassHierarchy create(List<ClassInfo> classes, ServiceRegistry serviceRegistry) {
return new ConfiguredClassHierarchy( classes, serviceRegistry );
public static ConfiguredClassHierarchy create(List<ClassInfo> classes, AnnotationBindingContext context) {
return new ConfiguredClassHierarchy( classes, context );
}
private ConfiguredClassHierarchy(List<ClassInfo> classes, ServiceRegistry serviceRegistry) {
private ConfiguredClassHierarchy(List<ClassInfo> classes, AnnotationBindingContext context) {
defaultAccessType = determineDefaultAccessType( classes );
inheritanceType = determineInheritanceType( classes );
ClassLoaderService classLoaderService = serviceRegistry.getService( ClassLoaderService.class );
Class<?> clazz = classLoaderService.classForName( classes.get( classes.size() - 1 ).name().toString() );
ResolvedTypeWithMembers resolvedMembers = ReflectionHelper.resolveMemberTypes( clazz );
// the resolved type for the top level class in the hierarchy
Class<?> clazz = context.classLoaderService().classForName( classes.get( classes.size() - 1 ).name().toString() );
ResolvedTypeWithMembers resolvedType = ReflectionHelper.resolveMemberTypes( clazz );
configuredClasses = new ArrayList<ConfiguredClass>();
ConfiguredClass parent = null;
for ( ClassInfo info : classes ) {
ConfiguredClass configuredClass = new ConfiguredClass(
info, parent, defaultAccessType, inheritanceType, serviceRegistry, resolvedMembers
info, parent, defaultAccessType, inheritanceType, resolvedType, context
);
configuredClasses.add( configuredClass );
parent = configuredClass;
@ -114,7 +113,7 @@ public class ConfiguredClassHierarchy implements Iterable<ConfiguredClass> {
if ( idAnnotations == null || idAnnotations.size() == 0 ) {
continue;
}
accessType = processIdAnnotations( idAnnotations );
accessType = determineAccessTypeByIdPlacement( idAnnotations );
}
if ( accessType == null ) {
@ -124,7 +123,7 @@ public class ConfiguredClassHierarchy implements Iterable<ConfiguredClass> {
return accessType;
}
private AccessType processIdAnnotations(List<AnnotationInstance> idAnnotations) {
private AccessType determineAccessTypeByIdPlacement(List<AnnotationInstance> idAnnotations) {
AccessType accessType = null;
for ( AnnotationInstance annotation : idAnnotations ) {
AccessType tmpAccessType;
@ -217,5 +216,3 @@ public class ConfiguredClassHierarchy implements Iterable<ConfiguredClass> {
return builder.toString();
}
}

View File

@ -39,7 +39,7 @@ import org.hibernate.metamodel.source.annotations.HibernateDotNames;
*
* @author Hardy Ferentschik
*/
public class MappedAttribute implements Comparable<MappedAttribute> {
public abstract class MappedAttribute implements Comparable<MappedAttribute> {
/**
* Annotations defined on the attribute, keyed against the annotation dot name.
*/

View File

@ -36,9 +36,9 @@ import org.jboss.jandex.DotName;
import org.jboss.jandex.Index;
import org.hibernate.AnnotationException;
import org.hibernate.metamodel.source.annotations.entity.ConfiguredClassHierarchy;
import org.hibernate.metamodel.source.annotations.JPADotNames;
import org.hibernate.metamodel.source.annotations.entity.ConfiguredClassHierarchy;
import org.hibernate.metamodel.source.annotations.entity.AnnotationBindingContext;
import org.hibernate.service.ServiceRegistry;
import org.hibernate.service.classloading.spi.ClassLoaderService;
@ -95,11 +95,12 @@ public class ConfiguredClassHierarchyBuilder {
}
}
AnnotationBindingContext context = new AnnotationBindingContext(index, serviceRegistry);
Set<ConfiguredClassHierarchy> hierarchies = new HashSet<ConfiguredClassHierarchy>();
List<List<ClassInfo>> processedList = new ArrayList<List<ClassInfo>>();
for ( List<ClassInfo> classInfoList : processedClassInfos.values() ) {
if ( !processedList.contains( classInfoList ) ) {
hierarchies.add( ConfiguredClassHierarchy.create( classInfoList, serviceRegistry ) );
hierarchies.add( ConfiguredClassHierarchy.create( classInfoList, context ) );
processedList.add( classInfoList );
}
}
@ -107,6 +108,13 @@ public class ConfiguredClassHierarchyBuilder {
return hierarchies;
}
/**
* Checks whether the passed jandex class info needs to be processed.
*
* @param info the jandex class info
*
* @return {@code true} if the class represented by {@code info} is relevant for the JPA mappings, {@code false} otherwise.
*/
private static boolean isConfiguredClass(ClassInfo info) {
boolean isConfiguredClass = true;
AnnotationInstance jpaEntityAnnotation = JandexHelper.getSingleAnnotation( info, JPADotNames.ENTITY );

View File

@ -0,0 +1,69 @@
/*
* 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.entity;
import javax.persistence.Embeddable;
import javax.persistence.Embedded;
import javax.persistence.Entity;
import javax.persistence.Id;
import org.junit.Test;
import org.hibernate.metamodel.binding.EntityBinding;
import org.hibernate.testing.FailureExpected;
import static junit.framework.Assert.assertNotNull;
/**
* Tests for {@code j.p.Embeddable}.
*
* @author Hardy Ferentschik
*/
public class EmbeddableBindingTests extends BaseAnnotationBindingTestCase {
@Test
@FailureExpected(jiraKey = "HHH6173", message = "Under construction")
public void testEmbeddable() {
buildMetadataSources( User.class );
EntityBinding binding = getEntityBinding( User.class );
assertNotNull( binding.getAttributeBinding( "address" ) );
}
@Entity
class User {
@Id
private int id;
@Embedded
private Address address;
}
@Embeddable
class Address {
String street;
String city;
String postCode;
}
}