HHH-13725 - Implement ManyToOne with Join Table associations support
This commit is contained in:
parent
36bf9f9dfe
commit
ed49f6abcf
|
@ -25,6 +25,7 @@ import org.hibernate.sql.ast.spi.SqlSelection;
|
|||
import org.hibernate.sql.ast.tree.expression.ColumnReference;
|
||||
import org.hibernate.sql.ast.tree.from.TableGroup;
|
||||
import org.hibernate.sql.ast.tree.from.TableReference;
|
||||
import org.hibernate.sql.ast.tree.from.TableReferenceJoin;
|
||||
import org.hibernate.sql.ast.tree.predicate.ComparisonPredicate;
|
||||
import org.hibernate.sql.ast.tree.predicate.Predicate;
|
||||
import org.hibernate.sql.results.internal.domain.basic.BasicResult;
|
||||
|
@ -63,14 +64,16 @@ public class SimpleForeignKeyDescriptor implements ForeignKeyDescriptor {
|
|||
DomainResultCreationState creationState) {
|
||||
final SqlAstCreationState sqlAstCreationState = creationState.getSqlAstCreationState();
|
||||
final SqlExpressionResolver sqlExpressionResolver = sqlAstCreationState.getSqlExpressionResolver();
|
||||
final TableReference keyTableKeyReference = getKeyTableReference( tableGroup, tableGroup );
|
||||
|
||||
final SqlSelection sqlSelection = sqlExpressionResolver.resolveSqlSelection(
|
||||
sqlExpressionResolver.resolveSqlExpression(
|
||||
SqlExpressionResolver.createColumnReferenceKey(
|
||||
tableGroup.getPrimaryTableReference(),
|
||||
keyTableKeyReference,
|
||||
keyColumnExpression
|
||||
),
|
||||
s -> new ColumnReference(
|
||||
tableGroup.getPrimaryTableReference().getIdentificationVariable(),
|
||||
keyTableKeyReference,
|
||||
keyColumnExpression,
|
||||
jdbcMapping,
|
||||
creationState.getSqlAstCreationState().getCreationContext().getSessionFactory()
|
||||
|
@ -95,25 +98,27 @@ public class SimpleForeignKeyDescriptor implements ForeignKeyDescriptor {
|
|||
JoinType joinType,
|
||||
SqlExpressionResolver sqlExpressionResolver,
|
||||
SqlAstCreationContext creationContext) {
|
||||
final TableReference tableReference = lhs.resolveTableReference( targetColumnContainingTable );
|
||||
final TableReference targetTableReference = lhs.resolveTableReference( targetColumnContainingTable );
|
||||
|
||||
final ColumnReference targetReference = (ColumnReference) sqlExpressionResolver.resolveSqlExpression(
|
||||
SqlExpressionResolver.createColumnReferenceKey( tableReference, keyColumnExpression ),
|
||||
SqlExpressionResolver.createColumnReferenceKey( targetTableReference, keyColumnExpression ),
|
||||
s -> new ColumnReference(
|
||||
tableReference.getIdentificationVariable(),
|
||||
targetTableReference.getIdentificationVariable(),
|
||||
targetColumnExpression,
|
||||
jdbcMapping,
|
||||
creationContext.getSessionFactory()
|
||||
)
|
||||
);
|
||||
|
||||
final TableReference keyTableKeyReference = getKeyTableReference( lhs, tableGroup );
|
||||
|
||||
final ColumnReference keyReference = (ColumnReference) sqlExpressionResolver.resolveSqlExpression(
|
||||
SqlExpressionResolver.createColumnReferenceKey(
|
||||
tableGroup.getPrimaryTableReference(),
|
||||
keyTableKeyReference,
|
||||
keyColumnExpression
|
||||
),
|
||||
s -> new ColumnReference(
|
||||
tableGroup.getPrimaryTableReference().getIdentificationVariable(),
|
||||
keyTableKeyReference.getIdentificationVariable(),
|
||||
keyColumnExpression,
|
||||
jdbcMapping,
|
||||
creationContext.getSessionFactory()
|
||||
|
@ -127,6 +132,15 @@ public class SimpleForeignKeyDescriptor implements ForeignKeyDescriptor {
|
|||
);
|
||||
}
|
||||
|
||||
protected TableReference getKeyTableReference(TableGroup lhs, TableGroup tableGroup) {
|
||||
for ( TableReferenceJoin tableJoin : lhs.getTableReferenceJoins() ) {
|
||||
if ( tableJoin.getJoinedTableReference().getTableExpression().equals( keyColumnContainingTable ) ) {
|
||||
return tableJoin.getJoinedTableReference();
|
||||
}
|
||||
}
|
||||
return tableGroup.getPrimaryTableReference();
|
||||
}
|
||||
|
||||
@Override
|
||||
public JavaTypeDescriptor getJavaTypeDescriptor() {
|
||||
return jdbcMapping.getJavaTypeDescriptor();
|
||||
|
@ -144,7 +158,13 @@ public class SimpleForeignKeyDescriptor implements ForeignKeyDescriptor {
|
|||
|
||||
@Override
|
||||
public void visitColumnMappings(FkColumnMappingConsumer consumer) {
|
||||
consumer.consume( keyColumnContainingTable, keyColumnExpression, targetColumnContainingTable, targetColumnExpression, jdbcMapping );
|
||||
consumer.consume(
|
||||
keyColumnContainingTable,
|
||||
keyColumnExpression,
|
||||
targetColumnContainingTable,
|
||||
targetColumnExpression,
|
||||
jdbcMapping
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -10,10 +10,10 @@ import java.util.Calendar;
|
|||
|
||||
import org.hibernate.stat.spi.StatisticsImplementor;
|
||||
|
||||
import org.hibernate.testing.orm.domain.gambit.BasicEntity;
|
||||
import org.hibernate.testing.orm.domain.gambit.EntityWithManyToOneJoinTable;
|
||||
import org.hibernate.testing.orm.domain.gambit.SimpleEntity;
|
||||
import org.hibernate.testing.orm.junit.DomainModel;
|
||||
import org.hibernate.testing.orm.junit.FailureExpected;
|
||||
import org.hibernate.testing.orm.junit.ServiceRegistry;
|
||||
import org.hibernate.testing.orm.junit.SessionFactory;
|
||||
import org.hibernate.testing.orm.junit.SessionFactoryScope;
|
||||
|
@ -32,7 +32,8 @@ import static org.hamcrest.MatcherAssert.assertThat;
|
|||
@DomainModel(
|
||||
annotatedClasses = {
|
||||
EntityWithManyToOneJoinTable.class,
|
||||
SimpleEntity.class
|
||||
SimpleEntity.class,
|
||||
BasicEntity.class
|
||||
}
|
||||
)
|
||||
@ServiceRegistry
|
||||
|
@ -75,7 +76,6 @@ public class EntityWithManyToOneJoinTableTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
@FailureExpected
|
||||
public void testSaveInDifferentTransactions(SessionFactoryScope scope) {
|
||||
EntityWithManyToOneJoinTable entity = new EntityWithManyToOneJoinTable( 3, "second", Integer.MAX_VALUE );
|
||||
|
||||
|
@ -83,7 +83,7 @@ public class EntityWithManyToOneJoinTableTest {
|
|||
4,
|
||||
Calendar.getInstance().getTime(),
|
||||
Calendar.getInstance().toInstant(),
|
||||
Integer.MAX_VALUE -1 ,
|
||||
Integer.MAX_VALUE - 1,
|
||||
Long.MAX_VALUE,
|
||||
null
|
||||
);
|
||||
|
@ -110,22 +110,25 @@ public class EntityWithManyToOneJoinTableTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
@FailureExpected
|
||||
public void testHqlSelect(SessionFactoryScope scope) {
|
||||
final StatisticsImplementor statistics = scope.getSessionFactory().getStatistics();
|
||||
statistics.clear();
|
||||
scope.inTransaction(
|
||||
session -> {
|
||||
final EntityWithManyToOneJoinTable result = session.createQuery(
|
||||
"select e from EntityWithManyToOneJoinTable e where e.id = 2",
|
||||
"select e from EntityWithManyToOneJoinTable e where e.id = 1",
|
||||
EntityWithManyToOneJoinTable.class
|
||||
).uniqueResult();
|
||||
|
||||
assertThat( result, notNullValue() );
|
||||
assertThat( result.getId(), is( 2 ) );
|
||||
assertThat( result.getId(), is( 1 ) );
|
||||
assertThat( result.getName(), is( "first" ) );
|
||||
|
||||
assertThat( statistics.getPrepareStatementCount(), is( 1L ) );
|
||||
assertThat( statistics.getPrepareStatementCount(), is( 2L ) );
|
||||
|
||||
assertThat( result.getOther().getSomeInteger(), is( Integer.MAX_VALUE ) );
|
||||
|
||||
assertThat( statistics.getPrepareStatementCount(), is( 2L ) );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
@ -140,6 +143,7 @@ public class EntityWithManyToOneJoinTableTest {
|
|||
"select e.name from EntityWithManyToOneJoinTable e where e.other.id = 2",
|
||||
String.class
|
||||
).uniqueResult();
|
||||
|
||||
assertThat( value, equalTo( "first" ) );
|
||||
|
||||
assertThat( statistics.getPrepareStatementCount(), is( 1L ) );
|
||||
|
@ -148,29 +152,63 @@ public class EntityWithManyToOneJoinTableTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
@FailureExpected
|
||||
public void testHqlSelectWithJoin(SessionFactoryScope scope) {
|
||||
final StatisticsImplementor statistics = scope.getSessionFactory().getStatistics();
|
||||
statistics.clear();
|
||||
scope.inTransaction(
|
||||
session -> {
|
||||
final String value = session.createQuery(
|
||||
"select from EntityWithManyToOneJoinTable e where e.id = 2",
|
||||
String.class
|
||||
final EntityWithManyToOneJoinTable result = session.createQuery(
|
||||
"select e from EntityWithManyToOneJoinTable e join e.other where e.id = 1",
|
||||
EntityWithManyToOneJoinTable.class
|
||||
).uniqueResult();
|
||||
assertThat( value, equalTo( "first" ) );
|
||||
assertThat( result, notNullValue() );
|
||||
assertThat( result.getId(), is( 1 ) );
|
||||
assertThat( result.getName(), is( "first" ) );
|
||||
|
||||
assertThat( statistics.getPrepareStatementCount(), is( 2L ) );
|
||||
|
||||
assertThat( result.getOther().getId(), is( 2 ) );
|
||||
assertThat( result.getOther().getSomeInteger(), is( Integer.MAX_VALUE ) );
|
||||
|
||||
assertThat( statistics.getPrepareStatementCount(), is( 2L ) );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHqlSelectWithJoinFetch(SessionFactoryScope scope) {
|
||||
final StatisticsImplementor statistics = scope.getSessionFactory().getStatistics();
|
||||
statistics.clear();
|
||||
scope.inTransaction(
|
||||
session -> {
|
||||
final EntityWithManyToOneJoinTable result = session.createQuery(
|
||||
"select e from EntityWithManyToOneJoinTable e join fetch e.other where e.id = 1",
|
||||
EntityWithManyToOneJoinTable.class
|
||||
).uniqueResult();
|
||||
|
||||
assertThat( result, notNullValue() );
|
||||
assertThat( result.getId(), is( 1 ) );
|
||||
assertThat( result.getName(), is( "first" ) );
|
||||
|
||||
assertThat( statistics.getPrepareStatementCount(), is( 1L ) );
|
||||
|
||||
assertThat( result.getOther().getId(), is( 2 ) );
|
||||
assertThat( result.getOther().getSomeInteger(), is( Integer.MAX_VALUE ) );
|
||||
|
||||
assertThat( statistics.getPrepareStatementCount(), is( 1L ) );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
@FailureExpected
|
||||
public void testUpdate(SessionFactoryScope scope) {
|
||||
EntityWithManyToOneJoinTable entity = new EntityWithManyToOneJoinTable( 1, "first", Integer.MAX_VALUE );
|
||||
EntityWithManyToOneJoinTable entity = new EntityWithManyToOneJoinTable( 2, "second", Integer.MAX_VALUE );
|
||||
|
||||
SimpleEntity other = new SimpleEntity(
|
||||
2,
|
||||
4,
|
||||
Calendar.getInstance().getTime(),
|
||||
null,
|
||||
Integer.MAX_VALUE,
|
||||
100,
|
||||
Long.MAX_VALUE,
|
||||
null
|
||||
);
|
||||
|
@ -183,17 +221,17 @@ public class EntityWithManyToOneJoinTableTest {
|
|||
} );
|
||||
|
||||
SimpleEntity anOther = new SimpleEntity(
|
||||
3,
|
||||
5,
|
||||
Calendar.getInstance().getTime(),
|
||||
null,
|
||||
Integer.MIN_VALUE,
|
||||
Integer.MIN_VALUE + 5,
|
||||
Long.MIN_VALUE,
|
||||
null
|
||||
);
|
||||
|
||||
scope.inTransaction(
|
||||
session -> {
|
||||
final EntityWithManyToOneJoinTable loaded = session.get( EntityWithManyToOneJoinTable.class, 1 );
|
||||
final EntityWithManyToOneJoinTable loaded = session.get( EntityWithManyToOneJoinTable.class, 2 );
|
||||
assert loaded != null;
|
||||
session.save( anOther );
|
||||
loaded.setOther( anOther );
|
||||
|
@ -202,10 +240,10 @@ public class EntityWithManyToOneJoinTableTest {
|
|||
|
||||
scope.inTransaction(
|
||||
session -> {
|
||||
final EntityWithManyToOneJoinTable loaded = session.get( EntityWithManyToOneJoinTable.class, 1 );
|
||||
final EntityWithManyToOneJoinTable loaded = session.get( EntityWithManyToOneJoinTable.class, 2 );
|
||||
|
||||
assertThat( loaded.getOther(), notNullValue() );
|
||||
assertThat( loaded.getOther().getId(), equalTo( 3 ) );
|
||||
assertThat( loaded.getOther().getId(), equalTo( 5 ) );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
package org.hibernate.testing.orm.domain.gambit;
|
||||
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.FetchType;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.JoinTable;
|
||||
import javax.persistence.ManyToOne;
|
||||
|
@ -22,6 +23,7 @@ public class EntityWithManyToOneJoinTable {
|
|||
private String name;
|
||||
private SimpleEntity other;
|
||||
private Integer someInteger;
|
||||
private BasicEntity lazyOther;
|
||||
|
||||
public EntityWithManyToOneJoinTable() {
|
||||
}
|
||||
|
@ -59,6 +61,16 @@ public class EntityWithManyToOneJoinTable {
|
|||
this.other = other;
|
||||
}
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinTable(name = "ENTITY_ANOTHER")
|
||||
public BasicEntity getLazyOther() {
|
||||
return lazyOther;
|
||||
}
|
||||
|
||||
public void setLazyOther(BasicEntity lazyOther) {
|
||||
this.lazyOther = lazyOther;
|
||||
}
|
||||
|
||||
public Integer getSomeInteger() {
|
||||
return someInteger;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue