HHH-12675 - Respect inverse property for JoinedSubclassEntityPersister

This commit is contained in:
Jan-Willem Gmelig Meyling 2018-06-10 15:26:50 +02:00 committed by Christian Beikov
parent 4bafeeecae
commit b25bfd79f4
4 changed files with 126 additions and 26 deletions

View File

@ -101,6 +101,8 @@ public class JoinedSubclassEntityPersister extends AbstractEntityPersister {
private final boolean[] subclassTableSequentialSelect;
private final boolean[] subclassTableIsLazyClosure;
private final boolean[] isInverseSubclassTable;
private final boolean[] isNullableSubclassTable;
// subclass discrimination works by assigning particular
// values to certain combinations of null primary key
@ -123,6 +125,7 @@ public class JoinedSubclassEntityPersister extends AbstractEntityPersister {
private final int coreTableSpan;
// only contains values for SecondaryTables, ie. not tables part of the "coreTableSpan"
private final boolean[] isNullableTable;
private final boolean[] isInverseTable;
//INITIALIZATION:
@ -235,20 +238,21 @@ public class JoinedSubclassEntityPersister extends AbstractEntityPersister {
//Span of the tableNames directly mapped by this entity and super-classes, if any
coreTableSpan = tableNames.size();
tableSpan = persistentClass.getJoinClosureSpan() + coreTableSpan;
isNullableTable = new boolean[persistentClass.getJoinClosureSpan()];
isNullableTable = new boolean[tableSpan];
isInverseTable = new boolean[tableSpan];
int tableIndex = 0;
Iterator joinItr = persistentClass.getJoinClosureIterator();
while ( joinItr.hasNext() ) {
for ( int tableIndex = 0; joinItr.hasNext(); tableIndex++ ) {
Join join = (Join) joinItr.next();
isNullableTable[tableIndex++] = join.isOptional() ||
isNullableTable[tableIndex] = join.isOptional() ||
creationContext.getSessionFactory()
.getSessionFactoryOptions()
.getJpaCompliance()
.isJpaCacheComplianceEnabled();
isInverseTable[tableIndex] = join.isInverse();
Table table = join.getTable();
final String tableName = determineTableName( table, jdbcEnvironment );
@ -285,6 +289,8 @@ public class JoinedSubclassEntityPersister extends AbstractEntityPersister {
ArrayList<Boolean> isConcretes = new ArrayList<Boolean>();
ArrayList<Boolean> isDeferreds = new ArrayList<Boolean>();
ArrayList<Boolean> isLazies = new ArrayList<Boolean>();
ArrayList<Boolean> isInverses = new ArrayList<Boolean>();
ArrayList<Boolean> isNullables = new ArrayList<Boolean>();
keyColumns = new ArrayList<String[]>();
tItr = persistentClass.getSubclassTableClosureIterator();
@ -293,6 +299,8 @@ public class JoinedSubclassEntityPersister extends AbstractEntityPersister {
isConcretes.add( persistentClass.isClassOrSuperclassTable( tab ) );
isDeferreds.add( Boolean.FALSE );
isLazies.add( Boolean.FALSE );
isInverses.add( Boolean.FALSE );
isNullables.add( Boolean.FALSE );
final String tableName = determineTableName( tab, jdbcEnvironment );
subclassTableNames.add( tableName );
String[] key = new String[idColumnSpan];
@ -311,6 +319,13 @@ public class JoinedSubclassEntityPersister extends AbstractEntityPersister {
isConcretes.add( persistentClass.isClassOrSuperclassTable( joinTable ) );
isDeferreds.add( join.isSequentialSelect() );
isInverses.add( join.isInverse() );
isNullables.add(
join.isOptional() || creationContext.getSessionFactory()
.getSessionFactoryOptions()
.getJpaCompliance()
.isJpaCacheComplianceEnabled()
);
isLazies.add( join.isLazy() );
String joinTableName = determineTableName( joinTable, jdbcEnvironment );
@ -328,6 +343,8 @@ public class JoinedSubclassEntityPersister extends AbstractEntityPersister {
isClassOrSuperclassTable = ArrayHelper.toBooleanArray( isConcretes );
subclassTableSequentialSelect = ArrayHelper.toBooleanArray( isDeferreds );
subclassTableIsLazyClosure = ArrayHelper.toBooleanArray( isLazies );
isInverseSubclassTable = ArrayHelper.toBooleanArray( isInverses );
isNullableSubclassTable = ArrayHelper.toBooleanArray( isNullables );
constraintOrderedTableNames = new String[naturalOrderSubclassTableNameClosure.length];
constraintOrderedKeyColumnNames = new String[naturalOrderSubclassTableNameClosure.length][];
@ -347,7 +364,6 @@ public class JoinedSubclassEntityPersister extends AbstractEntityPersister {
* tableNames -> CLIENT, PERSON
*/
tableSpan = naturalOrderTableNames.length;
this.tableNames = reverse( naturalOrderTableNames, coreTableSpan );
tableKeyColumns = reverse( naturalOrderTableKeyColumns, coreTableSpan );
tableKeyColumnReaders = reverse( naturalOrderTableKeyColumnReaders, coreTableSpan );
@ -374,6 +390,8 @@ public class JoinedSubclassEntityPersister extends AbstractEntityPersister {
PersistentClass pc = persistentClass;
int jk = coreTableSpan - 1;
while ( pc != null ) {
isNullableTable[jk] = false;
isInverseTable[jk] = false;
customSQLInsert[jk] = pc.getCustomSQLInsert();
insertCallable[jk] = customSQLInsert[jk] != null && pc.isCustomInsertCallable();
insertResultCheckStyles[jk] = pc.getCustomSQLInsertCheckStyle() == null
@ -404,6 +422,13 @@ public class JoinedSubclassEntityPersister extends AbstractEntityPersister {
while ( joinItr.hasNext() ) {
Join join = (Join) joinItr.next();
isInverseTable[j] = join.isInverse();
isNullableTable[j] = join.isOptional()
|| creationContext.getSessionFactory()
.getSessionFactoryOptions()
.getJpaCompliance()
.isJpaCacheComplianceEnabled();
customSQLInsert[j] = join.getCustomSQLInsert();
insertCallable[j] = customSQLInsert[j] != null && join.isCustomInsertCallable();
insertResultCheckStyles[j] = join.getCustomSQLInsertCheckStyle() == null
@ -723,11 +748,14 @@ public class JoinedSubclassEntityPersister extends AbstractEntityPersister {
}
}
@Override
protected boolean isNullableTable(int j) {
if ( j < coreTableSpan ) {
return false;
return isNullableTable[j];
}
return isNullableTable[j - coreTableSpan];
@Override
protected boolean isInverseTable(int j) {
return isInverseTable[j];
}
protected boolean isSubclassTableSequentialSelect(int j) {
@ -744,6 +772,16 @@ public class JoinedSubclassEntityPersister extends AbstractEntityPersister {
return subclassTableNameClosure[subclassPropertyTableNumberClosure[i]];
}
@Override
protected boolean isInverseSubclassTable(int j) {
return isInverseSubclassTable[j];
}
@Override
protected boolean isNullableSubclassTable(int j) {
return isNullableSubclassTable[j];
}
public Type getDiscriminatorType() {
return discriminatorType;
}

View File

@ -258,13 +258,10 @@ public class SingleTableEntityPersister extends AbstractEntityPersister {
if ( join.isSequentialSelect() && !persistentClass.isClassOrSuperclassJoin( join ) ) {
hasDeferred = true;
}
subclassTables.add(
join.getTable().getQualifiedName(
factory.getDialect(),
factory.getSettings().getDefaultCatalogName(),
factory.getSettings().getDefaultSchemaName()
)
);
String joinTableName = determineTableName( join.getTable(), jdbcEnvironment );
subclassTables.add( joinTableName );
Iterator iter = join.getKey().getColumnIterator();
String[] keyCols = new String[join.getKey().getColumnSpan()];
int i = 0;

View File

@ -14,14 +14,17 @@ import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
/**
* @author Emmanuel Bernard
*/
public class JoinedSubclassAndSecondaryTable extends BaseCoreFunctionalTestCase {
@Test
public void testSecondaryTableAndJoined() throws Exception {
public void testSecondaryTableAndJoined() {
Session s = openSession();
Transaction tx = s.beginTransaction();
SwimmingPool sp = new SwimmingPool();
@ -29,7 +32,7 @@ public class JoinedSubclassAndSecondaryTable extends BaseCoreFunctionalTestCase
s.flush();
s.clear();
long rowCount = getTableRowCount( s );
long rowCount = getTableRowCount( s, "POOL_ADDRESS" );
assertEquals(
"The address table is marked as optional. For null values no database row should be created",
0,
@ -37,7 +40,7 @@ public class JoinedSubclassAndSecondaryTable extends BaseCoreFunctionalTestCase
);
SwimmingPool sp2 = (SwimmingPool) s.get( SwimmingPool.class, sp.getId() );
assertEquals( sp.getAddress(), null );
assertNull( sp.getAddress() );
PoolAddress address = new PoolAddress();
address.setAddress( "Park Avenue" );
@ -47,24 +50,63 @@ public class JoinedSubclassAndSecondaryTable extends BaseCoreFunctionalTestCase
s.clear();
sp2 = (SwimmingPool) s.get( SwimmingPool.class, sp.getId() );
rowCount = getTableRowCount( s );
rowCount = getTableRowCount( s, "POOL_ADDRESS" );
assertEquals(
"Now we should have a row in the pool address table ",
1,
rowCount
);
assertFalse( sp2.getAddress() == null );
assertNotNull( sp2.getAddress() );
assertEquals( sp2.getAddress().getAddress(), "Park Avenue" );
tx.rollback();
s.close();
}
private long getTableRowCount(Session s) {
@Test
public void testSecondaryTableAndJoinedInverse() throws Exception {
Session s = openSession();
Transaction tx = s.beginTransaction();
SwimmingPool sp = new SwimmingPool();
s.persist( sp );
s.flush();
s.clear();
long rowCount = getTableRowCount( s, "POOL_ADDRESS_2" );
assertEquals(
"The address table is marked as optional. For null values no database row should be created",
0,
rowCount
);
SwimmingPool sp2 = (SwimmingPool) s.get( SwimmingPool.class, sp.getId() );
assertNull( sp.getSecondaryAddress() );
PoolAddress address = new PoolAddress();
address.setAddress( "Park Avenue" );
sp2.setSecondaryAddress( address );
s.flush();
s.clear();
sp2 = (SwimmingPool) s.get( SwimmingPool.class, sp.getId() );
rowCount = getTableRowCount( s, "POOL_ADDRESS_2" );
assertEquals(
"Now we should have a row in the pool address table ",
0,
rowCount
);
assertNull( sp2.getSecondaryAddress() );
tx.rollback();
s.close();
}
private long getTableRowCount(Session s, String tableName) {
// the type returned for count(*) in a native query depends on the dialect
// Oracle returns Types.NUMERIC, which is mapped to BigDecimal;
// H2 returns Types.BIGINT, which is mapped to BigInteger;
Object retVal = s.createSQLQuery( "select count(*) from POOL_ADDRESS" ).uniqueResult();
Object retVal = s.createSQLQuery( "select count(*) from " + tableName ).uniqueResult();
assertTrue( Number.class.isInstance( retVal ) );
return ( ( Number ) retVal ).longValue();
}

View File

@ -7,6 +7,10 @@
//$Id$
package org.hibernate.test.annotations.inheritance.joined;
import org.hibernate.annotations.Tables;
import javax.persistence.AttributeOverride;
import javax.persistence.Column;
import javax.persistence.Embedded;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
@ -14,14 +18,21 @@ import javax.persistence.Id;
import javax.persistence.Inheritance;
import javax.persistence.InheritanceType;
import javax.persistence.SecondaryTable;
import javax.persistence.SecondaryTables;
/**
* @author Emmanuel Bernard
*/
@Entity
@Inheritance(strategy = InheritanceType.JOINED)
@SecondaryTable(name="POOL_ADDRESS")
@org.hibernate.annotations.Table(appliesTo="POOL_ADDRESS", optional=true)
@SecondaryTables({
@SecondaryTable(name="POOL_ADDRESS"),
@SecondaryTable(name="POOL_ADDRESS_2")
})
@Tables({
@org.hibernate.annotations.Table(appliesTo="POOL_ADDRESS", optional=true),
@org.hibernate.annotations.Table(appliesTo="POOL_ADDRESS_2", optional=true, inverse = true)
})
public class Pool {
@Id @GeneratedValue
private Integer id;
@ -29,6 +40,10 @@ public class Pool {
@Embedded
private PoolAddress address;
@Embedded
@AttributeOverride(name = "address", column = @Column(table = "POOL_ADDRESS_2"))
private PoolAddress secondaryAddress;
public PoolAddress getAddress() {
return address;
}
@ -37,6 +52,14 @@ public class Pool {
this.address = address;
}
public PoolAddress getSecondaryAddress() {
return secondaryAddress;
}
public void setSecondaryAddress(PoolAddress secondaryAddress) {
this.secondaryAddress = secondaryAddress;
}
public Integer getId() {
return id;
}