HHH-9055 - Binding support for IdClass and MapsId needs a complete review

This commit is contained in:
Steve Ebersole 2014-04-08 13:30:04 -05:00
parent 700a233c41
commit 104eea1beb
15 changed files with 399 additions and 14 deletions

View File

@ -18,6 +18,9 @@ import org.hibernate.metamodel.spi.AttributeRole;
import org.hibernate.metamodel.spi.SingularAttributeNature; import org.hibernate.metamodel.spi.SingularAttributeNature;
import org.hibernate.type.ForeignKeyDirection; import org.hibernate.type.ForeignKeyDirection;
import org.jboss.jandex.AnnotationInstance;
import org.jboss.jandex.AnnotationValue;
public abstract class AbstractToOneAttributeSourceImpl extends SingularAttributeSourceImpl implements ToOneAttributeSource{ public abstract class AbstractToOneAttributeSourceImpl extends SingularAttributeSourceImpl implements ToOneAttributeSource{
private final SingularAssociationAttribute associationAttribute; private final SingularAssociationAttribute associationAttribute;
private final Set<CascadeStyle> unifiedCascadeStyles; private final Set<CascadeStyle> unifiedCascadeStyles;
@ -25,10 +28,14 @@ public abstract class AbstractToOneAttributeSourceImpl extends SingularAttribute
private SingularAttributeNature singularAttributeNature; private SingularAttributeNature singularAttributeNature;
private final Set<MappedByAssociationSource> ownedAssociationSources = new HashSet<MappedByAssociationSource>(); private final Set<MappedByAssociationSource> ownedAssociationSources = new HashSet<MappedByAssociationSource>();
private final MapsIdSourceImpl mapsIdSource;
public AbstractToOneAttributeSourceImpl(SingularAssociationAttribute associationAttribute) { public AbstractToOneAttributeSourceImpl(SingularAssociationAttribute associationAttribute) {
super( associationAttribute ); super( associationAttribute );
this.associationAttribute = associationAttribute; this.associationAttribute = associationAttribute;
this.unifiedCascadeStyles = determineCascadeStyles( associationAttribute ); this.unifiedCascadeStyles = determineCascadeStyles( associationAttribute );
this.mapsIdSource = MapsIdSourceImpl.interpret( associationAttribute.getMapsIdAnnotation() );
} }
private static Set<CascadeStyle> determineCascadeStyles(SingularAssociationAttribute associationAttribute) { private static Set<CascadeStyle> determineCascadeStyles(SingularAssociationAttribute associationAttribute) {
@ -146,4 +153,39 @@ public abstract class AbstractToOneAttributeSourceImpl extends SingularAttribute
public AttributeRole getAttributeRole() { public AttributeRole getAttributeRole() {
return associationAttribute.getRole(); return associationAttribute.getRole();
} }
@Override
public MapsIdSource getMapsIdSource() {
return mapsIdSource;
}
private static class MapsIdSourceImpl implements MapsIdSource {
private final boolean present;
private final String name;
public static MapsIdSourceImpl interpret(AnnotationInstance mapsIdAnnotation) {
if ( mapsIdAnnotation == null ) {
return new MapsIdSourceImpl( false, null );
}
final AnnotationValue value = mapsIdAnnotation.value();
final String name = value == null ? null : value.asString();
return new MapsIdSourceImpl( true, name );
}
private MapsIdSourceImpl(boolean present, String name) {
this.present = present;
this.name = name;
}
@Override
public boolean isDefined() {
return present;
}
@Override
public String getLookupClassAttributeName() {
return name;
}
}
} }

View File

@ -89,7 +89,9 @@ public class ToOneAttributeSourceImpl extends AbstractToOneAttributeSourceImpl i
this.containingTableName = resolveContainingTableName( associationAttribute, relationalValueSources ); this.containingTableName = resolveContainingTableName( associationAttribute, relationalValueSources );
setSingularAttributeNature( determineNatureIfPossible( associationAttribute ) ); setSingularAttributeNature( determineNatureIfPossible( associationAttribute ) );
this.foreignKeyDelegate = new ForeignKeyDelegate( this.foreignKeyDelegate = new ForeignKeyDelegate(
associationAttribute().getBackingMember().getAnnotations(), cls); associationAttribute().getBackingMember().getAnnotations(),
cls
);
} }
private SingularAttributeNature determineNatureIfPossible( private SingularAttributeNature determineNatureIfPossible(

View File

@ -173,9 +173,7 @@ public class PluralAttribute
this.classLoaderService = getContext().getServiceRegistry().getService( ClassLoaderService.class ); this.classLoaderService = getContext().getServiceRegistry().getService( ClassLoaderService.class );
// just to get the error the test expects AssociationHelper.INSTANCE.locateMapsId(
// todo : I'd really rather see this driven from the class-level, not the attribute-level
AssociationHelper.INSTANCE.determineMapsId(
backingMember, backingMember,
attributeNature, attributeNature,
container.getLocalBindingContext() container.getLocalBindingContext()

View File

@ -67,7 +67,7 @@ public class SingularAssociationAttribute
private ArrayList<Column> joinColumnValues = new ArrayList<Column>(); private ArrayList<Column> joinColumnValues = new ArrayList<Column>();
private ArrayList<Column> inverseJoinColumnValues = new ArrayList<Column>(); private ArrayList<Column> inverseJoinColumnValues = new ArrayList<Column>();
private final String mapsIdAttributeName; private final AnnotationInstance mapsIdAnnotation;
private final boolean hasPrimaryKeyJoinColumn; private final boolean hasPrimaryKeyJoinColumn;
@ -121,7 +121,7 @@ public class SingularAssociationAttribute
this.isOrphanRemoval = AssociationHelper.INSTANCE.determineOrphanRemoval( associationAnnotation ); this.isOrphanRemoval = AssociationHelper.INSTANCE.determineOrphanRemoval( associationAnnotation );
this.ignoreNotFound = AssociationHelper.INSTANCE.determineWhetherToIgnoreNotFound( backingMember ); this.ignoreNotFound = AssociationHelper.INSTANCE.determineWhetherToIgnoreNotFound( backingMember );
this.mapsIdAttributeName = AssociationHelper.INSTANCE.determineMapsId( this.mapsIdAnnotation = AssociationHelper.INSTANCE.locateMapsId(
backingMember, backingMember,
attributeNature, attributeNature,
container.getLocalBindingContext() container.getLocalBindingContext()
@ -262,4 +262,8 @@ public class SingularAssociationAttribute
public ArrayList<Column> getInverseJoinColumnValues() { public ArrayList<Column> getInverseJoinColumnValues() {
return inverseJoinColumnValues; return inverseJoinColumnValues;
} }
public AnnotationInstance getMapsIdAnnotation() {
return mapsIdAnnotation;
}
} }

View File

@ -258,7 +258,7 @@ public class AssociationHelper {
return false; return false;
} }
public String determineMapsId( public AnnotationInstance locateMapsId(
MemberDescriptor member, MemberDescriptor member,
PersistentAttribute.Nature attributeNature, PersistentAttribute.Nature attributeNature,
EntityBindingContext localBindingContext) { EntityBindingContext localBindingContext) {
@ -275,9 +275,7 @@ public class AssociationHelper {
"associations, property: " + member.toString() "associations, property: " + member.toString()
); );
} }
return mapsIdAnnotation.value() == null return mapsIdAnnotation;
? null
: mapsIdAnnotation.value().asString();
} }
public boolean determineWhetherToIgnoreNotFound(MemberDescriptor backingMember) { public boolean determineWhetherToIgnoreNotFound(MemberDescriptor backingMember) {

View File

@ -269,4 +269,25 @@ public abstract class AbstractToOneAttributeSourceImpl extends AbstractHbmSource
return true; return true;
} }
@Override
public MapsIdSource getMapsIdSource() {
return MapsIdSourceImpl.INSTANCE;
}
private static class MapsIdSourceImpl implements MapsIdSource {
/**
* Singleton access
*/
public static final MapsIdSourceImpl INSTANCE = new MapsIdSourceImpl();
@Override
public boolean isDefined() {
return false;
}
@Override
public String getLookupClassAttributeName() {
return null;
}
}
} }

View File

@ -41,8 +41,29 @@ public interface ToOneAttributeSource
FetchableAttributeSource, FetchableAttributeSource,
AssociationSource { AssociationSource {
public MapsIdSource getMapsIdSource();
public boolean isUnique(); public boolean isUnique();
public boolean isUnWrapProxy(); public boolean isUnWrapProxy();
ForeignKeyDirection getForeignKeyDirection(); ForeignKeyDirection getForeignKeyDirection();
public List<Binder.DefaultNamingStrategy> getDefaultNamingStrategies(final String entityName, final String tableName, final AttributeBinding referencedAttributeBinding); public List<Binder.DefaultNamingStrategy> getDefaultNamingStrategies(final String entityName, final String tableName, final AttributeBinding referencedAttributeBinding);
/**
* Source for JPA {@link javax.persistence.MapsId} annotation information
*/
public static interface MapsIdSource {
/**
* Was a MapsId annotation present on the to-one?
*
* @return {@code true} if MapsId annotation was present; {@code false} otherwise.
*/
public boolean isDefined();
/**
* The {@link javax.persistence.MapsId#value()} value.
*
* @return The indicated name
*/
public String getLookupClassAttributeName();
}
} }

View File

@ -65,11 +65,12 @@ import static org.hibernate.id.EntityIdentifierNature.SIMPLE;
*/ */
public class EntityIdentifier { public class EntityIdentifier {
private final EntityBinding entityBinding; private final EntityBinding entityBinding;
private EntityIdentifierBinding entityIdentifierBinding;
private IdentifierGenerator identifierGenerator;
private Binding binding;
private LookupClassBinding lookupClassBinding; private LookupClassBinding lookupClassBinding;
private IdentifierGenerator identifierGenerator;
/** /**
* Create an identifier * Create an identifier
* *
@ -140,7 +141,7 @@ public class EntityIdentifier {
public EntityIdentifierNature getNature() { public EntityIdentifierNature getNature() {
ensureBound(); ensureBound();
return entityIdentifierBinding.getNature(); return binding.getNature();
} }
public SingularAttributeBinding getAttributeBinding() { public SingularAttributeBinding getAttributeBinding() {
@ -163,7 +164,7 @@ public class EntityIdentifier {
public String getUnsavedValue() { public String getUnsavedValue() {
ensureBound(); ensureBound();
return entityIdentifierBinding.getUnsavedValue(); return binding.getUnsavedValue();
} }
public boolean isNonAggregatedComposite() { public boolean isNonAggregatedComposite() {
@ -590,4 +591,12 @@ public class EntityIdentifier {
} }
} }
public static interface Binding {
public EntityIdentifierNature getNature();
public String getUnsavedValue();
public IdentifierGeneratorDefinition getGeneratorDefinition();
}
} }

View File

@ -0,0 +1,38 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2014, 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.test.metamodel.derivedid.e1;
import javax.persistence.Entity;
import javax.persistence.Id;
/**
* Taken from "Example 1" of the JPA spec on derived identities.
*
* @author Steve Ebersole
*/
@Entity
public class Employee {
@Id long empId;
String empName;
}

View File

@ -0,0 +1,44 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2014, 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.test.metamodel.derivedid.e1.a;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.IdClass;
import javax.persistence.ManyToOne;
import org.hibernate.test.metamodel.derivedid.e1.Employee;
/**
* @author Steve Ebersole
*/
@Entity
@IdClass(DependentId.class)
public class Dependent {
@Id
String name;
@Id @ManyToOne
Employee emp;
}

View File

@ -0,0 +1,32 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2014, 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.test.metamodel.derivedid.e1.a;
/**
* @author Steve Ebersole
*/
public class DependentId {
String name; // matches name of @Id attribute
long emp; // matches name of @Id attribute and type of Employee PK
}

View File

@ -0,0 +1,80 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2014, 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.test.metamodel.derivedid.e1.a;
import org.hibernate.id.EntityIdentifierNature;
import org.hibernate.metamodel.Metadata;
import org.hibernate.metamodel.MetadataSources;
import org.hibernate.metamodel.spi.binding.EntityBinding;
import org.hibernate.metamodel.spi.binding.SingularAttributeBinding;
import org.hibernate.type.LongType;
import org.hibernate.type.Type;
import org.hibernate.testing.junit4.BaseUnitTestCase;
import org.hibernate.testing.junit4.ExtraAssertions;
import org.hibernate.test.metamodel.derivedid.e1.Employee;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
/**
* @author Steve Ebersole
*/
public class MappingTest extends BaseUnitTestCase {
/**
* Test for the mapping produced from e1.a
*/
@Test
public void testMapping() {
MetadataSources sources = new MetadataSources()
.addAnnotatedClass( Employee.class )
.addAnnotatedClass( DependentId.class )
.addAnnotatedClass( Dependent.class );
Metadata metadata = sources.buildMetadata();
EntityBinding employeeBinding = metadata.getEntityBinding( Employee.class.getName() );
assertEquals(
EntityIdentifierNature.SIMPLE,
employeeBinding.getHierarchyDetails().getEntityIdentifier().getNature()
);
SingularAttributeBinding empIdAttrBinding = employeeBinding.getHierarchyDetails().getEntityIdentifier().getAttributeBinding();
Type empIdType = empIdAttrBinding.getHibernateTypeDescriptor().getResolvedTypeMapping();
assertNotNull( empIdType );
ExtraAssertions.assertTyping( LongType.class, empIdType );
EntityBinding depBinding = metadata.getEntityBinding( Dependent.class.getName() );
assertEquals(
EntityIdentifierNature.NON_AGGREGATED_COMPOSITE,
depBinding.getHierarchyDetails().getEntityIdentifier().getNature()
);
assertNotNull( depBinding.getHierarchyDetails().getEntityIdentifier().getLookupClassBinding().getIdClassType() );
// The issue here is the lack of understanding that the IdClass attributes
// are not the same types as the entity attribute(s).
//
// For example, here we currently assume that the type of DependentId.emp
// matches the Dependent.emp type
}
}

View File

@ -0,0 +1,28 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2014, 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
*/
/**
* Represents the 1.a example of JPA derived identity section.
*/
package org.hibernate.test.metamodel.derivedid.e1.a;

View File

@ -0,0 +1,32 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2014, 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
*/
/**
* Example 1 in the JPA spec illustrates "derived identity" support when "The
* parent entity has a simple primary key". It then lists 2 cases:<ul>
* <li>"The dependent entity uses IdClass to represent a composite key"</li>
* <li>"The dependent entity uses EmbeddedId to represent a composite key"</li>
* </ul>
*/
package org.hibernate.test.metamodel.derivedid.e1;

View File

@ -0,0 +1,36 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2014, 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
*/
/**
* Test support for a loosely related set of features that JPA groups together
* under the term "derived identifiers".
* <p/>
* "Derived identifiers" simply means that an entity's identifier is (at least
* partially) derived from another entity. This is various forms of an
* identifier which includes one or more to-one associations.
* <p/>
* The JPA spec breaks this down into 6 top-level examples to discuss the
* implications of "derived identifiers" in different (6 different) scenarios.
*/
package org.hibernate.test.metamodel.derivedid;