HHH-7380 - union subclass support

This commit is contained in:
Strong Liu 2012-12-14 22:58:37 +08:00
parent 1e1d373c08
commit 9a462a7b31
16 changed files with 169 additions and 51 deletions

View File

@ -34,5 +34,15 @@ package org.hibernate;
public enum TruthValue {
TRUE,
FALSE,
UNKNOWN
UNKNOWN;
public static boolean toBoolean(TruthValue value, boolean defaultValue) {
if ( value == TruthValue.TRUE ) {
return true;
}
if ( value == TruthValue.FALSE ) {
return false;
}
return defaultValue;
}
}

View File

@ -244,16 +244,6 @@ public class Binder {
return unsavedValue;
}
private static boolean toBoolean( final TruthValue truthValue, final boolean truthValueDefault ) {
if ( truthValue == TruthValue.TRUE ) {
return true;
}
if ( truthValue == TruthValue.FALSE ) {
return false;
}
return truthValueDefault;
}
private final MetadataImplementor metadata;
private final IdentifierGeneratorFactory identifierGeneratorFactory;
private final ObjectNameNormalizer nameNormalizer;
@ -2030,8 +2020,10 @@ public class Binder {
// table per class and non-abstract entity
else {
Table includedTable = null;
if(superEntityBinding!=null && inheritanceType == InheritanceType.TABLE_PER_CLASS && Table.class.isInstance( superEntityBinding.getPrimaryTable() )){
includedTable = Table.class.cast( superEntityBinding.getPrimaryTable());
if ( superEntityBinding != null
&& inheritanceType == InheritanceType.TABLE_PER_CLASS
&& Table.class.isInstance( superEntityBinding.getPrimaryTable() ) ) {
includedTable = Table.class.cast( superEntityBinding.getPrimaryTable() );
}
table = createTable(
entitySource.getPrimaryTable(), new DefaultNamingStrategy() {
@ -2289,11 +2281,11 @@ public class Binder {
if ( valueSource.getNature() == RelationalValueSource.Nature.COLUMN ) {
final ColumnSource columnSource = ( ColumnSource ) valueSource;
final boolean isIncludedInInsert =
toBoolean(
TruthValue.toBoolean(
columnSource.isIncludedInInsert(),
valueSourceContainer.areValuesIncludedInInsertByDefault() );
final boolean isIncludedInUpdate =
toBoolean(
TruthValue.toBoolean(
columnSource.isIncludedInUpdate(),
valueSourceContainer.areValuesIncludedInUpdateByDefault() );
Column column = createColumn(
@ -2452,7 +2444,7 @@ public class Binder {
else {
// if the column is already non-nullable, leave it alone
if ( column.isNullable() ) {
column.setNullable( toBoolean( columnSource.isNullable(), isNullableByDefault ) );
column.setNullable( TruthValue.toBoolean( columnSource.isNullable(), isNullableByDefault ) );
}
}
column.setDefaultValue( columnSource.getDefaultValue() );
@ -2598,8 +2590,9 @@ public class Binder {
final LocalBindingContext bindingContext = bindingContexts.peek();
final MappingDefaults mappingDefaults = bindingContext.getMappingDefaults();
final String explicitCatalogName = tableSpecSource == null ? null : tableSpecSource.getExplicitCatalogName();
final String explicitSchemaName = tableSpecSource == null ? null : tableSpecSource.getExplicitSchemaName();
final boolean isTableSourceNull = tableSpecSource == null;
final String explicitCatalogName = isTableSourceNull ? null : tableSpecSource.getExplicitCatalogName();
final String explicitSchemaName = isTableSourceNull ? null : tableSpecSource.getExplicitSchemaName();
final Schema.Name schemaName =
new Schema.Name(
createIdentifier( explicitCatalogName, mappingDefaults.getCatalogName() ),
@ -2608,7 +2601,7 @@ public class Binder {
final Schema schema = metadata.getDatabase().locateSchema( schemaName );
TableSpecification tableSpec = null;
if ( tableSpecSource == null ) {
if ( isTableSourceNull ) {
if ( defaultNamingStrategy == null ) {
throw bindingContext().makeMappingException( "An explicit name must be specified for the table" );
}
@ -2723,7 +2716,7 @@ public class Binder {
return values;
}
final AttributeBinding referencedAttributeBinding =
entityBinding.locateAttributeBinding( attributeName );
entityBinding.locateAttributeBinding( attributeName, true );
if ( referencedAttributeBinding == null ) {
throw bindingContext().makeMappingException(
String.format(
@ -2831,9 +2824,12 @@ public class Binder {
}
final String explicitName = resolutionDelegate.getReferencedAttributeName();
final AttributeBinding referencedAttributeBinding = explicitName != null
? referencedEntityBinding.locateAttributeBinding( explicitName )
:referencedEntityBinding.locateAttributeBinding( resolutionDelegate.getJoinColumns( resolutionContext ) );
final AttributeBinding referencedAttributeBinding = locateAttributeBinding(
resolutionDelegate,
resolutionContext,
referencedEntityBinding,
explicitName
);
if ( !referencedAttributeBinding.getAttribute().isSingular() ) {
throw bindingContext().makeMappingException(
String.format(
@ -2843,6 +2839,26 @@ public class Binder {
return (SingularAttributeBinding) referencedAttributeBinding;
}
private AttributeBinding locateAttributeBinding(JoinColumnResolutionDelegate resolutionDelegate,
JoinColumnResolutionContext resolutionContext,
EntityBinding referencedEntityBinding,
String explicitName) {
AttributeBinding attributeBinding = explicitName != null
? referencedEntityBinding.locateAttributeBinding( explicitName )
: referencedEntityBinding.locateAttributeBinding( resolutionDelegate.getJoinColumns( resolutionContext ) );
if ( attributeBinding == null && referencedEntityBinding.getSuperEntityBinding() != null ) {
return locateAttributeBinding(
resolutionDelegate,
resolutionContext,
referencedEntityBinding.getSuperEntityBinding(),
explicitName
);
} else {
return attributeBinding;
}
}
private EntityBinding findOrBindEntityBinding(ValueHolder< Class< ? >> entityJavaTypeValue, String explicitEntityName) {
final String referencedEntityName =
bindingContext().qualifyClassName( explicitEntityName != null

View File

@ -707,7 +707,7 @@ public class MetadataImpl implements MetadataImplementor, Serializable {
throw new MappingException( "Entity binding not known: " + entityName );
}
// TODO: should this call EntityBinding.getReferencedAttributeBindingString), which does not exist yet?
AttributeBinding attributeBinding = entityBinding.locateAttributeBinding( propertyName );
AttributeBinding attributeBinding = entityBinding.locateAttributeBinding( propertyName, true );
if ( attributeBinding == null ) {
throw new MappingException( "unknown property: " + entityName + '.' + propertyName );
}

View File

@ -187,10 +187,16 @@ public abstract class AbstractToOneAttributeSourceImpl extends AbstractHbmSource
public JoinColumnResolutionDelegate getForeignKeyTargetColumnResolutionDelegate() {
return propertyRef == null
? null
: new JoinColumnResolutionDelegateImpl();
: new JoinColumnResolutionDelegateImpl( propertyRef );
}
public static class JoinColumnResolutionDelegateImpl implements JoinColumnResolutionDelegate {
private final String propertyRef;
public JoinColumnResolutionDelegateImpl(String propertyRef) {
this.propertyRef = propertyRef;
}
public class JoinColumnResolutionDelegateImpl implements JoinColumnResolutionDelegate {
@Override
public String getReferencedAttributeName() {
return propertyRef;

View File

@ -264,6 +264,17 @@ public class EntityBinding extends AbstractAttributeBindingContainer {
return secondaryTable.getSecondaryTableReference();
}
public AttributeBinding locateAttributeBinding(String name, boolean searchParent) {
AttributeBinding attributeBinding = locateAttributeBinding( name );
if ( attributeBinding == null && searchParent && getSuperEntityBinding() != null ) {
return getSuperEntityBinding().locateAttributeBinding( name, searchParent );
}
else {
return attributeBinding;
}
}
public String getPrimaryTableName() {
return primaryTableName;
}

View File

@ -173,15 +173,19 @@ public abstract class AbstractTableSpecification implements TableSpecification {
public PrimaryKey getPrimaryKey() {
return primaryKey;
}
@Override
public int generateColumnListId(Iterable<Column> columns) {
int result = getLogicalName().hashCode();
for ( Column column : columns ) {
if ( !this.equals( column.getTable() ) ) {
throw new IllegalArgumentException( "All columns must be from this table." );
}
sameTableCheck( column );
result = 31 * result + column.getColumnName().hashCode();
}
return result;
}
protected void sameTableCheck(Column column) {
if ( !this.equals( column.getTable() ) ) {
throw new IllegalArgumentException( "All columns must be from this table." );
}
}
}

View File

@ -1,10 +1,35 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2012, 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.spi.relational;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.hibernate.internal.util.collections.JoinedIterable;
@ -46,15 +71,44 @@ public class DenormalizedTable extends Table {
DerivedValue value = includedTable.locateDerivedValue( fragment );
return value != null ? value : super.locateDerivedValue( fragment );
}
//todo other constraints other than fk
//we have to copy all FKs defined in the parent table to this sub table, can this be doing only once? like using ValueHolder?
@Override
public Iterable<ForeignKey> getForeignKeys() {
return new JoinedIterable<ForeignKey>(
Arrays.asList(
includedTable.getForeignKeys(),
super.getForeignKeys()
)
);
copyFKsFromParentTable();
return super.getForeignKeys();
}
private Set<ForeignKey> alreadyCopiedNonNameParentFK = new HashSet<ForeignKey>( );
private void copyFKsFromParentTable() {
Iterable<ForeignKey> fksInSuperTable = includedTable.getForeignKeys();
final String fkNamePostfix = Integer.toHexString( getTableName().hashCode() );
for ( ForeignKey fk : fksInSuperTable ) {
String name = fk.getName();
if ( name == null ) {
if(!alreadyCopiedNonNameParentFK.contains( fk )){
copyFK( fk, name );
alreadyCopiedNonNameParentFK.add( fk );
}
}
else {
String fkName = name + fkNamePostfix;
ForeignKey copiedFK = super.locateForeignKey( fkName );
if ( copiedFK == null ) {
copyFK( fk, fkName );
}
}
}
}
private void copyFK(ForeignKey fk, String fkName) {
ForeignKey copiedFK = createForeignKey( fk.getTargetTable(), fkName );
copiedFK.setDeleteRule( fk.getDeleteRule() );
copiedFK.setUpdateRule( fk.getUpdateRule() );
Iterable<ForeignKey.ColumnMapping> columnMappings = fk.getColumnMappings();
for ( ForeignKey.ColumnMapping cm : columnMappings ) {
copiedFK.addColumnMapping( cm.getSourceColumn(), cm.getTargetColumn() );
}
}
@Override
@ -73,4 +127,13 @@ public class DenormalizedTable extends Table {
public PrimaryKey getPrimaryKey() {
return includedTable.getPrimaryKey();
}
@Override
protected void sameTableCheck(Column column) {
try{
super.sameTableCheck( column );
} catch ( IllegalArgumentException e ){
includedTable.sameTableCheck( column );
}
}
}

View File

@ -67,7 +67,7 @@ public class MetaAttributeContext {
public MetaAttribute getMetaAttribute(String key) {
MetaAttribute value = getLocalMetaAttribute( key );
if ( value == null ) {
if ( value == null && parentContext != null ) {
// recursive call
value = parentContext.getMetaAttribute( key );
}

View File

@ -608,7 +608,7 @@ public class UnionSubclassEntityPersister extends AbstractEntityPersister {
final StringBuilder buf = new StringBuilder()
.append("( ");
visitSubEntityBindings( entityBinding, new Callback() {
visitEntityHierarchy( entityBinding, new Callback() {
@Override
public void execute(EntityBinding eb) {
TableSpecification table = eb.getPrimaryTable();

View File

@ -29,7 +29,6 @@ import org.junit.Test;
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.hibernate.testing.FailureExpectedWithNewMetamodel;
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
import static org.junit.Assert.assertEquals;
@ -42,7 +41,6 @@ import static org.junit.Assert.fail;
/**
* @author Emmanuel Bernard
*/
@FailureExpectedWithNewMetamodel
public class SubclassTest extends BaseCoreFunctionalTestCase {
@Test
public void testDefault() throws Exception {

View File

@ -27,7 +27,6 @@ import org.junit.Test;
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.hibernate.testing.FailureExpectedWithNewMetamodel;
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
import static org.junit.Assert.assertEquals;
@ -35,7 +34,6 @@ import static org.junit.Assert.assertEquals;
/**
* @author Emmanuel Bernard
*/
@FailureExpectedWithNewMetamodel
public class PolymorphismTest extends BaseCoreFunctionalTestCase {
@Test
public void testPolymorphism() throws Exception {

View File

@ -31,6 +31,7 @@ import org.hibernate.internal.StaticFilterAliasGenerator;
import org.hibernate.internal.util.compare.EqualsHelper;
import org.hibernate.mapping.PersistentClass;
import org.hibernate.metadata.ClassMetadata;
import org.hibernate.metamodel.spi.binding.EntityBinding;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.tuple.entity.EntityMetamodel;
import org.hibernate.tuple.entity.EntityTuplizer;
@ -55,6 +56,17 @@ public class CustomPersister implements EntityPersister {
this.factory = factory;
}
public CustomPersister(
final EntityBinding entityBinding,
final EntityRegionAccessStrategy cacheAccessStrategy,
final NaturalIdRegionAccessStrategy naturalIdRegionAccessStrategy,
final SessionFactoryImplementor factory,
final Mapping mapping) throws HibernateException {
this.factory = factory;
}
public boolean hasLazyProperties() {
return false;
}

View File

@ -35,7 +35,7 @@ import static org.junit.Assert.assertTrue;
/**
* @author Gavin King
*/
@FailureExpectedWithNewMetamodel
//@FailureExpectedWithNewMetamodel
public class IJ2Test extends LegacyTestCase {
@Override

View File

@ -46,7 +46,6 @@ import static org.junit.Assert.assertTrue;
* @author Gavin King
*/
@SuppressWarnings( {"UnnecessaryBoxing"})
@FailureExpectedWithNewMetamodel
public class UnionSubclassTest extends BaseCoreFunctionalTestCase {
@Override
protected String[] getMappings() {
@ -54,6 +53,7 @@ public class UnionSubclassTest extends BaseCoreFunctionalTestCase {
}
@Test
@FailureExpectedWithNewMetamodel
public void testUnionSubclass() {
Session s = openSession();
Transaction t = s.beginTransaction();

View File

@ -8,7 +8,7 @@
<class name="I">
<id name="id">
<generator class="hilo"/>
<generator class="seqhilo"/>
</id>
<property name="name" unique="true" not-null="true"/>
<property name="type" column="type_"/>
@ -20,7 +20,7 @@
<class name="K">
<id name="id">
<generator class="hilo"/>
<generator class="seqhilo"/>
</id>
<set name="is" inverse="true">
<key column="parent"/>

View File

@ -5,19 +5,19 @@
<hibernate-mapping package="org.hibernate.test.propertyref.inheritence.union">
<class name="Person" table="U_SBCLS_PROPREF_PERS">
<class name="Person" >
<id name="id">
<generator class="hilo"/>
<generator class="seqhilo"/>
</id>
<property name="name" length="100"/>
<property name="personId" length="8" unique="true"/>
<union-subclass name="Customer" table="U_SBCLS_PROPREF_CUST">
<union-subclass name="Customer" >
<property name="customerId" length="8" unique="true"/>
</union-subclass>
</class>
<class name="Account" table="U_SBCLS_PROPREF_ACCT">
<class name="Account" >
<id name="accountId" length="32">
<generator class="uuid.hex"/>
</id>