From fb9bdb7f387a4c8e100e2a8109e3c9b8706f4fed Mon Sep 17 00:00:00 2001 From: Emmanuel Bernard Date: Thu, 29 Oct 2009 18:57:24 +0000 Subject: [PATCH] HHH-4533 Populate the JPA 2 metamodel with the new mapping.MappedSuperclass metadata git-svn-id: https://svn.jboss.org/repos/hibernate/core/trunk@17879 1b8cb986-b30d-0410-93ca-fae66ebed9b2 --- .../org/hibernate/cfg/AnnotationBinder.java | 42 ++++++ .../java/org/hibernate/cfg/BinderHelper.java | 21 +++ .../hibernate/cfg/ClassPropertyHolder.java | 2 +- .../hibernate/mapping/MappedSuperclass.java | 100 +++++++++++- .../hibernate/mapping/PersistentClass.java | 11 ++ .../java/org/hibernate/mapping/RootClass.java | 23 ++- .../java/org/hibernate/mapping/Subclass.java | 9 ++ .../metamodel/AbstractIdentifiableType.java | 15 +- .../ejb/metamodel/AttributeFactory.java | 40 ++--- .../metamodel/MappedSuperclassTypeImpl.java | 21 +++ .../ejb/metamodel/MetadataContext.java | 142 ++++++++++++++---- .../ejb/metamodel/MetamodelImpl.java | 56 ++++++- .../hibernate/ejb/test/metadata/Animal.java | 31 ++++ .../org/hibernate/ejb/test/metadata/Cat.java | 19 +++ .../hibernate/ejb/test/metadata/Cattish.java | 19 +++ .../org/hibernate/ejb/test/metadata/Dog.java | 19 +++ .../hibernate/ejb/test/metadata/Feline.java | 19 +++ .../ejb/test/metadata/MetadataTest.java | 91 ++++++++++- .../hibernate/ejb/test/metadata/SubThing.java | 17 +++ .../hibernate/ejb/test/metadata/Thing.java | 30 ++++ 20 files changed, 658 insertions(+), 69 deletions(-) create mode 100644 entitymanager/src/main/java/org/hibernate/ejb/metamodel/MappedSuperclassTypeImpl.java create mode 100644 entitymanager/src/test/java/org/hibernate/ejb/test/metadata/Animal.java create mode 100644 entitymanager/src/test/java/org/hibernate/ejb/test/metadata/Cat.java create mode 100644 entitymanager/src/test/java/org/hibernate/ejb/test/metadata/Cattish.java create mode 100644 entitymanager/src/test/java/org/hibernate/ejb/test/metadata/Dog.java create mode 100644 entitymanager/src/test/java/org/hibernate/ejb/test/metadata/Feline.java create mode 100644 entitymanager/src/test/java/org/hibernate/ejb/test/metadata/SubThing.java create mode 100644 entitymanager/src/test/java/org/hibernate/ejb/test/metadata/Thing.java diff --git a/annotations/src/main/java/org/hibernate/cfg/AnnotationBinder.java b/annotations/src/main/java/org/hibernate/cfg/AnnotationBinder.java index 5a0b6dd56e..6834f59bc5 100644 --- a/annotations/src/main/java/org/hibernate/cfg/AnnotationBinder.java +++ b/annotations/src/main/java/org/hibernate/cfg/AnnotationBinder.java @@ -775,6 +775,21 @@ public final class AnnotationBinder { ); entityBinder.setIgnoreIdAnnotations( ignoreIdAnnotations ); persistentClass.setIdentifierMapper( mapper ); + + //If id definition is on a mapped superclass, update the mapping + final org.hibernate.mapping.MappedSuperclass superclass = BinderHelper.getMappedSuperclassOrNull( + inferredData.getDeclaringClass(), + inheritanceStatePerClass, + mappings + ); + if (superclass != null) { + superclass.setDeclaredIdentifierMapper(mapper); + } + else { + //we are for sure on the entity + persistentClass.setDeclaredIdentifierMapper( mapper ); + } + Property property = new Property(); property.setName( "_identifierMapper" ); property.setNodeName( "id" ); @@ -1462,6 +1477,20 @@ public final class AnnotationBinder { Property prop = propBinder.bind(); propBinder.getSimpleValueBinder().setVersion(true); rootClass.setVersion( prop ); + + //If version is on a mapped superclass, update the mapping + final org.hibernate.mapping.MappedSuperclass superclass = BinderHelper.getMappedSuperclassOrNull( + inferredData.getDeclaringClass(), + inheritanceStatePerClass, + mappings + ); + if (superclass != null) { + superclass.setDeclaredVersion(prop); + } + else { + //we know the property is on the actual entity + rootClass.setDeclaredVersion( prop ); + } SimpleValue simpleValue = (SimpleValue) prop.getValue(); simpleValue.setNullValue( "undefined" ); @@ -2202,6 +2231,19 @@ public final class AnnotationBinder { binder.setProperty( inferredData.getProperty() ); Property prop = binder.make(); rootClass.setIdentifierProperty( prop ); + //if the id property is on a superclass, update the metamodel + final org.hibernate.mapping.MappedSuperclass superclass = BinderHelper.getMappedSuperclassOrNull( + inferredData.getDeclaringClass(), + inheritanceStatePerClass, + mappings + ); + if (superclass != null) { + superclass.setDeclaredIdentifierProperty(prop); + } + else { + //we know the property is on the actual entity + rootClass.setDeclaredIdentifierProperty( prop ); + } } } diff --git a/annotations/src/main/java/org/hibernate/cfg/BinderHelper.java b/annotations/src/main/java/org/hibernate/cfg/BinderHelper.java index dd3e292d7b..db86950406 100644 --- a/annotations/src/main/java/org/hibernate/cfg/BinderHelper.java +++ b/annotations/src/main/java/org/hibernate/cfg/BinderHelper.java @@ -60,6 +60,7 @@ import org.hibernate.mapping.SimpleValue; import org.hibernate.mapping.Table; import org.hibernate.mapping.ToOne; import org.hibernate.mapping.Value; +import org.hibernate.mapping.MappedSuperclass; import org.hibernate.type.TypeFactory; import org.hibernate.util.StringHelper; import org.slf4j.Logger; @@ -575,4 +576,24 @@ public class BinderHelper { log.info( "Binding Any Meta definition: {}", defAnn.name() ); mappings.addAnyMetaDef( defAnn ); } + + public static MappedSuperclass getMappedSuperclassOrNull(XClass declaringClass, + Map inheritanceStatePerClass, + ExtendedMappings mappings) { + boolean retrieve = false; + if ( declaringClass != null ) { + final InheritanceState inheritanceState = inheritanceStatePerClass.get( declaringClass ); + if ( inheritanceState == null ) { + throw new org.hibernate.annotations.common.AssertionFailure( + "Declaring class is not found in the inheritance state hierarchy: " + declaringClass + ); + } + if ( inheritanceState.isEmbeddableSuperclass ) { + retrieve = true; + } + } + return retrieve ? + mappings.getMappedSuperclass( mappings.getReflectionManager().toClass( declaringClass ) ) : + null; + } } diff --git a/annotations/src/main/java/org/hibernate/cfg/ClassPropertyHolder.java b/annotations/src/main/java/org/hibernate/cfg/ClassPropertyHolder.java index d0aa8b3d7c..2e89fc0e78 100644 --- a/annotations/src/main/java/org/hibernate/cfg/ClassPropertyHolder.java +++ b/annotations/src/main/java/org/hibernate/cfg/ClassPropertyHolder.java @@ -130,7 +130,7 @@ public class ClassPropertyHolder extends AbstractPropertyHolder { final ExtendedMappings mappings = getMappings(); final Class type = mappings.getReflectionManager().toClass( declaringClass ); MappedSuperclass superclass = mappings.getMappedSuperclass( type ); - superclass.addProperty( prop ); + superclass.addDeclaredProperty( prop ); } private void addPropertyToJoin(Property prop, XClass declaringClass, Join join) { diff --git a/core/src/main/java/org/hibernate/mapping/MappedSuperclass.java b/core/src/main/java/org/hibernate/mapping/MappedSuperclass.java index 816f992da9..32e5dad0af 100644 --- a/core/src/main/java/org/hibernate/mapping/MappedSuperclass.java +++ b/core/src/main/java/org/hibernate/mapping/MappedSuperclass.java @@ -12,20 +12,27 @@ import java.util.List; * * Do not use outside this use case. * - * A proper redesign will be evluated in Hibernate 4 + * + * A proper redesign will be evaluated in Hibernate 4 + * + * Implementation details: + * properties are copies of their closest sub-persistentClass versions * * @author Emmanuel Bernard */ public class MappedSuperclass { private final MappedSuperclass superMappedSuperclass; private final PersistentClass superPersistentClass; - private final List properties; + private final List declaredProperties; private Class mappedClass; + private Property identifierProperty; + private Property version; + private Component identifierMapper; public MappedSuperclass(MappedSuperclass superMappedSuperclass, PersistentClass superPersistentClass) { this.superMappedSuperclass = superMappedSuperclass; this.superPersistentClass = superPersistentClass; - this.properties = new ArrayList(); + this.declaredProperties = new ArrayList(); } /** @@ -39,6 +46,14 @@ public class MappedSuperclass { return superMappedSuperclass; } + public boolean hasIdentifierProperty() { + return getIdentifierProperty() != null; + } + + public boolean isVersioned() { + return getVersion() != null; + } + /** * Returns the PersistentClass of the first superclass marked as @Entity * or null if none exists @@ -49,21 +64,21 @@ public class MappedSuperclass { return superPersistentClass; } - public Iterator getPropertyIterator() { - return properties.iterator(); + public Iterator getDeclaredPropertyIterator() { + return declaredProperties.iterator(); } - public void addProperty(Property p) { + public void addDeclaredProperty(Property p) { //Do not add duplicate properties //TODO is it efficient enough? String name = p.getName(); - Iterator it = properties.iterator(); + Iterator it = declaredProperties.iterator(); while (it.hasNext()) { if ( name.equals( ((Property)it.next()).getName() ) ) { return; } } - properties.add(p); + declaredProperties.add(p); } public Class getMappedClass() { @@ -73,4 +88,73 @@ public class MappedSuperclass { public void setMappedClass(Class mappedClass) { this.mappedClass = mappedClass; } + + public Property getIdentifierProperty() { + //get direct identifiermapper or the one from the super mappedSuperclass + // or the one from the super persistentClass + Property propagatedIdentifierProp = identifierProperty; + if ( propagatedIdentifierProp == null ) { + if ( superMappedSuperclass != null ) { + propagatedIdentifierProp = superMappedSuperclass.getIdentifierProperty(); + } + if (propagatedIdentifierProp == null && superPersistentClass != null){ + propagatedIdentifierProp = superPersistentClass.getIdentifierProperty(); + } + } + return propagatedIdentifierProp; + } + + public Property getDeclaredIdentifierProperty() { + return identifierProperty; + } + + public void setDeclaredIdentifierProperty(Property prop) { + this.identifierProperty = prop; + } + + public Property getVersion() { + //get direct version or the one from the super mappedSuperclass + // or the one from the super persistentClass + Property propagatedVersion = version; + if (propagatedVersion == null) { + if ( superMappedSuperclass != null ) { + propagatedVersion = superMappedSuperclass.getVersion(); + } + if (propagatedVersion == null && superPersistentClass != null){ + propagatedVersion = superPersistentClass.getVersion(); + } + } + return propagatedVersion; + } + + public Property getDeclaredVersion() { + return version; + } + + public void setDeclaredVersion(Property prop) { + this.version = prop; + } + + public Component getIdentifierMapper() { + //get direct identifiermapper or the one from the super mappedSuperclass + // or the one from the super persistentClass + Component propagatedMapper = identifierMapper; + if ( propagatedMapper == null ) { + if ( superMappedSuperclass != null ) { + propagatedMapper = superMappedSuperclass.getIdentifierMapper(); + } + if (propagatedMapper == null && superPersistentClass != null){ + propagatedMapper = superPersistentClass.getIdentifierMapper(); + } + } + return propagatedMapper; + } + + public Component getDeclaredIdentifierMapper() { + return identifierMapper; + } + + public void setDeclaredIdentifierMapper(Component identifierMapper) { + this.identifierMapper = identifierMapper; + } } diff --git a/core/src/main/java/org/hibernate/mapping/PersistentClass.java b/core/src/main/java/org/hibernate/mapping/PersistentClass.java index 9a336f5dd1..a1cedb994a 100644 --- a/core/src/main/java/org/hibernate/mapping/PersistentClass.java +++ b/core/src/main/java/org/hibernate/mapping/PersistentClass.java @@ -98,6 +98,7 @@ public abstract class PersistentClass implements Serializable, Filterable, MetaA protected int optimisticLockMode; private MappedSuperclass superMappedSuperclass; + private Component declaredIdentifierMapper; public String getClassName() { return className; @@ -236,8 +237,10 @@ public abstract class PersistentClass implements Serializable, Filterable, MetaA public abstract boolean isMutable(); public abstract boolean hasIdentifierProperty(); public abstract Property getIdentifierProperty(); + public abstract Property getDeclaredIdentifierProperty(); public abstract KeyValue getIdentifier(); public abstract Property getVersion(); + public abstract Property getDeclaredVersion(); public abstract Value getDiscriminator(); public abstract boolean isInherited(); public abstract boolean isPolymorphic(); @@ -776,6 +779,14 @@ public abstract class PersistentClass implements Serializable, Filterable, MetaA return identifierMapper; } + public Component getDeclaredIdentifierMapper() { + return declaredIdentifierMapper; + } + + public void setDeclaredIdentifierMapper(Component declaredIdentifierMapper) { + this.declaredIdentifierMapper = declaredIdentifierMapper; + } + public boolean hasIdentifierMapper() { return identifierMapper != null; } diff --git a/core/src/main/java/org/hibernate/mapping/RootClass.java b/core/src/main/java/org/hibernate/mapping/RootClass.java index 9cf2d4cbf0..718e54138c 100644 --- a/core/src/main/java/org/hibernate/mapping/RootClass.java +++ b/core/src/main/java/org/hibernate/mapping/RootClass.java @@ -61,7 +61,9 @@ public class RootClass extends PersistentClass implements TableOwner { private Table table; private boolean discriminatorInsertable = true; private int nextSubclassId = 0; - + private Property declaredIdentifierProperty; + private Property declaredVersion; + int nextSubclassId() { return ++nextSubclassId; } @@ -80,6 +82,15 @@ public class RootClass extends PersistentClass implements TableOwner { public Property getIdentifierProperty() { return identifierProperty; } + + public Property getDeclaredIdentifierProperty() { + return declaredIdentifierProperty; + } + + public void setDeclaredIdentifierProperty(Property declaredIdentifierProperty) { + this.declaredIdentifierProperty = declaredIdentifierProperty; + } + public KeyValue getIdentifier() { return identifier; } @@ -128,6 +139,15 @@ public class RootClass extends PersistentClass implements TableOwner { public Property getVersion() { return version; } + + public Property getDeclaredVersion() { + return declaredVersion; + } + + public void setDeclaredVersion(Property declaredVersion) { + this.declaredVersion = declaredVersion; + } + public void setVersion(Property version) { this.version = version; } @@ -181,6 +201,7 @@ public class RootClass extends PersistentClass implements TableOwner { public void setIdentifierProperty(Property identifierProperty) { this.identifierProperty = identifierProperty; identifierProperty.setPersistentClass(this); + } public void setMutable(boolean mutable) { diff --git a/core/src/main/java/org/hibernate/mapping/Subclass.java b/core/src/main/java/org/hibernate/mapping/Subclass.java index 233dac677a..3587e22e91 100644 --- a/core/src/main/java/org/hibernate/mapping/Subclass.java +++ b/core/src/main/java/org/hibernate/mapping/Subclass.java @@ -70,6 +70,11 @@ public class Subclass extends PersistentClass { public Property getIdentifierProperty() { return getSuperclass().getIdentifierProperty(); } + + public Property getDeclaredIdentifierProperty() { + return null; + } + public KeyValue getIdentifier() { return getSuperclass().getIdentifier(); } @@ -143,6 +148,10 @@ public class Subclass extends PersistentClass { return getSuperclass().getVersion(); } + public Property getDeclaredVersion() { + return null; + } + public boolean hasEmbeddedIdentifier() { return getSuperclass().hasEmbeddedIdentifier(); } diff --git a/entitymanager/src/main/java/org/hibernate/ejb/metamodel/AbstractIdentifiableType.java b/entitymanager/src/main/java/org/hibernate/ejb/metamodel/AbstractIdentifiableType.java index 4ad1124b2e..5767eb9c81 100644 --- a/entitymanager/src/main/java/org/hibernate/ejb/metamodel/AbstractIdentifiableType.java +++ b/entitymanager/src/main/java/org/hibernate/ejb/metamodel/AbstractIdentifiableType.java @@ -97,7 +97,19 @@ public abstract class AbstractIdentifiableType } } else { - id_ = requireSupertype().getId( javaType ); + //yuk yuk bad me + if (this instanceof MappedSuperclassTypeImpl) { + final AbstractIdentifiableType supertype = getSupertype(); + if (supertype != null) { + id_ = supertype.getId( javaType ); + } + else { + id_ = null; + } + } + else { + id_ = requireSupertype().getId( javaType ); + } } return id_; } @@ -227,6 +239,7 @@ public abstract class AbstractIdentifiableType return new Builder() { public void applyIdAttribute(SingularAttributeImpl idAttribute) { AbstractIdentifiableType.this.id = idAttribute; + managedBuilder.addAttribute( idAttribute ); } public void applyIdClassAttributes(Set> idClassAttributes) { diff --git a/entitymanager/src/main/java/org/hibernate/ejb/metamodel/AttributeFactory.java b/entitymanager/src/main/java/org/hibernate/ejb/metamodel/AttributeFactory.java index 207d55ffd7..87d67cb114 100644 --- a/entitymanager/src/main/java/org/hibernate/ejb/metamodel/AttributeFactory.java +++ b/entitymanager/src/main/java/org/hibernate/ejb/metamodel/AttributeFactory.java @@ -54,19 +54,19 @@ public class AttributeFactory { } @SuppressWarnings({ "unchecked" }) - public AttributeImplementor buildAttribute(AbstractManagedType ownerType, Property property) { + public AttributeImplementor buildAttribute(AbstractManagedType ownerType, Property property, boolean getMember) { AttributeContext attrContext = getAttributeContext( property ); final AttributeImplementor attribute; if ( attrContext.isCollection() ) { - attribute = buildPluralAttribute( ownerType, property, attrContext ); + attribute = buildPluralAttribute( ownerType, property, attrContext, getMember ); } else { - final Type attrType = getType( ownerType, attrContext.getElementTypeStatus(), attrContext.getElementValue() ); + final Type attrType = getType( ownerType, attrContext.getElementTypeStatus(), attrContext.getElementValue(), getMember ); attribute = new SingularAttributeImpl( property.getName(), property.getType().getReturnedClass(), ownerType, - determineStandardJavaMember( ownerType, property ), + getMember ? determineStandardJavaMember( ownerType, property ) : null, false, false, property.isOptional(), @@ -78,21 +78,21 @@ public class AttributeFactory { } @SuppressWarnings( "unchecked" ) - private AttributeImplementor buildPluralAttribute(AbstractManagedType ownerType, Property property, AttributeContext attrContext) { + private AttributeImplementor buildPluralAttribute(AbstractManagedType ownerType, Property property, AttributeContext attrContext, boolean getMember) { AttributeImplementor attribute; - final Type attrType = getType( ownerType, attrContext.getElementTypeStatus(), attrContext.getElementValue() ); + final Type attrType = getType( ownerType, attrContext.getElementTypeStatus(), attrContext.getElementValue(), getMember ); final Class collectionClass = (Class) attrContext.getCollectionClass(); if ( java.util.Map.class.isAssignableFrom( collectionClass ) ) { - final Type keyType = getType( ownerType, attrContext.getKeyTypeStatus(), attrContext.getKeyValue() ); + final Type keyType = getType( ownerType, attrContext.getKeyTypeStatus(), attrContext.getKeyValue(), getMember ); attribute = PluralAttributeImpl.create( ownerType, attrType, collectionClass, keyType ) - .member( determineStandardJavaMember( ownerType, property ) ) + .member( getMember ? determineStandardJavaMember( ownerType, property ) : null ) .property( property ) .persistentAttributeType( attrContext.getElementAttributeType() ) .build(); } else { attribute = PluralAttributeImpl.create( ownerType, attrType, collectionClass, null ) - .member( determineStandardJavaMember( ownerType, property ) ) + .member( getMember ? determineStandardJavaMember( ownerType, property ) : null ) .property( property ) .persistentAttributeType( attrContext.getElementAttributeType() ) .build(); @@ -100,13 +100,13 @@ public class AttributeFactory { return attribute; } - private Type getType(AbstractManagedType owner, AttributeContext.TypeStatus elementTypeStatus, Value value) { + private Type getType(AbstractManagedType owner, AttributeContext.TypeStatus elementTypeStatus, Value value, boolean getMember) { final org.hibernate.type.Type type = value.getType(); switch ( elementTypeStatus ) { case BASIC: return buildBasicType( type ); case EMBEDDABLE: - return buildEmbeddableType( owner, value, type ); + return buildEmbeddableType( owner, value, type, getMember ); case ENTITY: return buildEntityType( type ); default: @@ -128,7 +128,7 @@ public class AttributeFactory { } @SuppressWarnings( "unchecked" ) - private Type buildEmbeddableType(AbstractManagedType owner, Value value, org.hibernate.type.Type type) { + private Type buildEmbeddableType(AbstractManagedType owner, Value value, org.hibernate.type.Type type, boolean getMember) { //build embedable type final Class clazz = type.getReturnedClass(); final EmbeddableTypeImpl embeddableType = new EmbeddableTypeImpl( clazz, owner, (ComponentType) type ); @@ -137,22 +137,22 @@ public class AttributeFactory { final Iterator subProperties = component.getPropertyIterator(); while ( subProperties.hasNext() ) { final Property property = subProperties.next(); - embeddableType.getBuilder().addAttribute( buildAttribute( embeddableType, property ) ); + embeddableType.getBuilder().addAttribute( buildAttribute( embeddableType, property, getMember ) ); } embeddableType.lock(); return embeddableType; } @SuppressWarnings({ "unchecked" }) - public SingularAttributeImpl buildIdAttribute(AbstractManagedType ownerType, Property property) { + public SingularAttributeImpl buildIdAttribute(AbstractManagedType ownerType, Property property, boolean getMember) { final AttributeContext attrContext = getAttributeContext( property ); - final Type attrType = getType( ownerType, attrContext.getElementTypeStatus(), attrContext.getElementValue() ); + final Type attrType = getType( ownerType, attrContext.getElementTypeStatus(), attrContext.getElementValue(), getMember ); final Class idJavaType = property.getType().getReturnedClass(); return new SingularAttributeImpl.Identifier( property.getName(), idJavaType, ownerType, - determineIdentifierJavaMember( ownerType, property ), + getMember ? determineIdentifierJavaMember( ownerType, property ) : null, attrType, attrContext.getElementAttributeType() ); @@ -232,15 +232,15 @@ public class AttributeFactory { } @SuppressWarnings({ "unchecked" }) - public SingularAttributeImpl buildVerisonAttribute(AbstractManagedType ownerType, Property property) { + public SingularAttributeImpl buildVersionAttribute(AbstractManagedType ownerType, Property property, boolean getMember) { final AttributeContext attrContext = getAttributeContext( property ); final Class javaType = property.getType().getReturnedClass(); - final Type attrType = getType( ownerType, attrContext.getElementTypeStatus(), attrContext.getElementValue() ); + final Type attrType = getType( ownerType, attrContext.getElementTypeStatus(), attrContext.getElementValue(), getMember ); return new SingularAttributeImpl.Version( property.getName(), javaType, ownerType, - determineVersionJavaMember( ownerType, property ), + getMember ? determineVersionJavaMember( ownerType, property ) : null, attrType, attrContext.getElementAttributeType() ); @@ -252,7 +252,7 @@ public class AttributeFactory { // this should never happen, but to be safe... throw new IllegalArgumentException( "Given property did not match declared version property" ); } - return entityMetamodel.getTuplizer( EntityMode.POJO ).getIdentifierGetter().getMember(); + return entityMetamodel.getTuplizer( EntityMode.POJO ).getVersionGetter().getMember(); } private static class AttributeContext { diff --git a/entitymanager/src/main/java/org/hibernate/ejb/metamodel/MappedSuperclassTypeImpl.java b/entitymanager/src/main/java/org/hibernate/ejb/metamodel/MappedSuperclassTypeImpl.java new file mode 100644 index 0000000000..7bfce7751a --- /dev/null +++ b/entitymanager/src/main/java/org/hibernate/ejb/metamodel/MappedSuperclassTypeImpl.java @@ -0,0 +1,21 @@ +package org.hibernate.ejb.metamodel; + +import javax.persistence.metamodel.MappedSuperclassType; +import javax.persistence.metamodel.Type; + +/** + * @author Emmanuel Bernard + */ +public class MappedSuperclassTypeImpl extends AbstractIdentifiableType implements MappedSuperclassType { + public MappedSuperclassTypeImpl( + Class javaType, + AbstractIdentifiableType superType, + boolean hasIdentifierProperty, + boolean versioned) { + super( javaType, superType, hasIdentifierProperty, versioned ); + } + + public PersistenceType getPersistenceType() { + return PersistenceType.MAPPED_SUPERCLASS; + } +} diff --git a/entitymanager/src/main/java/org/hibernate/ejb/metamodel/MetadataContext.java b/entitymanager/src/main/java/org/hibernate/ejb/metamodel/MetadataContext.java index 1f7d797a4b..c7acb73e1d 100644 --- a/entitymanager/src/main/java/org/hibernate/ejb/metamodel/MetadataContext.java +++ b/entitymanager/src/main/java/org/hibernate/ejb/metamodel/MetadataContext.java @@ -25,16 +25,18 @@ import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; -import java.util.LinkedHashMap; import java.util.Map; import java.util.Set; +import java.util.List; +import java.util.ArrayList; import javax.persistence.metamodel.Attribute; import javax.persistence.metamodel.SingularAttribute; -import javax.persistence.MappedSuperclass; +import org.hibernate.mapping.MappedSuperclass; import org.hibernate.mapping.PersistentClass; import org.hibernate.mapping.Property; import org.hibernate.engine.SessionFactoryImplementor; +import org.hibernate.annotations.common.AssertionFailure; /** * Defines a context for storing information during the building of the {@link MetamodelImpl}. @@ -53,14 +55,18 @@ class MetadataContext { private final SessionFactoryImplementor sessionFactory; private final AttributeFactory attributeFactory = new AttributeFactory( this ); - private HashMap,EntityTypeImpl> entityTypes + private Map,EntityTypeImpl> entityTypes = new HashMap, EntityTypeImpl>(); - private HashMap> entityTypesByEntityName + private Map> entityTypesByEntityName = new HashMap>(); - private LinkedHashMap> entityTypesByPersistentClass - = new LinkedHashMap>(); - private HashMap, EmbeddableTypeImpl> embeddables + private Map> entityTypesByPersistentClass + = new HashMap>(); + private Map, EmbeddableTypeImpl> embeddables = new HashMap, EmbeddableTypeImpl>(); + private Map> mappedSuperclassByMappedSuperclassMapping + = new HashMap>(); + //this list contains MappedSuperclass and EntityTypes ordered by superclass first + private List orderedMappings = new ArrayList(); public MetadataContext(SessionFactoryImplementor sessionFactory) { this.sessionFactory = sessionFactory; @@ -87,12 +93,19 @@ class MetadataContext { entityTypes.put( entityType.getBindableJavaType(), entityType ); entityTypesByEntityName.put( persistentClass.getEntityName(), entityType ); entityTypesByPersistentClass.put( persistentClass, entityType ); + orderedMappings.add( persistentClass ); } /*package*/ void registerEmbeddedableType(EmbeddableTypeImpl embeddableType) { embeddables.put( embeddableType.getJavaType(), embeddableType ); } + /*package*/ void registerMappedSuperclassType(MappedSuperclass mappedSuperclass, + MappedSuperclassTypeImpl mappedSuperclassType) { + mappedSuperclassByMappedSuperclassMapping.put( mappedSuperclass, mappedSuperclassType ); + orderedMappings.add( mappedSuperclass ); + } + /** * Given a Hibernate {@link PersistentClass}, locate the corresponding JPA {@link org.hibernate.type.EntityType} * implementation. May retur null if the given {@link PersistentClass} has not yet been processed. @@ -128,50 +141,116 @@ class MetadataContext { @SuppressWarnings({ "unchecked" }) public void wrapUp() { - // IMPL NOTE : entityTypesByPersistentClass is a insertion-ordered map, where the insertion order - // ensures that a type's super type is already processed... - for ( Map.Entry> entry : entityTypesByPersistentClass.entrySet() ) { - applyIdMetadata( entry.getKey(), entry.getValue() ); - applyVersionAttribute( entry.getKey(), entry.getValue() ); - Iterator properties = ( Iterator ) entry.getKey().getPropertyIterator(); - while ( properties.hasNext() ) { - final Property property = properties.next(); - final Attribute attribute = attributeFactory.buildAttribute( entry.getValue(), property ); - entry.getValue().getBuilder().addAttribute( attribute ); + //we need to process types from superclasses to subclasses + for (Object mapping : orderedMappings) { + if ( PersistentClass.class.isAssignableFrom( mapping.getClass() ) ) { + @SuppressWarnings( "unchecked" ) + final PersistentClass safeMapping = (PersistentClass) mapping; + final EntityTypeImpl jpa2Mapping = entityTypesByPersistentClass.get( safeMapping ); + applyIdMetadata( safeMapping, jpa2Mapping ); + applyVersionAttribute( safeMapping, jpa2Mapping ); + Iterator properties = ( Iterator ) safeMapping.getDeclaredPropertyIterator(); + while ( properties.hasNext() ) { + final Property property = properties.next(); + final Attribute attribute = attributeFactory.buildAttribute( jpa2Mapping, property, true ); + jpa2Mapping.getBuilder().addAttribute( attribute ); + } + jpa2Mapping.lock(); + populateStaticMetamodel( jpa2Mapping ); + } + else if ( MappedSuperclass.class.isAssignableFrom( mapping.getClass() ) ) { + @SuppressWarnings( "unchecked" ) + final MappedSuperclass safeMapping = (MappedSuperclass) mapping; + final MappedSuperclassTypeImpl jpa2Mapping = mappedSuperclassByMappedSuperclassMapping.get( + safeMapping + ); + applyIdMetadata( safeMapping, jpa2Mapping ); + applyVersionAttribute( safeMapping, jpa2Mapping ); + Iterator properties = ( Iterator ) safeMapping.getDeclaredPropertyIterator(); + while ( properties.hasNext() ) { + final Property property = properties.next(); + final Attribute attribute = attributeFactory.buildAttribute( jpa2Mapping, property, false ); + jpa2Mapping.getBuilder().addAttribute( attribute ); + } + jpa2Mapping.lock(); + populateStaticMetamodel( jpa2Mapping ); + } + else { + throw new AssertionFailure( "Unexpected mapping type: " + mapping.getClass() ); } - entry.getValue().lock(); - populateStaticMetamodel( entry.getValue() ); } } private void applyIdMetadata(PersistentClass persistentClass, EntityTypeImpl jpaEntityType) { if ( persistentClass.hasIdentifierProperty() ) { - jpaEntityType.getBuilder().applyIdAttribute( - attributeFactory.buildIdAttribute( jpaEntityType, persistentClass.getIdentifierProperty() ) - ); + final Property declaredIdentifierProperty = persistentClass.getDeclaredIdentifierProperty(); + if (declaredIdentifierProperty != null) { + jpaEntityType.getBuilder().applyIdAttribute( + attributeFactory.buildIdAttribute( jpaEntityType, declaredIdentifierProperty, true ) + ); + } } else { jpaEntityType.getBuilder().applyIdClassAttributes( buildIdClassAttributes( jpaEntityType, persistentClass ) ); } } - private void applyVersionAttribute(PersistentClass persistentClass, EntityTypeImpl jpaEntityType) { - if ( ! persistentClass.isVersioned() ) { - return; + private void applyIdMetadata(MappedSuperclass mappingType, MappedSuperclassTypeImpl jpaMappingType) { + if ( mappingType.hasIdentifierProperty() ) { + final Property declaredIdentifierProperty = mappingType.getDeclaredIdentifierProperty(); + if (declaredIdentifierProperty != null) { + jpaMappingType.getBuilder().applyIdAttribute( + attributeFactory.buildIdAttribute( jpaMappingType, declaredIdentifierProperty, false ) + ); + } + } + //an MappedSuperclass can have no identifier if the id is set below in the hierarchy + else if ( mappingType.getIdentifierMapper() != null ){ + final Set> attributes = buildIdClassAttributes( + jpaMappingType, mappingType + ); + jpaMappingType.getBuilder().applyIdClassAttributes( attributes ); + } + } + + private void applyVersionAttribute(PersistentClass persistentClass, EntityTypeImpl jpaEntityType) { + final Property declaredVersion = persistentClass.getDeclaredVersion(); + if (declaredVersion != null) { + jpaEntityType.getBuilder().applyVersionAttribute( + attributeFactory.buildVersionAttribute( jpaEntityType, declaredVersion, true ) + ); + } + } + + private void applyVersionAttribute(MappedSuperclass mappingType, MappedSuperclassTypeImpl jpaMappingType) { + final Property declaredVersion = mappingType.getDeclaredVersion(); + if ( declaredVersion != null ) { + jpaMappingType.getBuilder().applyVersionAttribute( + attributeFactory.buildVersionAttribute( jpaMappingType, declaredVersion, false ) + ); } - jpaEntityType.getBuilder().applyVersionAttribute( - attributeFactory.buildVerisonAttribute( jpaEntityType, persistentClass.getVersion() ) - ); } - @SuppressWarnings( "unchecked") private Set> buildIdClassAttributes( EntityTypeImpl jpaEntityType, PersistentClass persistentClass) { Set> attributes = new HashSet>(); + @SuppressWarnings( "unchecked") Iterator properties = persistentClass.getIdentifierMapper().getPropertyIterator(); while ( properties.hasNext() ) { - attributes.add( attributeFactory.buildIdAttribute( jpaEntityType, properties.next() ) ); + attributes.add( attributeFactory.buildIdAttribute( jpaEntityType, properties.next(), true ) ); + } + return attributes; + } + + private Set> buildIdClassAttributes( + MappedSuperclassTypeImpl jpaMappingType, + MappedSuperclass mappingType) { + Set> attributes = new HashSet>(); + @SuppressWarnings( "unchecked" ) + Iterator properties = mappingType.getIdentifierMapper().getPropertyIterator(); + while ( properties.hasNext() ) { + attributes.add( attributeFactory.buildIdAttribute( jpaMappingType, properties.next(), false ) ); } return attributes; } @@ -206,4 +285,7 @@ class MetadataContext { // push the attributes on to the metamodel class... } + public MappedSuperclassTypeImpl locateMappedSuperclassType(MappedSuperclass mappedSuperclass) { + return mappedSuperclassByMappedSuperclassMapping.get(mappedSuperclass); + } } diff --git a/entitymanager/src/main/java/org/hibernate/ejb/metamodel/MetamodelImpl.java b/entitymanager/src/main/java/org/hibernate/ejb/metamodel/MetamodelImpl.java index 68b7cd8280..30c71c4d9d 100644 --- a/entitymanager/src/main/java/org/hibernate/ejb/metamodel/MetamodelImpl.java +++ b/entitymanager/src/main/java/org/hibernate/ejb/metamodel/MetamodelImpl.java @@ -32,6 +32,7 @@ import javax.persistence.metamodel.ManagedType; import javax.persistence.metamodel.EmbeddableType; import org.hibernate.mapping.PersistentClass; +import org.hibernate.mapping.MappedSuperclass; import org.hibernate.engine.SessionFactoryImplementor; /** @@ -46,7 +47,7 @@ public class MetamodelImpl implements Metamodel, Serializable { /** * Build the metamodel using the information from the collection of Hibernate - * {@link PersistentClass} models as well as the Hibernate {@link SessionFactory}. + * {@link PersistentClass} models as well as the Hibernate {@link org.hibernate.SessionFactory}. * * @param persistentClasses Iterator over the Hibernate (config-time) metamodel * @param sessionFactory The Hibernate session factry. @@ -74,16 +75,24 @@ public class MetamodelImpl implements Metamodel, Serializable { return entityType; } - @SuppressWarnings({ "unchecked" }) + //TODO remove / reduce @SW scope + @SuppressWarnings( "unchecked" ) private static EntityTypeImpl buildEntityType(PersistentClass persistentClass, MetadataContext context) { - final PersistentClass superPersistentClass = persistentClass.getSuperclass(); - final EntityTypeImpl superEntityType = superPersistentClass == null + final MappedSuperclass superMappedSuperclass = persistentClass.getSuperMappedSuperclass(); + AbstractIdentifiableType superType = superMappedSuperclass == null ? null - : locateOrBuildEntityType( superPersistentClass, context ); + : locateOrBuildMappedsuperclassType( superMappedSuperclass, context ); + //no mappedSuperclass, check for a super entity + if (superType == null) { + final PersistentClass superPersistentClass = persistentClass.getSuperclass(); + superType = superPersistentClass == null + ? null + : locateOrBuildEntityType( superPersistentClass, context ); + } final Class javaType = persistentClass.getMappedClass(); EntityTypeImpl entityType = new EntityTypeImpl( javaType, - superEntityType, + superType, persistentClass.getClassName(), persistentClass.hasIdentifierProperty(), persistentClass.isVersioned() @@ -92,6 +101,41 @@ public class MetamodelImpl implements Metamodel, Serializable { return entityType; } + private static MappedSuperclassTypeImpl locateOrBuildMappedsuperclassType( + MappedSuperclass mappedSuperclass, MetadataContext context) { + MappedSuperclassTypeImpl mappedSuperclassType = context.locateMappedSuperclassType( mappedSuperclass ); + if ( mappedSuperclassType == null ) { + mappedSuperclassType = buildMappedSuperclassType(mappedSuperclass, context); + } + return mappedSuperclassType; + } + + //TODO remove / reduce @SW scope + @SuppressWarnings( "unchecked" ) + private static MappedSuperclassTypeImpl buildMappedSuperclassType(MappedSuperclass mappedSuperclass, + MetadataContext context) { + final MappedSuperclass superMappedSuperclass = mappedSuperclass.getSuperMappedSuperclass(); + AbstractIdentifiableType superType = superMappedSuperclass == null + ? null + : locateOrBuildMappedsuperclassType( superMappedSuperclass, context ); + //no mappedSuperclass, check for a super entity + if (superType == null) { + final PersistentClass superPersistentClass = mappedSuperclass.getSuperPersistentClass(); + superType = superPersistentClass == null + ? null + : locateOrBuildEntityType( superPersistentClass, context ); + } + final Class javaType = mappedSuperclass.getMappedClass(); + MappedSuperclassTypeImpl mappedSuperclassType = new MappedSuperclassTypeImpl( + javaType, + superType, + mappedSuperclass.hasIdentifierProperty(), + mappedSuperclass.isVersioned() + ); + context.registerMappedSuperclassType( mappedSuperclass, mappedSuperclassType ); + return mappedSuperclassType; + } + /** * Instantiate the metamodel. * diff --git a/entitymanager/src/test/java/org/hibernate/ejb/test/metadata/Animal.java b/entitymanager/src/test/java/org/hibernate/ejb/test/metadata/Animal.java new file mode 100644 index 0000000000..5e54db40ad --- /dev/null +++ b/entitymanager/src/test/java/org/hibernate/ejb/test/metadata/Animal.java @@ -0,0 +1,31 @@ +package org.hibernate.ejb.test.metadata; + +import javax.persistence.MappedSuperclass; +import javax.persistence.Id; +import javax.persistence.GeneratedValue; + +/** + * @author Emmanuel Bernard + */ +@MappedSuperclass +public class Animal extends SubThing { + private Long id; + private int legNbr; + + @Id @GeneratedValue + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public int getLegNbr() { + return legNbr; + } + + public void setLegNbr(int legNbr) { + this.legNbr = legNbr; + } +} diff --git a/entitymanager/src/test/java/org/hibernate/ejb/test/metadata/Cat.java b/entitymanager/src/test/java/org/hibernate/ejb/test/metadata/Cat.java new file mode 100644 index 0000000000..73743c457f --- /dev/null +++ b/entitymanager/src/test/java/org/hibernate/ejb/test/metadata/Cat.java @@ -0,0 +1,19 @@ +package org.hibernate.ejb.test.metadata; + +import javax.persistence.Entity; + +/** + * @author Emmanuel Bernard + */ +@Entity +public class Cat extends Cattish { + private String nickname; + + public String getNickname() { + return nickname; + } + + public void setNickname(String nickname) { + this.nickname = nickname; + } +} diff --git a/entitymanager/src/test/java/org/hibernate/ejb/test/metadata/Cattish.java b/entitymanager/src/test/java/org/hibernate/ejb/test/metadata/Cattish.java new file mode 100644 index 0000000000..c7f1ae5cc8 --- /dev/null +++ b/entitymanager/src/test/java/org/hibernate/ejb/test/metadata/Cattish.java @@ -0,0 +1,19 @@ +package org.hibernate.ejb.test.metadata; + +import javax.persistence.MappedSuperclass; + +/** + * @author Emmanuel Bernard + */ +@MappedSuperclass +public class Cattish extends Feline { + private long hoursOfSleep; + + public long getHoursOfSleep() { + return hoursOfSleep; + } + + public void setHoursOfSleep(long hoursOfSleep) { + this.hoursOfSleep = hoursOfSleep; + } +} diff --git a/entitymanager/src/test/java/org/hibernate/ejb/test/metadata/Dog.java b/entitymanager/src/test/java/org/hibernate/ejb/test/metadata/Dog.java new file mode 100644 index 0000000000..b319840d3d --- /dev/null +++ b/entitymanager/src/test/java/org/hibernate/ejb/test/metadata/Dog.java @@ -0,0 +1,19 @@ +package org.hibernate.ejb.test.metadata; + +import javax.persistence.Entity; + +/** + * @author Emmanuel Bernard + */ +@Entity +public class Dog extends Animal { + private String name; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } +} diff --git a/entitymanager/src/test/java/org/hibernate/ejb/test/metadata/Feline.java b/entitymanager/src/test/java/org/hibernate/ejb/test/metadata/Feline.java new file mode 100644 index 0000000000..fafc2e82e0 --- /dev/null +++ b/entitymanager/src/test/java/org/hibernate/ejb/test/metadata/Feline.java @@ -0,0 +1,19 @@ +package org.hibernate.ejb.test.metadata; + +import javax.persistence.Entity; + +/** + * @author Emmanuel Bernard + */ +@Entity +public class Feline extends Animal { + private String color; + + public String getColor() { + return color; + } + + public void setColor(String color) { + this.color = color; + } +} diff --git a/entitymanager/src/test/java/org/hibernate/ejb/test/metadata/MetadataTest.java b/entitymanager/src/test/java/org/hibernate/ejb/test/metadata/MetadataTest.java index fc28f252df..db44c8958a 100644 --- a/entitymanager/src/test/java/org/hibernate/ejb/test/metadata/MetadataTest.java +++ b/entitymanager/src/test/java/org/hibernate/ejb/test/metadata/MetadataTest.java @@ -12,6 +12,9 @@ import javax.persistence.metamodel.SetAttribute; import javax.persistence.metamodel.PluralAttribute; import javax.persistence.metamodel.MapAttribute; import javax.persistence.metamodel.ListAttribute; +import javax.persistence.metamodel.MappedSuperclassType; +import javax.persistence.metamodel.CollectionAttribute; +import javax.persistence.metamodel.IdentifiableType; import org.hibernate.ejb.test.TestCase; @@ -53,6 +56,8 @@ public class MetadataTest extends TestCase { assertFalse( fridgeType.hasVersionAttribute() ); assertEquals( Type.PersistenceType.ENTITY, fridgeType.getPersistenceType() ); + assertEquals( 3, fridgeType.getDeclaredAttributes().size() ); + final EntityType houseType = factory.getMetamodel().entity( House.class ); assertTrue( houseType.hasSingleIdAttribute() ); final SingularAttribute houseId = houseType.getDeclaredId( House.Key.class ); @@ -74,6 +79,7 @@ public class MetadataTest extends TestCase { final SingularAttribute version = foodType.getVersion( Long.class ); assertNotNull( version ); assertTrue( version.isVersion() ); + assertEquals( 3, foodType.getDeclaredAttributes().size() ); } @@ -158,7 +164,84 @@ public class MetadataTest extends TestCase { assertEquals( PluralAttribute.CollectionType.LIST, roomsBySize.getCollectionType() ); } - //todo test plural + public void testHierarchy() { + final EntityType cat = factory.getMetamodel().entity( Cat.class ); + assertNotNull( cat ); + assertEquals( 7, cat.getAttributes().size() ); + assertEquals( 1, cat.getDeclaredAttributes().size() ); + + assertTrue( cat.hasVersionAttribute() ); + assertEquals( "version", cat.getVersion(Long.class).getName() ); + verifyDeclaredVersiobnNotPresent( cat ); + verifyDeclaredIdNotPresentAndIdPresent(cat); + + assertEquals( Type.PersistenceType.MAPPED_SUPERCLASS, cat.getSupertype().getPersistenceType() ); + MappedSuperclassType cattish = (MappedSuperclassType) cat.getSupertype(); + assertEquals( 6, cattish.getAttributes().size() ); + assertEquals( 1, cattish.getDeclaredAttributes().size() ); + + assertTrue( cattish.hasVersionAttribute() ); + assertEquals( "version", cattish.getVersion(Long.class).getName() ); + verifyDeclaredVersiobnNotPresent( cattish ); + verifyDeclaredIdNotPresentAndIdPresent(cattish); + + assertEquals( Type.PersistenceType.ENTITY, cattish.getSupertype().getPersistenceType() ); + EntityType feline = (EntityType) cattish.getSupertype(); + assertEquals( 5, feline.getAttributes().size() ); + assertEquals( 1, feline.getDeclaredAttributes().size() ); + + assertTrue( feline.hasVersionAttribute() ); + assertEquals( "version", feline.getVersion(Long.class).getName() ); + verifyDeclaredVersiobnNotPresent( feline ); + verifyDeclaredIdNotPresentAndIdPresent(feline); + + assertEquals( Type.PersistenceType.MAPPED_SUPERCLASS, feline.getSupertype().getPersistenceType() ); + MappedSuperclassType animal = (MappedSuperclassType) feline.getSupertype(); + assertEquals( 4, animal.getAttributes().size() ); + assertEquals( 2, animal.getDeclaredAttributes().size() ); + + assertTrue( animal.hasVersionAttribute() ); + assertEquals( "version", animal.getVersion(Long.class).getName() ); + verifyDeclaredVersiobnNotPresent( animal ); + assertEquals( "id", animal.getId(Long.class).getName() ); + assertEquals( "id", animal.getDeclaredId(Long.class).getName() ); + + assertEquals( Type.PersistenceType.MAPPED_SUPERCLASS, animal.getSupertype().getPersistenceType() ); + MappedSuperclassType thing = (MappedSuperclassType) animal.getSupertype(); + assertEquals( 2, thing.getAttributes().size() ); + assertEquals( 2, thing.getDeclaredAttributes().size() ); + final SingularAttribute weight = thing.getDeclaredSingularAttribute( "weight", Double.class ); + assertEquals( Double.class, weight.getJavaType() ); + + assertEquals( "version", thing.getVersion(Long.class).getName() ); + assertEquals( "version", thing.getDeclaredVersion(Long.class).getName() ); + assertNull( thing.getId( Long.class ) ); + + assertNull( thing.getSupertype() ); + } + + private void verifyDeclaredIdNotPresentAndIdPresent(IdentifiableType type) { + assertEquals( "id", type.getId(Long.class).getName() ); + try { + type.getDeclaredId(Long.class); + fail("Should not have a declared id"); + } + catch (IllegalArgumentException e) { + //success + } + } + + private void verifyDeclaredVersiobnNotPresent(IdentifiableType type) { + try { + type.getDeclaredVersion(Long.class); + fail("Should not have a declared version"); + } + catch (IllegalArgumentException e) { + //success + } + } + + //todo test plural @Override public Class[] getAnnotatedClasses() { @@ -166,7 +249,11 @@ public class MetadataTest extends TestCase { Fridge.class, FoodItem.class, Person.class, - House.class + House.class, + Dog.class, + Cat.class, + Cattish.class, + Feline.class }; } diff --git a/entitymanager/src/test/java/org/hibernate/ejb/test/metadata/SubThing.java b/entitymanager/src/test/java/org/hibernate/ejb/test/metadata/SubThing.java new file mode 100644 index 0000000000..292fcada8e --- /dev/null +++ b/entitymanager/src/test/java/org/hibernate/ejb/test/metadata/SubThing.java @@ -0,0 +1,17 @@ +package org.hibernate.ejb.test.metadata; + +/** + * @author Emmanuel Bernard + */ +//not an entity but in between mapped superclass and entity +public class SubThing extends Thing { + private String blah; + + public String getBlah() { + return blah; + } + + public void setBlah(String blah) { + this.blah = blah; + } +} diff --git a/entitymanager/src/test/java/org/hibernate/ejb/test/metadata/Thing.java b/entitymanager/src/test/java/org/hibernate/ejb/test/metadata/Thing.java new file mode 100644 index 0000000000..b73539e811 --- /dev/null +++ b/entitymanager/src/test/java/org/hibernate/ejb/test/metadata/Thing.java @@ -0,0 +1,30 @@ +package org.hibernate.ejb.test.metadata; + +import javax.persistence.MappedSuperclass; +import javax.persistence.Version; + +/** + * @author Emmanuel Bernard + */ +@MappedSuperclass +public class Thing { + private Double weight; + private Long version; + + public Double getWeight() { + return weight; + } + + public void setWeight(Double weight) { + this.weight = weight; + } + + @Version + public Long getVersion() { + return version; + } + + public void setVersion(Long version) { + this.version = version; + } +}