HHH-7471: Fixed back ref support. This is now done at the end of building metadata within the MetadataImpl, as opposed to within the Binder, since we need everything resolved on both sides of an association to know when a back ref is needed. Also corrected some issues with the hbm file.

This commit is contained in:
John Verhaeg 2012-07-24 17:02:13 -05:00
parent df74a4d55f
commit 37d365cdf0
5 changed files with 1210 additions and 1164 deletions

View File

@ -0,0 +1,2 @@
package org.hibernate.boot;

View File

@ -25,10 +25,9 @@ package org.hibernate.metamodel.internal;
import java.io.Serializable;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.jboss.logging.Logger;
import org.hibernate.AssertionFailure;
import org.hibernate.DuplicateMappingException;
import org.hibernate.MappingException;
@ -41,6 +40,7 @@ import org.hibernate.engine.ResultSetMappingDefinition;
import org.hibernate.engine.spi.FilterDefinition;
import org.hibernate.engine.spi.NamedQueryDefinition;
import org.hibernate.engine.spi.NamedSQLQueryDefinition;
import org.hibernate.engine.spi.SyntheticAttributeHelper;
import org.hibernate.id.factory.IdentifierGeneratorFactory;
import org.hibernate.id.factory.spi.MutableIdentifierGeneratorFactory;
import org.hibernate.integrator.spi.Integrator;
@ -55,13 +55,21 @@ import org.hibernate.metamodel.internal.source.annotations.AnnotationMetadataSou
import org.hibernate.metamodel.internal.source.hbm.HbmMetadataSourceProcessorImpl;
import org.hibernate.metamodel.spi.MetadataSourceProcessor;
import org.hibernate.metamodel.spi.binding.AttributeBinding;
import org.hibernate.metamodel.spi.binding.BackRefAttributeBinding;
import org.hibernate.metamodel.spi.binding.EntityBinding;
import org.hibernate.metamodel.spi.binding.FetchProfile;
import org.hibernate.metamodel.spi.binding.HibernateTypeDescriptor;
import org.hibernate.metamodel.spi.binding.IdGenerator;
import org.hibernate.metamodel.spi.binding.ManyToOneAttributeBinding;
import org.hibernate.metamodel.spi.binding.PluralAttributeBinding;
import org.hibernate.metamodel.spi.binding.PluralAttributeElementNature;
import org.hibernate.metamodel.spi.binding.PluralAttributeKeyBinding;
import org.hibernate.metamodel.spi.binding.RelationalValueBinding;
import org.hibernate.metamodel.spi.binding.TypeDefinition;
import org.hibernate.metamodel.spi.domain.BasicType;
import org.hibernate.metamodel.spi.domain.SingularAttribute;
import org.hibernate.metamodel.spi.domain.Type;
import org.hibernate.metamodel.spi.relational.Column;
import org.hibernate.metamodel.spi.relational.Database;
import org.hibernate.metamodel.spi.source.FilterDefinitionSource;
import org.hibernate.metamodel.spi.source.IdentifierGeneratorSource;
@ -69,10 +77,10 @@ import org.hibernate.metamodel.spi.source.MappingDefaults;
import org.hibernate.metamodel.spi.source.MetaAttributeContext;
import org.hibernate.metamodel.spi.source.MetadataImplementor;
import org.hibernate.metamodel.spi.source.TypeDescriptorSource;
import org.hibernate.persister.spi.PersisterClassResolver;
import org.hibernate.service.ServiceRegistry;
import org.hibernate.service.classloading.spi.ClassLoaderService;
import org.hibernate.type.TypeResolver;
import org.jboss.logging.Logger;
/**
* Container for configuration data collected during binding the metamodel.
@ -92,7 +100,7 @@ public class MetadataImpl implements MetadataImplementor, Serializable {
private final Options options;
private final ValueHolder<ClassLoaderService> classLoaderService;
private final ValueHolder<PersisterClassResolver> persisterClassResolverService;
// private final ValueHolder<PersisterClassResolver> persisterClassResolverService;
private TypeResolver typeResolver = new TypeResolver();
@ -159,14 +167,14 @@ public class MetadataImpl implements MetadataImplementor, Serializable {
}
}
);
this.persisterClassResolverService = new ValueHolder<PersisterClassResolver>(
new ValueHolder.DeferredInitializer<PersisterClassResolver>() {
@Override
public PersisterClassResolver initialize() {
return serviceRegistry.getService( PersisterClassResolver.class );
}
}
);
// this.persisterClassResolverService = new ValueHolder<PersisterClassResolver>(
// new ValueHolder.DeferredInitializer<PersisterClassResolver>() {
// @Override
// public PersisterClassResolver initialize() {
// return serviceRegistry.getService( PersisterClassResolver.class );
// }
// }
// );
//check for typeContributingIntegrators integrators
for ( Integrator integrator : serviceRegistry.getService( IntegratorService.class ).getIntegrators() ) {
@ -303,6 +311,71 @@ public class MetadataImpl implements MetadataImplementor, Serializable {
}
private void bindMappingDependentMetadata(MetadataSourceProcessor[] metadataSourceProcessors) {
// Create required back references, which are required for one-to-many associations with key bindings that are non-inverse,
// non-nullable, and unidirectional
for ( PluralAttributeBinding pluralAttributeBinding : collectionBindingMap.values() ) {
// Find one-to-many associations with key bindings that are non-inverse and non-nullable
PluralAttributeKeyBinding keyBinding = pluralAttributeBinding.getPluralAttributeKeyBinding();
if ( keyBinding.isInverse() || keyBinding.isNullable() ||
pluralAttributeBinding.getPluralAttributeElementBinding().getPluralAttributeElementNature() !=
PluralAttributeElementNature.ONE_TO_MANY ) {
continue;
}
// Ensure this isn't a bidirectional association by ensuring FK columns don't match relational columns of any
// many-to-one on opposite side
EntityBinding referencedEntityBinding =
entityBindingMap.get(
pluralAttributeBinding.getPluralAttributeElementBinding().getHibernateTypeDescriptor().
getResolvedTypeMapping().getName() );
List<Column> columns = keyBinding.getForeignKey().getColumns();
boolean bidirectional = false;
for ( AttributeBinding attributeBinding : referencedEntityBinding.attributeBindings() ) {
if ( !(attributeBinding instanceof ManyToOneAttributeBinding) ) {
continue;
}
// Check if the opposite many-to-one attribute binding references the one-to-many attribute binding being processed
ManyToOneAttributeBinding manyToOneAttributeBinding = ( ManyToOneAttributeBinding ) attributeBinding;
if ( !manyToOneAttributeBinding.getReferencedEntityBinding().equals(
pluralAttributeBinding.getContainer().seekEntityBinding() ) ) {
continue;
}
// Check if the many-to-one attribute binding's columns match the one-to-many attribute binding's FK columns
// (meaning this is a bidirectional association, and no back reference should be created)
List<RelationalValueBinding> valueBindings = manyToOneAttributeBinding.getRelationalValueBindings();
if ( columns.size() != valueBindings.size() ) {
continue;
}
bidirectional = true;
for ( int ndx = valueBindings.size(); --ndx >= 0; ) {
if ( columns.get(ndx) != valueBindings.get( ndx ).getValue() ) {
bidirectional = false;
break;
}
}
if ( bidirectional ) {
break;
}
}
if ( bidirectional ) continue;
// Create the synthetic back reference attribute
SingularAttribute syntheticAttribute =
referencedEntityBinding.getEntity().createSyntheticSingularAttribute(
SyntheticAttributeHelper.createBackRefAttributeName( pluralAttributeBinding.getAttribute().getRole() ) );
// Create the back reference attribute binding.
BackRefAttributeBinding backRefAttributeBinding =
referencedEntityBinding.makeBackRefAttributeBinding( syntheticAttribute, pluralAttributeBinding );
final HibernateTypeDescriptor keyTypeDescriptor = keyBinding.getHibernateTypeDescriptor();
final HibernateTypeDescriptor hibernateTypeDescriptor = backRefAttributeBinding.getHibernateTypeDescriptor();
hibernateTypeDescriptor.setJavaTypeName( keyTypeDescriptor.getJavaTypeName() );
hibernateTypeDescriptor.setExplicitTypeName( keyTypeDescriptor.getExplicitTypeName() );
hibernateTypeDescriptor.setToOne( keyTypeDescriptor.isToOne() );
hibernateTypeDescriptor.getTypeParameters().putAll( keyTypeDescriptor.getTypeParameters() );
hibernateTypeDescriptor.setResolvedTypeMapping( keyTypeDescriptor.getResolvedTypeMapping() );
backRefAttributeBinding.getAttribute().resolveType(
keyBinding.getReferencedAttributeBinding().getAttribute().getSingularAttributeType() );
}
for ( MetadataSourceProcessor metadataSourceProcessor : metadataSourceProcessors ) {
metadataSourceProcessor.processMappingDependentMetadata();
}
@ -383,9 +456,9 @@ public class MetadataImpl implements MetadataImplementor, Serializable {
return classLoaderService.getValue();
}
private PersisterClassResolver persisterClassResolverService() {
return persisterClassResolverService.getValue();
}
// private PersisterClassResolver persisterClassResolverService() {
// return persisterClassResolverService.getValue();
// }
@Override
public Options getOptions() {

View File

@ -57,8 +57,10 @@
<many-to-one name="vehicle"
column="vehicleID"
unique="false"
not-null="true"
not-null="false"
cascade="none"
insert = "false"
update = "false"
lazy="false"/>
</class>

View File

@ -23,6 +23,10 @@
*/
package org.hibernate.test.cascade.circle;
import static org.hamcrest.core.IsInstanceOf.instanceOf;
import static org.junit.Assert.assertThat;
import org.junit.Test;
import org.hibernate.Session;
@ -119,7 +123,7 @@ public class CascadeMergeToChildBeforeParentTest extends BaseCoreFunctionalTestC
route.getNodes().add( pickupNode );
route.getNodes().add( deliveryNode );
Route mergedRoute = (Route) s.merge( route );
assertThat( s.merge( route ), instanceOf( Route.class ) );
s.getTransaction().commit();
s.close();
@ -189,7 +193,7 @@ public class CascadeMergeToChildBeforeParentTest extends BaseCoreFunctionalTestC
vehicle.setTransientField( "anewvalue" );
vehicle.setRoute( route );
Route mergedRoute = (Route) s.merge( route );
assertThat( s.merge( route ), instanceOf( Route.class ) );
s.getTransaction().commit();
s.close();
@ -276,7 +280,7 @@ public class CascadeMergeToChildBeforeParentTest extends BaseCoreFunctionalTestC
vehicle.setTransientField( "anewvalue" );
vehicle.setRoute( route );
Route mergedRoute = (Route) s.merge( route );
assertThat( s.merge( route ), instanceOf( Route.class ) );
s.getTransaction().commit();
s.close();