HHH-7380 - union subclass support
This commit is contained in:
parent
1e1d373c08
commit
9a462a7b31
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 );
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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." );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 );
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -35,7 +35,7 @@ import static org.junit.Assert.assertTrue;
|
|||
/**
|
||||
* @author Gavin King
|
||||
*/
|
||||
@FailureExpectedWithNewMetamodel
|
||||
//@FailureExpectedWithNewMetamodel
|
||||
public class IJ2Test extends LegacyTestCase {
|
||||
|
||||
@Override
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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"/>
|
||||
|
|
|
@ -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>
|
||||
|
|
Loading…
Reference in New Issue