HHH-12675 - Respect inverse property for JoinedSubclassEntityPersister
This commit is contained in:
parent
4bafeeecae
commit
b25bfd79f4
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue