HHH-6537 Adding check for @MapsId annotation to AssociationAttribute.

This commit is contained in:
Hardy Ferentschik 2011-08-05 12:32:00 +02:00
parent 21ea415f61
commit 89991f8610
6 changed files with 144 additions and 36 deletions

View File

@ -37,12 +37,15 @@ import org.jboss.jandex.DotName;
import org.hibernate.FetchMode; import org.hibernate.FetchMode;
import org.hibernate.annotations.NotFoundAction; import org.hibernate.annotations.NotFoundAction;
import org.hibernate.mapping.PropertyGeneration; import org.hibernate.mapping.PropertyGeneration;
import org.hibernate.metamodel.source.annotations.AnnotationBindingContext; import org.hibernate.metamodel.source.MappingException;
import org.hibernate.metamodel.source.annotations.EnumConversionHelper;
import org.hibernate.metamodel.source.annotations.HibernateDotNames; import org.hibernate.metamodel.source.annotations.HibernateDotNames;
import org.hibernate.metamodel.source.annotations.JPADotNames;
import org.hibernate.metamodel.source.annotations.JandexHelper; import org.hibernate.metamodel.source.annotations.JandexHelper;
import org.hibernate.metamodel.source.annotations.attribute.type.AttributeTypeResolver; import org.hibernate.metamodel.source.annotations.attribute.type.AttributeTypeResolver;
import org.hibernate.metamodel.source.annotations.attribute.type.AttributeTypeResolverImpl; import org.hibernate.metamodel.source.annotations.attribute.type.AttributeTypeResolverImpl;
import org.hibernate.metamodel.source.annotations.attribute.type.CompositeAttributeTypeResolver; import org.hibernate.metamodel.source.annotations.attribute.type.CompositeAttributeTypeResolver;
import org.hibernate.metamodel.source.annotations.entity.EntityBindingContext;
/** /**
* Represents an association attribute. * Represents an association attribute.
@ -60,6 +63,8 @@ public class AssociationAttribute extends MappedAttribute {
private final boolean isLazy; private final boolean isLazy;
private final boolean isOrphanRemoval; private final boolean isOrphanRemoval;
private final FetchMode fetchMode; private final FetchMode fetchMode;
private final boolean mapsId;
private final String referencedIdAttributeName;
private boolean isInsertable = true; private boolean isInsertable = true;
private boolean isUpdatable = true; private boolean isUpdatable = true;
@ -70,7 +75,7 @@ public class AssociationAttribute extends MappedAttribute {
AttributeNature attributeNature, AttributeNature attributeNature,
String accessType, String accessType,
Map<DotName, List<AnnotationInstance>> annotations, Map<DotName, List<AnnotationInstance>> annotations,
AnnotationBindingContext context) { EntityBindingContext context) {
return new AssociationAttribute( return new AssociationAttribute(
name, name,
attributeType, attributeType,
@ -86,7 +91,7 @@ public class AssociationAttribute extends MappedAttribute {
AttributeNature associationType, AttributeNature associationType,
String accessType, String accessType,
Map<DotName, List<AnnotationInstance>> annotations, Map<DotName, List<AnnotationInstance>> annotations,
AnnotationBindingContext context) { EntityBindingContext context) {
super( name, javaType, accessType, annotations, context ); super( name, javaType, accessType, annotations, context );
this.associationNature = associationType; this.associationNature = associationType;
this.ignoreNotFound = ignoreNotFound(); this.ignoreNotFound = ignoreNotFound();
@ -105,6 +110,8 @@ public class AssociationAttribute extends MappedAttribute {
this.cascadeTypes = determineCascadeTypes( associationAnnotation ); this.cascadeTypes = determineCascadeTypes( associationAnnotation );
this.fetchMode = determineFetchMode(); this.fetchMode = determineFetchMode();
this.referencedIdAttributeName = determineMapsId();
this.mapsId = referencedIdAttributeName != null;
} }
public boolean isIgnoreNotFound() { public boolean isIgnoreNotFound() {
@ -135,6 +142,14 @@ public class AssociationAttribute extends MappedAttribute {
return fetchMode; return fetchMode;
} }
public String getReferencedIdAttributeName() {
return referencedIdAttributeName;
}
public boolean mapsId() {
return mapsId;
}
@Override @Override
public AttributeTypeResolver getHibernateTypeResolver() { public AttributeTypeResolver getHibernateTypeResolver() {
if ( resolver == null ) { if ( resolver == null ) {
@ -271,10 +286,31 @@ public class AssociationAttribute extends MappedAttribute {
"value", "value",
org.hibernate.annotations.FetchMode.class org.hibernate.annotations.FetchMode.class
); );
mode = EnumConversionHelper.annotationFetchModeToHibernateFetchMode( annotationFetchMode );
} }
return mode; return mode;
} }
private String determineMapsId() {
String referencedIdAttributeName;
AnnotationInstance mapsIdAnnotation = JandexHelper.getSingleAnnotation( annotations(), JPADotNames.MAPS_ID );
if ( mapsIdAnnotation == null ) {
return null;
}
if ( !( AttributeNature.MANY_TO_ONE.equals( getAssociationNature() ) || AttributeNature.MANY_TO_ONE
.equals( getAssociationNature() ) ) ) {
throw new MappingException(
"@MapsId can only be specified on a many-to-one or one-to-one associations",
getContext().getOrigin()
);
}
referencedIdAttributeName = JandexHelper.getValue( mapsIdAnnotation, "value", String.class );
return referencedIdAttributeName;
}
} }

View File

@ -40,17 +40,17 @@ import org.hibernate.internal.util.StringHelper;
import org.hibernate.mapping.PropertyGeneration; import org.hibernate.mapping.PropertyGeneration;
import org.hibernate.metamodel.binding.IdGenerator; import org.hibernate.metamodel.binding.IdGenerator;
import org.hibernate.metamodel.source.MappingException; import org.hibernate.metamodel.source.MappingException;
import org.hibernate.metamodel.source.annotations.AnnotationBindingContext; import org.hibernate.metamodel.source.annotations.EnumConversionHelper;
import org.hibernate.metamodel.source.annotations.HibernateDotNames; import org.hibernate.metamodel.source.annotations.HibernateDotNames;
import org.hibernate.metamodel.source.annotations.JPADotNames; import org.hibernate.metamodel.source.annotations.JPADotNames;
import org.hibernate.metamodel.source.annotations.JandexHelper; import org.hibernate.metamodel.source.annotations.JandexHelper;
import org.hibernate.metamodel.source.annotations.EnumConversionHelper;
import org.hibernate.metamodel.source.annotations.attribute.type.AttributeTypeResolver; import org.hibernate.metamodel.source.annotations.attribute.type.AttributeTypeResolver;
import org.hibernate.metamodel.source.annotations.attribute.type.AttributeTypeResolverImpl;
import org.hibernate.metamodel.source.annotations.attribute.type.CompositeAttributeTypeResolver; import org.hibernate.metamodel.source.annotations.attribute.type.CompositeAttributeTypeResolver;
import org.hibernate.metamodel.source.annotations.attribute.type.EnumeratedTypeResolver; import org.hibernate.metamodel.source.annotations.attribute.type.EnumeratedTypeResolver;
import org.hibernate.metamodel.source.annotations.attribute.type.AttributeTypeResolverImpl;
import org.hibernate.metamodel.source.annotations.attribute.type.LobTypeResolver; import org.hibernate.metamodel.source.annotations.attribute.type.LobTypeResolver;
import org.hibernate.metamodel.source.annotations.attribute.type.TemporalTypeResolver; import org.hibernate.metamodel.source.annotations.attribute.type.TemporalTypeResolver;
import org.hibernate.metamodel.source.annotations.entity.EntityBindingContext;
/** /**
* Represent a basic attribute (explicitly or implicitly mapped). * Represent a basic attribute (explicitly or implicitly mapped).
@ -90,13 +90,13 @@ public class BasicAttribute extends MappedAttribute {
private final String customWriteFragment; private final String customWriteFragment;
private final String customReadFragment; private final String customReadFragment;
private final String checkCondition; private final String checkCondition;
private AttributeTypeResolver resolver; private AttributeTypeResolver resolver;
public static BasicAttribute createSimpleAttribute(String name, public static BasicAttribute createSimpleAttribute(String name,
Class<?> attributeType, Class<?> attributeType,
Map<DotName, List<AnnotationInstance>> annotations, Map<DotName, List<AnnotationInstance>> annotations,
String accessType, String accessType,
AnnotationBindingContext context) { EntityBindingContext context) {
return new BasicAttribute( name, attributeType, accessType, annotations, context ); return new BasicAttribute( name, attributeType, accessType, annotations, context );
} }
@ -104,7 +104,7 @@ public class BasicAttribute extends MappedAttribute {
Class<?> attributeType, Class<?> attributeType,
String accessType, String accessType,
Map<DotName, List<AnnotationInstance>> annotations, Map<DotName, List<AnnotationInstance>> annotations,
AnnotationBindingContext context) { EntityBindingContext context) {
super( name, attributeType, accessType, annotations, context ); super( name, attributeType, accessType, annotations, context );
AnnotationInstance versionAnnotation = JandexHelper.getSingleAnnotation( annotations, JPADotNames.VERSION ); AnnotationInstance versionAnnotation = JandexHelper.getSingleAnnotation( annotations, JPADotNames.VERSION );
@ -303,25 +303,25 @@ public class BasicAttribute extends MappedAttribute {
return generator; return generator;
} }
@Override @Override
public AttributeTypeResolver getHibernateTypeResolver() { public AttributeTypeResolver getHibernateTypeResolver() {
if ( resolver == null ) { if ( resolver == null ) {
resolver = getDefaultHibernateTypeResolver(); resolver = getDefaultHibernateTypeResolver();
} }
return resolver; return resolver;
} }
private AttributeTypeResolver getDefaultHibernateTypeResolver() { private AttributeTypeResolver getDefaultHibernateTypeResolver() {
CompositeAttributeTypeResolver resolver = new CompositeAttributeTypeResolver( CompositeAttributeTypeResolver resolver = new CompositeAttributeTypeResolver(
new AttributeTypeResolverImpl( new AttributeTypeResolverImpl(
this this
) )
); );
resolver.addHibernateTypeResolver( new TemporalTypeResolver( this ) ); resolver.addHibernateTypeResolver( new TemporalTypeResolver( this ) );
resolver.addHibernateTypeResolver( new LobTypeResolver( this ) ); resolver.addHibernateTypeResolver( new LobTypeResolver( this ) );
resolver.addHibernateTypeResolver( new EnumeratedTypeResolver( this ) ); resolver.addHibernateTypeResolver( new EnumeratedTypeResolver( this ) );
return resolver; return resolver;
} }
} }

View File

@ -30,11 +30,11 @@ import org.jboss.jandex.AnnotationInstance;
import org.jboss.jandex.DotName; import org.jboss.jandex.DotName;
import org.hibernate.mapping.PropertyGeneration; import org.hibernate.mapping.PropertyGeneration;
import org.hibernate.metamodel.source.annotations.AnnotationBindingContext;
import org.hibernate.metamodel.source.annotations.HibernateDotNames; import org.hibernate.metamodel.source.annotations.HibernateDotNames;
import org.hibernate.metamodel.source.annotations.JPADotNames; import org.hibernate.metamodel.source.annotations.JPADotNames;
import org.hibernate.metamodel.source.annotations.JandexHelper; import org.hibernate.metamodel.source.annotations.JandexHelper;
import org.hibernate.metamodel.source.annotations.attribute.type.AttributeTypeResolver; import org.hibernate.metamodel.source.annotations.attribute.type.AttributeTypeResolver;
import org.hibernate.metamodel.source.annotations.entity.EntityBindingContext;
/** /**
* Base class for the different types of mapped attributes * Base class for the different types of mapped attributes
@ -82,9 +82,9 @@ public abstract class MappedAttribute implements Comparable<MappedAttribute> {
/** /**
* The binding context * The binding context
*/ */
private final AnnotationBindingContext context; private final EntityBindingContext context;
MappedAttribute(String name, Class<?> attributeType, String accessType, Map<DotName, List<AnnotationInstance>> annotations, AnnotationBindingContext context) { MappedAttribute(String name, Class<?> attributeType, String accessType, Map<DotName, List<AnnotationInstance>> annotations, EntityBindingContext context) {
this.context = context; this.context = context;
this.annotations = annotations; this.annotations = annotations;
this.name = name; this.name = name;
@ -120,7 +120,7 @@ public abstract class MappedAttribute implements Comparable<MappedAttribute> {
return accessType; return accessType;
} }
public AnnotationBindingContext getContext() { public EntityBindingContext getContext() {
return context; return context;
} }

View File

@ -137,7 +137,7 @@ public class ConfiguredClass {
*/ */
private final String customTuplizer; private final String customTuplizer;
private final LocalBindingContextImpl localBindingContext; private final EntityBindingContext localBindingContext;
public ConfiguredClass( public ConfiguredClass(
ClassInfo classInfo, ClassInfo classInfo,
@ -155,7 +155,7 @@ public class ConfiguredClass {
this.idAttributeMap = new TreeMap<String, BasicAttribute>(); this.idAttributeMap = new TreeMap<String, BasicAttribute>();
this.associationAttributeMap = new TreeMap<String, AssociationAttribute>(); this.associationAttributeMap = new TreeMap<String, AssociationAttribute>();
this.localBindingContext = new LocalBindingContextImpl( context, this ); this.localBindingContext = new EntityBindingContext( context, this );
collectAttributes(); collectAttributes();
attributeOverrideMap = Collections.unmodifiableMap( findAttributeOverrides() ); attributeOverrideMap = Collections.unmodifiableMap( findAttributeOverrides() );
@ -177,7 +177,7 @@ public class ConfiguredClass {
return parent; return parent;
} }
public LocalBindingContextImpl getLocalBindingContext() { public EntityBindingContext getLocalBindingContext() {
return localBindingContext; return localBindingContext;
} }

View File

@ -44,11 +44,11 @@ import org.hibernate.service.ServiceRegistry;
* *
* @author Steve Ebersole * @author Steve Ebersole
*/ */
public class LocalBindingContextImpl implements LocalBindingContext, AnnotationBindingContext { public class EntityBindingContext implements LocalBindingContext, AnnotationBindingContext {
private final AnnotationBindingContext contextDelegate; private final AnnotationBindingContext contextDelegate;
private final Origin origin; private final Origin origin;
public LocalBindingContextImpl(AnnotationBindingContext contextDelegate, ConfiguredClass source) { public EntityBindingContext(AnnotationBindingContext contextDelegate, ConfiguredClass source) {
this.contextDelegate = contextDelegate; this.contextDelegate = contextDelegate;
this.origin = new Origin( SourceType.ANNOTATION, source.getName() ); this.origin = new Origin( SourceType.ANNOTATION, source.getName() );
} }

View File

@ -0,0 +1,72 @@
package org.hibernate.metamodel.source.annotations.entity;
import javax.persistence.Embeddable;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.MapsId;
import javax.persistence.OneToMany;
import org.junit.Test;
import org.hibernate.metamodel.MetadataSources;
import org.hibernate.metamodel.source.MappingException;
import org.hibernate.service.ServiceRegistryBuilder;
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertTrue;
import static junit.framework.Assert.fail;
/**
* @author Hardy Ferentschik
*/
public class MapsIdTest extends BaseAnnotationBindingTestCase {
@Entity
public class Employee {
@Id
long empId;
String name;
}
@Embeddable
public class DependentId {
String name;
long empid; // corresponds to PK type of Employee
}
@Entity
public class Dependent {
@Id
// should be @EmbeddedId, but embedded id are not working atm
DependentId id;
@MapsId("empid")
@OneToMany
Employee emp; // maps the empid attribute of embedded id @ManyToOne Employee emp;
}
@Test
@Resources(annotatedClasses = DependentId.class)
public void testMapsIsOnOneToManyThrowsException() {
try {
sources = new MetadataSources( new ServiceRegistryBuilder().buildServiceRegistry() );
sources.addAnnotatedClass( DependentId.class );
sources.addAnnotatedClass( Dependent.class );
sources.addAnnotatedClass( Employee.class );
sources.buildMetadata();
fail();
}
catch ( MappingException e ) {
assertTrue(
e.getMessage()
.startsWith( "@MapsId can only be specified on a many-to-one or one-to-one associations" )
);
assertEquals(
"Wrong error origin",
"org.hibernate.metamodel.source.annotations.entity.MapsIdTest$Dependent",
e.getOrigin().getName()
);
}
}
}