HHH-7843 : Add support for one-to-one to new metamodel

This commit is contained in:
Gail Badner 2013-03-01 15:25:24 -08:00
parent a9e116fc6c
commit cabd9ea4dc
17 changed files with 166 additions and 95 deletions

View File

@ -266,7 +266,6 @@ public class Binder {
// Create/Bind root-specific information
bindIdentifier( rootEntityBinding, rootEntitySource.getIdentifierSource() );
bindSecondaryTables( rootEntityBinding, rootEntitySource );
//bindUniqueConstraints( rootEntityBinding, rootEntitySource );
bindVersion( rootEntityBinding, rootEntitySource.getVersioningAttributeSource() );
bindDiscriminator( rootEntityBinding, rootEntitySource );
bindIdentifierGenerator( rootEntityBinding );
@ -279,7 +278,6 @@ public class Binder {
rootEntityBinding.setMutable( rootEntitySource.isMutable() );
rootEntityBinding.setWhereFilter( rootEntitySource.getWhere() );
rootEntityBinding.setRowId( rootEntitySource.getRowId() );
//bindAttributes( rootEntityBinding, rootEntitySource );
}
};
LocalBindingContextExecutor subEntityCallback = new LocalBindingContextExecutor() {
@ -288,14 +286,11 @@ public class Binder {
final EntitySource entitySource = bindingContextContext.getEntitySource();
final EntityBinding entityBinding = bindingContextContext.getEntityBinding();
final EntityBinding superEntityBinding = bindingContextContext.getSuperEntityBinding();
// Create new entity binding
entityBinding.setMutable( entityBinding.getHierarchyDetails().getRootEntityBinding().isMutable() );
markSuperEntityTableAbstractIfNecessary( superEntityBinding );
bindPrimaryTable( entityBinding, entitySource );
bindSubEntityPrimaryKey( entityBinding, entitySource );
bindSecondaryTables( entityBinding, entitySource );
//bindUniqueConstraints( entityBinding, entitySource );
//bindAttributes( entityBinding, entitySource );
}
};
Set<EntityHierarchy> unresolvedEntityHierarchies = new HashSet<EntityHierarchy>( );
@ -337,10 +332,6 @@ public class Binder {
bindPluralAttributes( entityHierarchy, true );
}
//for ( final EntityHierarchy entityHierarchy : entityHierarchiesByRootEntitySource.values() ) {
// completeCompositeAttributes( entityHierarchy );
//}
LocalBindingContextExecutor uniqueKeyExecutor = new LocalBindingContextExecutor() {
@Override
public void execute(LocalBindingContextExecutionContext bindingContextContext) {
@ -350,6 +341,11 @@ public class Binder {
for ( final EntityHierarchy entityHierarchy : entityHierarchiesByRootEntitySource.values() ) {
applyToEntityHierarchy( entityHierarchy, uniqueKeyExecutor, uniqueKeyExecutor );
}
// TODO: check if any many-to-one attribute bindings with logicalOneToOne == false have all columns
// (and no formulas) contained in a defined unique key that only contains these columns.
// if so, mark the many-to-one as a logical one-to-one.
}
private boolean isIdentifierDependentOnOtherEntityHierarchy(EntityHierarchy entityHierarchy) {
@ -412,42 +408,6 @@ public class Binder {
}
}
private void completeCompositeAttributes(
final EntityHierarchy entityHierarchy) {
LocalBindingContextExecutor executor = new LocalBindingContextExecutor() {
@Override
public void execute(LocalBindingContextExecutionContext bindingContextContext) {
completeCompositeAttributes(
bindingContextContext.getEntityBinding(),
bindingContextContext.getEntitySource()
);
}
};
applyToEntityHierarchy( entityHierarchy, executor, executor );
}
private void completeCompositeAttributes(
final AttributeBindingContainer attributeBindingContainer,
final AttributeSourceContainer attributeSourceContainer) {
for ( AttributeSource attributeSource : attributeSourceContainer.attributeSources() ) {
if ( attributeSource.isSingular() ) {
SingularAttributeSource singularAttributeSource = (SingularAttributeSource) attributeSource;
if ( singularAttributeSource.getNature() == SingularAttributeSource.Nature.COMPOSITE ) {
ComponentAttributeSource componentAttributeSource = (ComponentAttributeSource) attributeSource;
CompositeAttributeBinding compositeAttributeBinding =
(CompositeAttributeBinding) attributeBindingContainer.locateAttributeBinding( componentAttributeSource.getName() );
completeCompositeAttributes( compositeAttributeBinding, componentAttributeSource );
typeHelper.bindAggregatedCompositeAttributeType(
false,
(Aggregate) compositeAttributeBinding.getAttribute().getSingularAttributeType(),
null, // TODO: don't have the default value at this point; shouldn't be needed...
compositeAttributeBinding
);
}
}
}
}
private void bindSingularAttributes(
final EntityHierarchy entityHierarchy,
final SingularAttributeSource.Nature nature) {
@ -1215,25 +1175,6 @@ public class Binder {
}
private AttributeBinding attributeBinding(
final String entityName,
final String attributeName) {
/*
// Check if binding has already been created
final EntityBinding entityBinding = findOrBindEntityBinding( entityName );
final AttributeSource attributeSource = attributeSourcesByName.get(
attributeSourcesByNameKey(
entityName,
attributeName
)
);
bindAttribute( entityBinding, attributeSource );
return entityBinding.locateAttributeBinding( attributeName );
*/
return null;
}
private AttributeBinding bindAttribute(
final AttributeBindingContainer attributeBindingContainer,
final AttributeSource attributeSource) {
@ -1484,7 +1425,7 @@ public class Binder {
attributeSource.getFetchTiming() != FetchTiming.IMMEDIATE,
attributeSource.isUnWrapProxy(),
!attributeSource.isNotFoundAnException(),
false //TODO: determine if isLogicalOneToOne
attributeSource.isUnique()
);
}
};
@ -2261,10 +2202,7 @@ public class Binder {
SingularAssociationAttributeBinding referencedAttributeBinding =
(SingularAssociationAttributeBinding) referencedEntityBinding.locateAttributeBindingByPath( mappedBy, true );
if ( referencedAttributeBinding == null ) {
referencedAttributeBinding = (SingularAssociationAttributeBinding) attributeBinding(
referencedEntityBinding.getEntity()
.getName(), mappedBy
);
throw new NotYetImplementedException( "Referenced columns not used by an attribute binding is not supported yet." );
}
keyBinding.setHibernateTypeDescriptor(
referencedAttributeBinding.getReferencedAttributeBinding()
@ -2536,8 +2474,9 @@ public class Binder {
AttributeBinding referencedAttributeBinding;
final String referencedAttributeName = resolutionDelegate.getReferencedAttributeName();
if ( referencedAttributeName == null ) {
referencedAttributeBinding = attributeBindingContainer.locateAttributeBinding(
resolutionDelegate.getJoinColumns( new JoinColumnResolutionContextImpl( entityBinding ) )
referencedAttributeBinding = attributeBindingContainer.seekEntityBinding().locateAttributeBinding(
resolutionDelegate.getJoinColumns( new JoinColumnResolutionContextImpl( entityBinding ) ),
true
);
}
else {
@ -2545,16 +2484,7 @@ public class Binder {
}
if ( referencedAttributeBinding == null ) {
referencedAttributeBinding = attributeBinding(
entityBinding.getEntity().getName(),
referencedAttributeName
);
}
if ( referencedAttributeBinding == null ) {
throw bindingContext().makeMappingException(
"Plural attribute key references an attribute binding that does not exist: "
+ referencedAttributeBinding
);
throw new NotYetImplementedException( "Referenced columns not used by an attribute binding is not supported yet." );
}
if ( !referencedAttributeBinding.getAttribute().isSingular() ) {
throw bindingContext().makeMappingException(

View File

@ -29,6 +29,7 @@ import org.hibernate.AssertionFailure;
import org.hibernate.cfg.NotYetImplementedException;
import org.hibernate.metamodel.internal.source.annotations.attribute.AssociationAttribute;
import org.hibernate.metamodel.internal.source.annotations.attribute.PluralAssociationAttribute;
import org.hibernate.metamodel.internal.source.annotations.attribute.SingularAssociationAttribute;
import org.hibernate.metamodel.internal.source.annotations.entity.ConfiguredClass;
import org.hibernate.metamodel.spi.source.AttributeSource;
import org.hibernate.metamodel.spi.source.PluralAttributeSource;
@ -48,7 +49,7 @@ public class SourceHelper {
switch ( associationAttribute.getNature() ) {
case ONE_TO_ONE:
case MANY_TO_ONE: {
attributeList.add( new ToOneAttributeSourceImpl( associationAttribute ) );
attributeList.add( new ToOneAttributeSourceImpl( (SingularAssociationAttribute) associationAttribute ) );
break;
}
case MANY_TO_MANY:

View File

@ -38,6 +38,7 @@ import org.hibernate.metamodel.internal.Binder;
import org.hibernate.metamodel.internal.source.annotations.attribute.AssociationAttribute;
import org.hibernate.metamodel.internal.source.annotations.attribute.Column;
import org.hibernate.metamodel.internal.source.annotations.attribute.MappedAttribute;
import org.hibernate.metamodel.internal.source.annotations.attribute.SingularAssociationAttribute;
import org.hibernate.metamodel.internal.source.annotations.util.EnumConversionHelper;
import org.hibernate.metamodel.internal.source.annotations.util.JPADotNames;
import org.hibernate.metamodel.internal.source.annotations.util.JandexHelper;
@ -47,6 +48,7 @@ import org.hibernate.metamodel.spi.relational.TableSpecification;
import org.hibernate.metamodel.spi.relational.Value;
import org.hibernate.metamodel.spi.source.ForeignKeyContributingSource;
import org.hibernate.metamodel.spi.source.RelationalValueSource;
import org.hibernate.metamodel.spi.source.SingularAttributeSource;
import org.hibernate.metamodel.spi.source.ToOneAttributeSource;
import org.hibernate.type.ForeignKeyDirection;
@ -56,8 +58,10 @@ import org.hibernate.type.ForeignKeyDirection;
public class ToOneAttributeSourceImpl extends SingularAttributeSourceImpl implements ToOneAttributeSource {
private final AssociationAttribute associationAttribute;
private final Set<CascadeStyle> cascadeStyles;
private SingularAttributeSource.Nature nature;
private SingularAttributeSource ownerAttributeSource;
public ToOneAttributeSourceImpl(AssociationAttribute associationAttribute) {
public ToOneAttributeSourceImpl(SingularAssociationAttribute associationAttribute) {
super( associationAttribute );
this.associationAttribute = associationAttribute;
@ -66,15 +70,26 @@ public class ToOneAttributeSourceImpl extends SingularAttributeSourceImpl implem
associationAttribute.getHibernateCascadeTypes(),
associationAttribute.getContext()
);
this.nature = getNature( associationAttribute );
}
@Override
public Nature getNature() {
private static Nature getNature(SingularAssociationAttribute associationAttribute) {
if ( MappedAttribute.Nature.MANY_TO_ONE.equals( associationAttribute.getNature() ) ) {
return Nature.MANY_TO_ONE;
}
else if ( MappedAttribute.Nature.ONE_TO_ONE.equals( associationAttribute.getNature() ) ) {
throw new NotYetImplementedException( "One-to-one using annotations is not supported yet." );
if ( associationAttribute.getMappedBy() != null || associationAttribute.hasPrimaryKeyJoinColumn()) {
return Nature.ONE_TO_ONE;
}
else if ( associationAttribute.getJoinTableAnnotation() != null ) {
return Nature.MANY_TO_ONE;
}
else {
//if ( associationAttribute.getJoinColumnValues() ) {
throw new NotYetImplementedException( "One-to-one without mappedBy configured using annotations is not supported yet." );
// if ID is not initialized, then this can't be a one-to-one (mapToPk == false)
// if join columns are the entity's ID, then it is a one-to-one (mapToPk == true)
}
}
else {
throw new AssertionError(String.format( "Wrong attribute nature[%s] for toOne attribute: %s",
@ -82,11 +97,40 @@ public class ToOneAttributeSourceImpl extends SingularAttributeSourceImpl implem
}
}
/*
private static Nature determineNature(
ToOneAttributeSource currentAttributeSource
ToOneAttributeSource ownerAttributeSource,
AssociationAttribute associationAttribute) {
switch ( associationAttribute.getNature() ) {
case ONE_TO_ONE:
throw new NotYetImplementedException( );
case MANY_TO_ONE:
return new ManyToAnyPluralAttributeElementSourceImpl( associationAttribute );
// case ONE_TO_MANY:
// return usesJoinTable( ownerAttributeSource ) ?
// new ManyToManyPluralAttributeElementSourceImpl( ownerAttributeSource, associationAttribute, true ) :
// new OneToManyPluralAttributeElementSourceImpl( ownerAttributeSource, associationAttribute );
}
throw new AssertionError( "Unexpected attribute nature for a to-one association attribute:" + associationAttribute.getNature() );
}
*/
@Override
public Nature getNature() {
return nature;
}
@Override
public String getReferencedEntityName() {
return associationAttribute.getReferencedEntityType();
}
@Override
public boolean isUnique() {
return MappedAttribute.Nature.ONE_TO_ONE.equals( associationAttribute.getNature() );
}
@Override
public boolean isNotFoundAnException() {
return !associationAttribute.isIgnoreNotFound();

View File

@ -91,7 +91,7 @@ public class AssociationAttribute extends MappedAttribute {
private AttributeTypeResolver resolver;
public static AssociationAttribute createAssociationAttribute(
static AssociationAttribute createAssociationAttribute(
ClassInfo classInfo,
String name,
Class<?> attributeType,

View File

@ -0,0 +1,82 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2013, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.metamodel.internal.source.annotations.attribute;
import java.util.List;
import java.util.Map;
import org.jboss.jandex.AnnotationInstance;
import org.jboss.jandex.ClassInfo;
import org.jboss.jandex.DotName;
import org.hibernate.metamodel.internal.source.annotations.entity.EntityBindingContext;
import org.hibernate.metamodel.internal.source.annotations.util.JPADotNames;
import org.hibernate.metamodel.internal.source.annotations.util.JandexHelper;
/**
* @author Gail Badner
*/
public class SingularAssociationAttribute extends AssociationAttribute {
private final boolean hasPrimaryKeyJoinColumn;
public static AssociationAttribute createSingularAssociationAttribute(
ClassInfo classInfo,
String name,
Class<?> attributeType,
Nature attributeNature,
String accessType,
Map<DotName, List<AnnotationInstance>> annotations,
EntityBindingContext context) {
return new SingularAssociationAttribute(
classInfo,
name,
attributeType,
attributeType,
attributeNature,
accessType,
annotations,
context
);
}
SingularAssociationAttribute(
ClassInfo classInfo,
String name,
Class<?> attributeType,
Class<?> referencedAttributeType,
Nature attributeNature,
String accessType,
Map<DotName, List<AnnotationInstance>> annotations,
EntityBindingContext context) {
super( classInfo, name, attributeType, referencedAttributeType, attributeNature, accessType, annotations, context );
this.hasPrimaryKeyJoinColumn =
JandexHelper.containsSingleAnnotation( annotations, JPADotNames.PRIMARY_KEY_JOIN_COLUMN ) ||
JandexHelper.containsSingleAnnotation( annotations, JPADotNames.PRIMARY_KEY_JOIN_COLUMNS );
}
public boolean hasPrimaryKeyJoinColumn() {
return hasPrimaryKeyJoinColumn;
}
}

View File

@ -54,6 +54,7 @@ import org.hibernate.metamodel.internal.source.annotations.attribute.AttributeOv
import org.hibernate.metamodel.internal.source.annotations.attribute.BasicAttribute;
import org.hibernate.metamodel.internal.source.annotations.attribute.MappedAttribute;
import org.hibernate.metamodel.internal.source.annotations.attribute.PluralAssociationAttribute;
import org.hibernate.metamodel.internal.source.annotations.attribute.SingularAssociationAttribute;
import org.hibernate.metamodel.internal.source.annotations.util.AnnotationParserHelper;
import org.hibernate.metamodel.internal.source.annotations.util.HibernateDotNames;
import org.hibernate.metamodel.internal.source.annotations.util.JPADotNames;
@ -514,7 +515,7 @@ public class ConfiguredClass {
}
case ONE_TO_ONE:
case MANY_TO_ONE: {
final AssociationAttribute attribute = AssociationAttribute.createAssociationAttribute(
final AssociationAttribute attribute = SingularAssociationAttribute.createSingularAssociationAttribute(
classInfo,
attributeName,
resolvedMember.getType().getErasedType(),

View File

@ -180,6 +180,11 @@ class KeyManyToOneSourceImpl
return keyManyToOneElement.getEntityName();
}
@Override
public boolean isUnique() {
return false;
}
@Override
public ForeignKeyDirection getForeignKeyDirection() {
return ForeignKeyDirection.TO_PARENT;

View File

@ -185,6 +185,11 @@ class ManyToOneAttributeSourceImpl extends AbstractToOneAttributeSourceImpl {
: manyToOneElement.getEntityName();
}
@Override
public boolean isUnique() {
return manyToOneElement.isUnique();
}
@Override
public String getExplicitForeignKeyName() {
return manyToOneElement.getForeignKey();

View File

@ -187,6 +187,11 @@ class OneToOneAttributeSourceImpl extends AbstractToOneAttributeSourceImpl {
: oneToOneElement.getEntityName();
}
@Override
public boolean isUnique() {
return true;
}
@Override
public String getExplicitForeignKeyName() {
return oneToOneElement.getForeignKey();

View File

@ -29,7 +29,6 @@ import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.hibernate.AssertionFailure;
import org.hibernate.EntityMode;

View File

@ -59,6 +59,9 @@ public class ForeignKey extends AbstractConstraint {
protected ForeignKey(TableSpecification sourceTable, TableSpecification targetTable, String name) {
super( sourceTable, name );
if ( targetTable == null ) {
throw new IllegalArgumentException( "targetTable must be non-null." );
}
this.targetTable = targetTable;
}

View File

@ -44,6 +44,7 @@ public interface ToOneAttributeSource
* @return The name of the referenced entity
*/
public String getReferencedEntityName();
public boolean isUnique();
public boolean isNotFoundAnException();
public boolean isUnWrapProxy();
ForeignKeyDirection getForeignKeyDirection();

View File

@ -147,7 +147,7 @@ public class JoinTest extends BaseCoreFunctionalTestCase {
}
@Test
@FailureExpectedWithNewMetamodel
@FailureExpectedWithNewMetamodel( message = "sequential selects not supported yet.")
public void testManyToOne() throws Exception {
Session s = openSession();
Transaction tx = s.beginTransaction();

View File

@ -41,7 +41,6 @@ import static org.junit.Assert.fail;
* @author Emmanuel Bernard
* @author Gail Badner
*/
@FailureExpectedWithNewMetamodel(message = "Needs one to one mapping support. See Binder#bindSingularAttribute")
public class OptionalOneToOneMappedByTest extends BaseCoreFunctionalTestCase {
// @OneToOne(mappedBy="address") with foreign generator

View File

@ -38,7 +38,6 @@ import static org.junit.Assert.assertNull;
/**
* @author Steve Ebersole
*/
@FailureExpectedWithNewMetamodel
public class DeleteOneToOneOrphansTest extends BaseCoreFunctionalTestCase {
@Override
public String[] getMappings() {

View File

@ -38,7 +38,6 @@ import static org.junit.Assert.assertNull;
/**
* @author Steve Ebersole
*/
@FailureExpectedWithNewMetamodel
public class DeleteOneToOneOrphansTest extends BaseCoreFunctionalTestCase {
@Override
public String[] getMappings() {

View File

@ -30,7 +30,6 @@ import static org.junit.Assert.assertNull;
import java.util.List;
import org.hibernate.Session;
import org.hibernate.testing.FailureExpectedWithNewMetamodel;
import org.hibernate.testing.TestForIssue;
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
import org.junit.Test;
@ -64,7 +63,6 @@ public class DeleteOneToOneOrphansTest extends BaseCoreFunctionalTestCase {
}
@Test
@FailureExpectedWithNewMetamodel
public void testOrphanedWhileManaged() {
createData();