HHH-15099 - Improve handling of associations marked with @NotFound
HHH-15106 - fk() SQM function
This commit is contained in:
parent
362b4c0ac7
commit
0af7ed353a
|
@ -28,6 +28,7 @@ import java.util.function.BiConsumer;
|
|||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
import jakarta.persistence.TemporalType;
|
||||
|
||||
import org.hibernate.HibernateException;
|
||||
import org.hibernate.Internal;
|
||||
|
@ -374,8 +375,6 @@ import org.hibernate.usertype.UserVersionType;
|
|||
|
||||
import org.jboss.logging.Logger;
|
||||
|
||||
import jakarta.persistence.TemporalType;
|
||||
|
||||
import static org.hibernate.internal.util.NullnessHelper.coalesceSuppliedValues;
|
||||
import static org.hibernate.query.sqm.BinaryArithmeticOperator.ADD;
|
||||
import static org.hibernate.query.sqm.BinaryArithmeticOperator.MULTIPLY;
|
||||
|
@ -2880,6 +2879,27 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
|
|||
else if ( parentPath instanceof SqmFrom<?, ?> ) {
|
||||
registerTreatUsage( (SqmFrom<?, ?>) parentPath, tableGroup );
|
||||
}
|
||||
|
||||
if ( getCurrentClauseStack().getCurrent() != Clause.SELECT
|
||||
&& parentPath.getParentPath() != null
|
||||
&& tableGroup.getModelPart() instanceof ToOneAttributeMapping ) {
|
||||
// we need to handle the case of an implicit path involving a to-one
|
||||
// association with not-found mapping where that path has been previously
|
||||
// joined using left. typically, this indicates that the to-one is being
|
||||
// fetched - the fetch would use a left-join. however, since the path is
|
||||
// used outside the select-clause also, we need to force the join to be inner
|
||||
final ToOneAttributeMapping toOneMapping = (ToOneAttributeMapping) tableGroup.getModelPart();
|
||||
if ( toOneMapping.hasNotFoundAction() ) {
|
||||
final NavigablePath parentParentPath = parentPath.getParentPath().getNavigablePath();
|
||||
final TableGroup parentParentTableGroup = fromClauseIndex.findTableGroup( parentParentPath );
|
||||
parentParentTableGroup.visitTableGroupJoins( (join) -> {
|
||||
if ( join.getNavigablePath().equals( parentPath.getNavigablePath() ) ) {
|
||||
join.setJoinType( SqlAstJoinType.INNER );
|
||||
}
|
||||
} );
|
||||
}
|
||||
}
|
||||
|
||||
return tableGroup;
|
||||
}
|
||||
|
||||
|
|
|
@ -10,6 +10,7 @@ import org.hibernate.query.spi.NavigablePath;
|
|||
import org.hibernate.query.sqm.sql.internal.DomainResultProducer;
|
||||
import org.hibernate.sql.ast.SqlAstJoinType;
|
||||
import org.hibernate.sql.ast.SqlAstWalker;
|
||||
import org.hibernate.sql.ast.SqlTreeCreationLogger;
|
||||
import org.hibernate.sql.ast.spi.SqlAstTreeHelper;
|
||||
import org.hibernate.sql.ast.tree.SqlAstNode;
|
||||
import org.hibernate.sql.ast.tree.predicate.Predicate;
|
||||
|
@ -21,36 +22,46 @@ import org.hibernate.sql.results.graph.DomainResultCreationState;
|
|||
*/
|
||||
public class TableGroupJoin implements TableJoin, DomainResultProducer {
|
||||
private final NavigablePath navigablePath;
|
||||
private final SqlAstJoinType sqlAstJoinType;
|
||||
private final TableGroup joinedGroup;
|
||||
|
||||
private SqlAstJoinType joinType;
|
||||
private Predicate predicate;
|
||||
|
||||
public TableGroupJoin(
|
||||
NavigablePath navigablePath,
|
||||
SqlAstJoinType sqlAstJoinType,
|
||||
SqlAstJoinType joinType,
|
||||
TableGroup joinedGroup) {
|
||||
this( navigablePath, sqlAstJoinType, joinedGroup, null );
|
||||
this( navigablePath, joinType, joinedGroup, null );
|
||||
}
|
||||
|
||||
public TableGroupJoin(
|
||||
NavigablePath navigablePath,
|
||||
SqlAstJoinType sqlAstJoinType,
|
||||
SqlAstJoinType joinType,
|
||||
TableGroup joinedGroup,
|
||||
Predicate predicate) {
|
||||
assert !joinedGroup.isLateral() || ( sqlAstJoinType == SqlAstJoinType.INNER
|
||||
|| sqlAstJoinType == SqlAstJoinType.LEFT
|
||||
|| sqlAstJoinType == SqlAstJoinType.CROSS )
|
||||
assert !joinedGroup.isLateral() || ( joinType == SqlAstJoinType.INNER
|
||||
|| joinType == SqlAstJoinType.LEFT
|
||||
|| joinType == SqlAstJoinType.CROSS )
|
||||
: "Lateral is only allowed with inner, left or cross joins";
|
||||
this.navigablePath = navigablePath;
|
||||
this.sqlAstJoinType = sqlAstJoinType;
|
||||
this.joinType = joinType;
|
||||
this.joinedGroup = joinedGroup;
|
||||
this.predicate = predicate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SqlAstJoinType getJoinType() {
|
||||
return sqlAstJoinType;
|
||||
return joinType;
|
||||
}
|
||||
|
||||
public void setJoinType(SqlAstJoinType joinType) {
|
||||
SqlTreeCreationLogger.LOGGER.debugf(
|
||||
"Adjusting join-type for TableGroupJoin(%s) : %s -> %s",
|
||||
navigablePath,
|
||||
this.joinType,
|
||||
joinType
|
||||
);
|
||||
this.joinType = joinType;
|
||||
}
|
||||
|
||||
public TableGroup getJoinedGroup() {
|
||||
|
|
|
@ -22,6 +22,7 @@ import org.hibernate.query.sqm.ParsingException;
|
|||
|
||||
import org.hibernate.testing.jdbc.SQLStatementInspector;
|
||||
import org.hibernate.testing.orm.junit.DomainModel;
|
||||
import org.hibernate.testing.orm.junit.FailureExpected;
|
||||
import org.hibernate.testing.orm.junit.JiraKey;
|
||||
import org.hibernate.testing.orm.junit.SessionFactory;
|
||||
import org.hibernate.testing.orm.junit.SessionFactoryScope;
|
||||
|
@ -42,6 +43,12 @@ public class FkRefTests {
|
|||
|
||||
@Test
|
||||
@JiraKey( "HHH-15099" )
|
||||
@JiraKey( "HHH-15106" )
|
||||
@FailureExpected(
|
||||
reason = "Coin is selected and so its currency needs to be fetched. At the " +
|
||||
"moment, that fetch always happens via a join-fetch. Ideally we'd support " +
|
||||
"loading these via subsequent-select also"
|
||||
)
|
||||
public void testSimplePredicateUse(SessionFactoryScope scope) {
|
||||
final SQLStatementInspector statementInspector = scope.getCollectingStatementInspector();
|
||||
statementInspector.clear();
|
||||
|
@ -83,12 +90,13 @@ public class FkRefTests {
|
|||
}
|
||||
|
||||
/**
|
||||
* Baseline test for {@link #testNullnessPredicateUse}. Here we use the
|
||||
* Baseline test for {@link #testNullnessPredicateUse2}. Here we use the
|
||||
* normal "target" reference, which for a not-found mapping should trigger
|
||||
* a join to the association table and use the fk-target column
|
||||
*/
|
||||
@Test
|
||||
@JiraKey( "HHH-15099" )
|
||||
@JiraKey( "HHH-15106" )
|
||||
public void testNullnessPredicateUseBaseline(SessionFactoryScope scope) {
|
||||
final SQLStatementInspector statementInspector = scope.getCollectingStatementInspector();
|
||||
statementInspector.clear();
|
||||
|
@ -103,17 +111,61 @@ public class FkRefTests {
|
|||
assertThat( statementInspector.getSqlQueries() ).hasSize( 1 );
|
||||
assertThat( statementInspector.getSqlQueries().get( 0 ) ).contains( " join " );
|
||||
assertThat( statementInspector.getSqlQueries().get( 0 ) ).doesNotContain( " left " );
|
||||
assertThat( statementInspector.getSqlQueries().get( 0 ) ).doesNotContain( " inner " );
|
||||
assertThat( statementInspector.getSqlQueries().get( 0 ) ).doesNotContain( " cross " );
|
||||
} );
|
||||
}
|
||||
|
||||
@Test
|
||||
@JiraKey( "HHH-15099" )
|
||||
@JiraKey( "HHH-15106" )
|
||||
public void testNullnessPredicateUse1(SessionFactoryScope scope) {
|
||||
final SQLStatementInspector statementInspector = scope.getCollectingStatementInspector();
|
||||
statementInspector.clear();
|
||||
|
||||
// there is one Coin (id=3) which has a null currency_fk
|
||||
scope.inTransaction( (session) -> {
|
||||
final String hql = "select c.id from Coin c where fk(c.currency) is null";
|
||||
final List<Integer> coinIds = session.createQuery( hql, Integer.class ).getResultList();
|
||||
assertThat( coinIds ).hasSize( 1 );
|
||||
assertThat( coinIds.get( 0 ) ).isNotNull();
|
||||
assertThat( coinIds.get( 0 ) ).isEqualTo( 3 );
|
||||
|
||||
assertThat( statementInspector.getSqlQueries() ).hasSize( 1 );
|
||||
assertThat( statementInspector.getSqlQueries().get( 0 ) ).doesNotContain( " join " );
|
||||
} );
|
||||
|
||||
statementInspector.clear();
|
||||
|
||||
// check using `currency` as a naked "property-ref"
|
||||
scope.inTransaction( (session) -> {
|
||||
final String hql = "select c.id from Coin c where fk(currency) is null";
|
||||
final List<Integer> coinIds = session.createQuery( hql, Integer.class ).getResultList();
|
||||
assertThat( coinIds ).hasSize( 1 );
|
||||
assertThat( coinIds.get( 0 ) ).isNotNull();
|
||||
|
||||
assertThat( statementInspector.getSqlQueries() ).hasSize( 1 );
|
||||
assertThat( statementInspector.getSqlQueries().get( 0 ) ).doesNotContain( " join " );
|
||||
} );
|
||||
}
|
||||
|
||||
/**
|
||||
* It is not ideal that we render the join for these. Need to come back and address that
|
||||
* It is not ideal that we render the join for these.
|
||||
*
|
||||
* Ideally we'd perform a subsequent-select, not sure if that is feasible as it requires
|
||||
* understanding the overall query structure.
|
||||
*
|
||||
* Compare with {@link #testNullnessPredicateUse1}. There, because we perform a scalar select,
|
||||
* the currency does not need to be fetched. So it works there
|
||||
*/
|
||||
@Test
|
||||
@JiraKey( "HHH-15099" )
|
||||
public void testNullnessPredicateUse(SessionFactoryScope scope) {
|
||||
@JiraKey( "HHH-15106" )
|
||||
@FailureExpected(
|
||||
reason = "Coin is selected and so its currency needs to be fetched. At the " +
|
||||
"moment, that fetch always happens via a join-fetch. Ideally we'd support " +
|
||||
"loading these via subsequent-select also"
|
||||
)
|
||||
public void testNullnessPredicateUse2(SessionFactoryScope scope) {
|
||||
final SQLStatementInspector statementInspector = scope.getCollectingStatementInspector();
|
||||
statementInspector.clear();
|
||||
|
||||
|
@ -124,7 +176,6 @@ public class FkRefTests {
|
|||
assertThat( coins ).hasSize( 1 );
|
||||
assertThat( coins.get( 0 ) ).isNotNull();
|
||||
assertThat( coins.get( 0 ).getId() ).isEqualTo( 3 );
|
||||
assertThat( coins.get( 0 ).getCurrency() ).isNull();
|
||||
|
||||
assertThat( statementInspector.getSqlQueries() ).hasSize( 1 );
|
||||
assertThat( statementInspector.getSqlQueries().get( 0 ) ).doesNotContain( " join " );
|
||||
|
@ -139,7 +190,6 @@ public class FkRefTests {
|
|||
assertThat( coins ).hasSize( 1 );
|
||||
assertThat( coins.get( 0 ) ).isNotNull();
|
||||
assertThat( coins.get( 0 ).getId() ).isEqualTo( 3 );
|
||||
assertThat( coins.get( 0 ).getCurrency() ).isNull();
|
||||
|
||||
assertThat( statementInspector.getSqlQueries() ).hasSize( 1 );
|
||||
assertThat( statementInspector.getSqlQueries().get( 0 ) ).doesNotContain( " join " );
|
||||
|
@ -148,6 +198,7 @@ public class FkRefTests {
|
|||
|
||||
@Test
|
||||
@JiraKey( "HHH-15099" )
|
||||
@JiraKey( "HHH-15106" )
|
||||
public void testFkRefDereferenceNotAllowed(SessionFactoryScope scope) {
|
||||
final SQLStatementInspector statementInspector = scope.getCollectingStatementInspector();
|
||||
statementInspector.clear();
|
||||
|
@ -190,9 +241,9 @@ public class FkRefTests {
|
|||
session.persist( noCurrency );
|
||||
} );
|
||||
|
||||
// scope.inTransaction( (session) -> {
|
||||
// session.createQuery( "delete Currency where id = 1" ).executeUpdate();
|
||||
// } );
|
||||
scope.inTransaction( (session) -> {
|
||||
session.createQuery( "delete Currency where id = 1" ).executeUpdate();
|
||||
} );
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
|
|
|
@ -68,12 +68,12 @@ public class NotFoundExceptionLogicalOneToOneTest {
|
|||
@Test
|
||||
@JiraKey( "HHH-15060" )
|
||||
public void testProxyCoin(SessionFactoryScope scope) {
|
||||
// test handling of a proxy for the missing Coin
|
||||
// test handling of a proxy for the Coin with the missing Currency
|
||||
scope.inTransaction( (session) -> {
|
||||
final Coin proxy = session.byId( Coin.class ).getReference( 1 );
|
||||
try {
|
||||
Hibernate.initialize( proxy );
|
||||
Assertions.fail( "Expecting ObjectNotFoundException" );
|
||||
Assertions.fail( "Expecting FetchNotFoundException" );
|
||||
}
|
||||
catch (FetchNotFoundException expected) {
|
||||
assertThat( expected.getEntityName() ).endsWith( "Currency" );
|
||||
|
@ -132,7 +132,7 @@ public class NotFoundExceptionLogicalOneToOneTest {
|
|||
assertThat( statementInspector.getSqlQueries().get( 0 ) ).contains( " Coin " );
|
||||
assertThat( statementInspector.getSqlQueries().get( 0 ) ).contains( " Currency " );
|
||||
assertThat( statementInspector.getSqlQueries().get( 0 ) ).contains( " join " );
|
||||
assertThat( statementInspector.getSqlQueries().get( 0 ) ).doesNotContain( " inner " );
|
||||
assertThat( statementInspector.getSqlQueries().get( 0 ) ).doesNotContain( " left " );
|
||||
assertThat( statementInspector.getSqlQueries().get( 0 ) ).doesNotContain( " cross " );
|
||||
}
|
||||
|
||||
|
@ -148,15 +148,15 @@ public class NotFoundExceptionLogicalOneToOneTest {
|
|||
statementInspector.clear();
|
||||
|
||||
scope.inTransaction( (session) -> {
|
||||
final String hql = "select c from Coin c where c.currency.id = 2";
|
||||
final List<Coin> coins = session.createQuery( hql, Coin.class ).getResultList();
|
||||
assertThat( coins ).hasSize( 1 );
|
||||
final String hql = "select c.id from Coin c where c.currency.id = 2";
|
||||
final List<Integer> coinIds = session.createQuery( hql, Integer.class ).getResultList();
|
||||
assertThat( coinIds ).hasSize( 1 );
|
||||
|
||||
assertThat( statementInspector.getSqlQueries() ).hasSize( 1 );
|
||||
assertThat( statementInspector.getSqlQueries().get( 0 ) ).contains( " Coin " );
|
||||
assertThat( statementInspector.getSqlQueries().get( 0 ) ).contains( " Currency " );
|
||||
assertThat( statementInspector.getSqlQueries().get( 0 ) ).contains( " join " );
|
||||
assertThat( statementInspector.getSqlQueries().get( 0 ) ).doesNotContain( " inner " );
|
||||
assertThat( statementInspector.getSqlQueries().get( 0 ) ).doesNotContain( " left " );
|
||||
assertThat( statementInspector.getSqlQueries().get( 0 ) ).doesNotContain( " cross " );
|
||||
} );
|
||||
}
|
||||
|
@ -172,29 +172,29 @@ public class NotFoundExceptionLogicalOneToOneTest {
|
|||
statementInspector.clear();
|
||||
|
||||
scope.inTransaction( (session) -> {
|
||||
// NOTE : this query is conceptually the same as the one from
|
||||
// `#testQueryImplicitPathDereferencePredicateBaseline` in that we want
|
||||
// a join and we want to use the fk target column (here, `Currency.id`)
|
||||
// rather than the normal perf-opt strategy of using the fk key column
|
||||
// (here, `Coin.currency_fk`).
|
||||
final String hql = "select c from Coin c join fetch c.currency c2 where c2.name = 'USD'";
|
||||
final List<Coin> coins = session.createQuery( hql, Coin.class ).getResultList();
|
||||
assertThat( coins ).hasSize( 1 );
|
||||
assertThat( statementInspector.getSqlQueries() ).hasSize( 1 );
|
||||
assertThat( statementInspector.getSqlQueries().get( 0 ) ).contains( " Coin " );
|
||||
assertThat( statementInspector.getSqlQueries().get( 0 ) ).contains( " Currency " );
|
||||
assertThat( statementInspector.getSqlQueries().get( 0 ) ).contains( " join " );
|
||||
assertThat( statementInspector.getSqlQueries().get( 0 ) ).doesNotContain( " left " );
|
||||
assertThat( statementInspector.getSqlQueries().get( 0 ) ).doesNotContain( " cross " );
|
||||
} );
|
||||
|
||||
statementInspector.clear();
|
||||
|
||||
scope.inTransaction( (session) -> {
|
||||
// NOTE : this query is conceptually the same as the one from
|
||||
// `#testQueryImplicitPathDereferencePredicateBaseline` in that we want
|
||||
// a join and we want to use the fk target column (here, `Currency.id`)
|
||||
// rather than the normal perf-opt strategy of using the fk key column
|
||||
// (here, `Coin.currency_fk`).
|
||||
final String hql = "select c from Coin c join fetch c.currency c2 where c2.name = 'Euro'";
|
||||
final String hql = "select c from Coin c join fetch c.currency c2 where c2.id = 1";
|
||||
final List<Coin> coins = session.createQuery( hql, Coin.class ).getResultList();
|
||||
assertThat( coins ).hasSize( 0 );
|
||||
assertThat( statementInspector.getSqlQueries() ).hasSize( 1 );
|
||||
assertThat( statementInspector.getSqlQueries().get( 0 ) ).contains( " Coin " );
|
||||
assertThat( statementInspector.getSqlQueries().get( 0 ) ).contains( " Currency " );
|
||||
assertThat( statementInspector.getSqlQueries().get( 0 ) ).contains( " join " );
|
||||
assertThat( statementInspector.getSqlQueries().get( 0 ) ).doesNotContain( " left " );
|
||||
assertThat( statementInspector.getSqlQueries().get( 0 ) ).doesNotContain( " cross " );
|
||||
} );
|
||||
}
|
||||
|
||||
|
@ -213,7 +213,7 @@ public class NotFoundExceptionLogicalOneToOneTest {
|
|||
assertThat( statementInspector.getSqlQueries().get( 0 ) ).contains( " Coin " );
|
||||
assertThat( statementInspector.getSqlQueries().get( 0 ) ).contains( " Currency " );
|
||||
assertThat( statementInspector.getSqlQueries().get( 0 ) ).contains( " join " );
|
||||
assertThat( statementInspector.getSqlQueries().get( 0 ) ).doesNotContain( " inner " );
|
||||
assertThat( statementInspector.getSqlQueries().get( 0 ) ).doesNotContain( " left " );
|
||||
assertThat( statementInspector.getSqlQueries().get( 0 ) ).doesNotContain( " cross " );
|
||||
} );
|
||||
|
||||
|
@ -228,7 +228,8 @@ public class NotFoundExceptionLogicalOneToOneTest {
|
|||
assertThat( statementInspector.getSqlQueries().get( 0 ) ).contains( " Coin " );
|
||||
assertThat( statementInspector.getSqlQueries().get( 0 ) ).contains( " Currency " );
|
||||
assertThat( statementInspector.getSqlQueries().get( 0 ) ).contains( " join " );
|
||||
assertThat( statementInspector.getSqlQueries().get( 0 ) ).doesNotContain( " inner " );
|
||||
assertThat( statementInspector.getSqlQueries().get( 0 ) ).doesNotContain( " left " );
|
||||
assertThat( statementInspector.getSqlQueries().get( 0 ) ).doesNotContain( " cross " );
|
||||
} );
|
||||
}
|
||||
|
||||
|
|
|
@ -97,6 +97,7 @@ public class NotFoundExceptionManyToOneTest {
|
|||
assertThat( statementInspector.getSqlQueries().get( 0 ) ).contains( " Coin " );
|
||||
assertThat( statementInspector.getSqlQueries().get( 0 ) ).contains( " Currency " );
|
||||
assertThat( statementInspector.getSqlQueries().get( 0 ) ).contains( " join " );
|
||||
assertThat( statementInspector.getSqlQueries().get( 0 ) ).contains( " left " );
|
||||
assertThat( statementInspector.getSqlQueries().get( 0 ) ).doesNotContain( " inner " );
|
||||
|
||||
assertThat( expected.getEntityName() ).isEqualTo( Currency.class.getName() );
|
||||
|
@ -108,7 +109,7 @@ public class NotFoundExceptionManyToOneTest {
|
|||
/**
|
||||
* Baseline for {@link #testQueryImplicitPathDereferencePredicate}. Ultimately, we want
|
||||
* SQL generated there to behave exactly the same as this query - specifically forcing the
|
||||
* join
|
||||
* join. Because the
|
||||
*/
|
||||
@Test
|
||||
@JiraKey( "HHH-15060" )
|
||||
|
@ -122,11 +123,15 @@ public class NotFoundExceptionManyToOneTest {
|
|||
assertThat( coins ).isEmpty();
|
||||
} );
|
||||
|
||||
// the problem, as with the rest of the failures here, is that the fetch
|
||||
// causes a left join to be used. The where-clause path is either
|
||||
// 1) not processed first
|
||||
// 2) not processed properly
|
||||
assertThat( statementInspector.getSqlQueries() ).hasSize( 1 );
|
||||
assertThat( statementInspector.getSqlQueries().get( 0 ) ).contains( " Coin " );
|
||||
assertThat( statementInspector.getSqlQueries().get( 0 ) ).contains( " Currency " );
|
||||
assertThat( statementInspector.getSqlQueries().get( 0 ) ).contains( " join " );
|
||||
assertThat( statementInspector.getSqlQueries().get( 0 ) ).doesNotContain( " inner " );
|
||||
assertThat( statementInspector.getSqlQueries().get( 0 ) ).doesNotContain( " left " );
|
||||
assertThat( statementInspector.getSqlQueries().get( 0 ) ).doesNotContain( " cross " );
|
||||
}
|
||||
|
||||
|
@ -142,15 +147,18 @@ public class NotFoundExceptionManyToOneTest {
|
|||
statementInspector.clear();
|
||||
|
||||
scope.inTransaction( (session) -> {
|
||||
final String hql = "select c from Coin c where c.currency.id = 2";
|
||||
final List<Coin> coins = session.createQuery( hql, Coin.class ).getResultList();
|
||||
assertThat( coins ).hasSize( 1 );
|
||||
final String hql = "select c.id from Coin c where c.currency.id = 2";
|
||||
final List<Integer> coinIds = session.createQuery( hql, Integer.class ).getResultList();
|
||||
assertThat( coinIds ).hasSize( 1 );
|
||||
|
||||
// this form works because we do not fetch the currency since
|
||||
// we select just the Coin id
|
||||
|
||||
assertThat( statementInspector.getSqlQueries() ).hasSize( 1 );
|
||||
assertThat( statementInspector.getSqlQueries().get( 0 ) ).contains( " Coin " );
|
||||
assertThat( statementInspector.getSqlQueries().get( 0 ) ).contains( " Currency " );
|
||||
assertThat( statementInspector.getSqlQueries().get( 0 ) ).contains( " join " );
|
||||
assertThat( statementInspector.getSqlQueries().get( 0 ) ).doesNotContain( " inner " );
|
||||
assertThat( statementInspector.getSqlQueries().get( 0 ) ).doesNotContain( " left " );
|
||||
assertThat( statementInspector.getSqlQueries().get( 0 ) ).doesNotContain( " cross " );
|
||||
} );
|
||||
}
|
||||
|
@ -166,25 +174,20 @@ public class NotFoundExceptionManyToOneTest {
|
|||
statementInspector.clear();
|
||||
|
||||
scope.inTransaction( (session) -> {
|
||||
// NOTE : this query is conceptually the same as the one from
|
||||
// `#testQueryImplicitPathDereferencePredicateBaseline` in that we want
|
||||
// a join and we want to use the fk target column (here, `Currency.id`)
|
||||
// rather than the normal perf-opt strategy of using the fk key column
|
||||
// (here, `Coin.currency_fk`).
|
||||
final String hql = "select c from Coin c join fetch c.currency c2 where c2.name = 'USD'";
|
||||
final List<Coin> coins = session.createQuery( hql, Coin.class ).getResultList();
|
||||
assertThat( coins ).hasSize( 1 );
|
||||
assertThat( statementInspector.getSqlQueries() ).hasSize( 1 );
|
||||
assertThat( statementInspector.getSqlQueries().get( 0 ) ).contains( " Coin " );
|
||||
assertThat( statementInspector.getSqlQueries().get( 0 ) ).contains( " Currency " );
|
||||
assertThat( statementInspector.getSqlQueries().get( 0 ) ).contains( " join " );
|
||||
assertThat( statementInspector.getSqlQueries().get( 0 ) ).doesNotContain( " left " );
|
||||
assertThat( statementInspector.getSqlQueries().get( 0 ) ).doesNotContain( " cross " );
|
||||
} );
|
||||
|
||||
statementInspector.clear();
|
||||
|
||||
scope.inTransaction( (session) -> {
|
||||
// NOTE : this query is conceptually the same as the one from
|
||||
// `#testQueryImplicitPathDereferencePredicateBaseline` in that we want
|
||||
// a join and we want to use the fk target column (here, `Currency.id`)
|
||||
// rather than the normal perf-opt strategy of using the fk key column
|
||||
// (here, `Coin.currency_fk`).
|
||||
final String hql = "select c from Coin c join fetch c.currency c2 where c2.name = 'Euro'";
|
||||
final List<Coin> coins = session.createQuery( hql, Coin.class ).getResultList();
|
||||
assertThat( coins ).hasSize( 0 );
|
||||
|
@ -198,6 +201,11 @@ public class NotFoundExceptionManyToOneTest {
|
|||
final SQLStatementInspector statementInspector = scope.getCollectingStatementInspector();
|
||||
statementInspector.clear();
|
||||
|
||||
// the problem, as with the rest of the failures here, is that the fetch
|
||||
// causes a left join to be used. The where-clause path is either
|
||||
// 1) not processed first
|
||||
// 2) not processed properly
|
||||
|
||||
scope.inTransaction( (session) -> {
|
||||
final String hql = "select c from Coin c where c.currency.id = 1";
|
||||
final List<Coin> coins = session.createQuery( hql, Coin.class ).getResultList();
|
||||
|
@ -207,7 +215,8 @@ public class NotFoundExceptionManyToOneTest {
|
|||
assertThat( statementInspector.getSqlQueries().get( 0 ) ).contains( " Coin " );
|
||||
assertThat( statementInspector.getSqlQueries().get( 0 ) ).contains( " Currency " );
|
||||
assertThat( statementInspector.getSqlQueries().get( 0 ) ).contains( " join " );
|
||||
assertThat( statementInspector.getSqlQueries().get( 0 ) ).doesNotContain( " inner " );
|
||||
assertThat( statementInspector.getSqlQueries().get( 0 ) ).doesNotContain( " left " );
|
||||
assertThat( statementInspector.getSqlQueries().get( 0 ) ).doesNotContain( " cross " );
|
||||
} );
|
||||
}
|
||||
|
||||
|
|
|
@ -12,7 +12,6 @@ import jakarta.persistence.Entity;
|
|||
import jakarta.persistence.FetchType;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.ManyToOne;
|
||||
import jakarta.persistence.Tuple;
|
||||
|
||||
import org.hibernate.Hibernate;
|
||||
import org.hibernate.ObjectNotFoundException;
|
||||
|
@ -21,7 +20,6 @@ import org.hibernate.annotations.NotFoundAction;
|
|||
|
||||
import org.hibernate.testing.jdbc.SQLStatementInspector;
|
||||
import org.hibernate.testing.orm.junit.DomainModel;
|
||||
import org.hibernate.testing.orm.junit.FailureExpected;
|
||||
import org.hibernate.testing.orm.junit.JiraKey;
|
||||
import org.hibernate.testing.orm.junit.SessionFactory;
|
||||
import org.hibernate.testing.orm.junit.SessionFactoryScope;
|
||||
|
@ -49,10 +47,22 @@ public class NotFoundIgnoreManyToOneTest {
|
|||
|
||||
@Test
|
||||
@JiraKey( "HHH-15060" )
|
||||
public void testProxy(SessionFactoryScope scope) {
|
||||
public void testProxyCoin(SessionFactoryScope scope) {
|
||||
scope.inTransaction( (session) -> {
|
||||
// the non-existent Child
|
||||
// - this is the one valid deviation from treating the broken fk as null
|
||||
// Coin#1 has the broken fk
|
||||
final Coin proxy = session.byId( Coin.class ).getReference( 1 );
|
||||
assertThat( proxy ).isNotNull();
|
||||
Hibernate.initialize( proxy );
|
||||
assertThat( Hibernate.isInitialized( proxy ) ).isTrue();
|
||||
assertThat( proxy.getCurrency() ).isNull();
|
||||
} );
|
||||
}
|
||||
|
||||
@Test
|
||||
@JiraKey( "HHH-15060" )
|
||||
public void testProxyCurrency(SessionFactoryScope scope) {
|
||||
scope.inTransaction( (session) -> {
|
||||
// Currency#1 does not exist
|
||||
final Currency proxy = session.byId( Currency.class ).getReference( 1 );
|
||||
try {
|
||||
Hibernate.initialize( proxy );
|
||||
|
@ -71,6 +81,19 @@ public class NotFoundIgnoreManyToOneTest {
|
|||
final SQLStatementInspector statementInspector = scope.getCollectingStatementInspector();
|
||||
statementInspector.clear();
|
||||
|
||||
scope.inTransaction( (session) -> {
|
||||
session.get( Coin.class, 2 );
|
||||
|
||||
assertThat( statementInspector.getSqlQueries() ).hasSize( 1 );
|
||||
assertThat( statementInspector.getSqlQueries().get( 0 ) ).contains( " Coin " );
|
||||
assertThat( statementInspector.getSqlQueries().get( 0 ) ).contains( " Currency " );
|
||||
assertThat( statementInspector.getSqlQueries().get( 0 ) ).contains( " join " );
|
||||
assertThat( statementInspector.getSqlQueries().get( 0 ) ).doesNotContain( " inner " );
|
||||
assertThat( statementInspector.getSqlQueries().get( 0 ) ).doesNotContain( " cross " );
|
||||
} );
|
||||
|
||||
statementInspector.clear();
|
||||
|
||||
scope.inTransaction( (session) -> {
|
||||
final Coin coin = session.get( Coin.class, 1 );
|
||||
assertThat( coin.getCurrency() ).isNull();
|
||||
|
@ -82,6 +105,80 @@ public class NotFoundIgnoreManyToOneTest {
|
|||
} );
|
||||
}
|
||||
|
||||
@Test
|
||||
@JiraKey( "HHH-15060" )
|
||||
public void testQueryImplicitPathDereferencePredicateBaseline(SessionFactoryScope scope) {
|
||||
final SQLStatementInspector statementInspector = scope.getCollectingStatementInspector();
|
||||
statementInspector.clear();
|
||||
|
||||
scope.inTransaction( (session) -> {
|
||||
final String hql = "select c from Coin c where c.currency.name = 'Euro'";
|
||||
final List<Coin> coins = session.createSelectionQuery( hql, Coin.class ).getResultList();
|
||||
assertThat( coins ).hasSize( 0 );
|
||||
|
||||
assertThat( statementInspector.getSqlQueries() ).hasSize( 1 );
|
||||
assertThat( statementInspector.getSqlQueries().get( 0 ) ).contains( " join " );
|
||||
assertThat( statementInspector.getSqlQueries().get( 0 ) ).doesNotContain( " left " );
|
||||
assertThat( statementInspector.getSqlQueries().get( 0 ) ).doesNotContain( " cross " );
|
||||
} );
|
||||
}
|
||||
|
||||
@Test
|
||||
@JiraKey( "HHH-15060" )
|
||||
public void testQueryImplicitPathDereferencePredicateBaseline2(SessionFactoryScope scope) {
|
||||
final SQLStatementInspector statementInspector = scope.getCollectingStatementInspector();
|
||||
statementInspector.clear();
|
||||
|
||||
scope.inTransaction( (session) -> {
|
||||
final String hql = "select c.id from Coin c where c.currency.id = 1";
|
||||
final List<Integer> coins = session.createSelectionQuery( hql, Integer.class ).getResultList();
|
||||
assertThat( coins ).isEmpty();
|
||||
|
||||
// technically we could use a subsequent-select rather than a join...
|
||||
assertThat( statementInspector.getSqlQueries() ).hasSize( 1 );
|
||||
assertThat( statementInspector.getSqlQueries().get( 0 ) ).contains( " join " );
|
||||
assertThat( statementInspector.getSqlQueries().get( 0 ) ).doesNotContain( " left " );
|
||||
assertThat( statementInspector.getSqlQueries().get( 0 ) ).doesNotContain( " cross " );
|
||||
} );
|
||||
}
|
||||
|
||||
/**
|
||||
* Baseline for {@link #testQueryImplicitPathDereferencePredicate}. Ultimately, we want
|
||||
* SQL generated there to behave exactly the same as this query
|
||||
*/
|
||||
@Test
|
||||
@JiraKey( "HHH-15060" )
|
||||
public void testQueryImplicitPathDereferencePredicateBaseline3(SessionFactoryScope scope) {
|
||||
final SQLStatementInspector statementInspector = scope.getCollectingStatementInspector();
|
||||
statementInspector.clear();
|
||||
|
||||
scope.inTransaction( (session) -> {
|
||||
final String hql = "select c from Coin c join fetch c.currency c2 where c2.name = 'USD'";
|
||||
final List<Coin> coins = session.createQuery( hql, Coin.class ).getResultList();
|
||||
assertThat( coins ).hasSize( 1 );
|
||||
assertThat( statementInspector.getSqlQueries() ).hasSize( 1 );
|
||||
assertThat( statementInspector.getSqlQueries().get( 0 ) ).contains( " Coin " );
|
||||
assertThat( statementInspector.getSqlQueries().get( 0 ) ).contains( " Currency " );
|
||||
assertThat( statementInspector.getSqlQueries().get( 0 ) ).contains( " join " );
|
||||
assertThat( statementInspector.getSqlQueries().get( 0 ) ).doesNotContain( " left " );
|
||||
assertThat( statementInspector.getSqlQueries().get( 0 ) ).doesNotContain( " cross " );
|
||||
} );
|
||||
|
||||
statementInspector.clear();
|
||||
|
||||
scope.inTransaction( (session) -> {
|
||||
final String hql = "select c from Coin c join fetch c.currency c2 where c2.id = 1";
|
||||
final List<Coin> coins = session.createQuery( hql, Coin.class ).getResultList();
|
||||
assertThat( coins ).hasSize( 0 );
|
||||
assertThat( statementInspector.getSqlQueries() ).hasSize( 1 );
|
||||
assertThat( statementInspector.getSqlQueries().get( 0 ) ).contains( " Coin " );
|
||||
assertThat( statementInspector.getSqlQueries().get( 0 ) ).contains( " Currency " );
|
||||
assertThat( statementInspector.getSqlQueries().get( 0 ) ).contains( " join " );
|
||||
assertThat( statementInspector.getSqlQueries().get( 0 ) ).doesNotContain( " left " );
|
||||
assertThat( statementInspector.getSqlQueries().get( 0 ) ).doesNotContain( " cross " );
|
||||
} );
|
||||
}
|
||||
|
||||
@Test
|
||||
@JiraKey( "HHH-15060" )
|
||||
public void testQueryImplicitPathDereferencePredicate(SessionFactoryScope scope) {
|
||||
|
@ -93,58 +190,41 @@ public class NotFoundIgnoreManyToOneTest {
|
|||
final List<Coin> coins = session.createSelectionQuery( hql, Coin.class ).getResultList();
|
||||
assertThat( coins ).isEmpty();
|
||||
|
||||
// technically we could use a subsequent-select rather than a join...
|
||||
assertThat( statementInspector.getSqlQueries() ).hasSize( 1 );
|
||||
assertThat( statementInspector.getSqlQueries().get( 0 ) ).contains( " join " );
|
||||
assertThat( statementInspector.getSqlQueries().get( 0 ) ).doesNotContain( " inner " );
|
||||
assertThat( statementInspector.getSqlQueries().get( 0 ) ).doesNotContain( " left " );
|
||||
assertThat( statementInspector.getSqlQueries().get( 0 ) ).doesNotContain( " cross " );
|
||||
} );
|
||||
}
|
||||
|
||||
@Test
|
||||
@JiraKey( "HHH-15060" )
|
||||
public void testQueryOwnerSelection(SessionFactoryScope scope) {
|
||||
final SQLStatementInspector statementInspector = scope.getCollectingStatementInspector();
|
||||
statementInspector.clear();
|
||||
scope.inTransaction( (session) -> {
|
||||
final String hql = "select c from Coin c where c.id = 1";
|
||||
final Coin coin = session.createQuery( hql, Coin.class ).uniqueResult();
|
||||
assertThat( coin ).isNotNull();
|
||||
assertThat( Hibernate.isPropertyInitialized( coin, "currency" ) ).isTrue();
|
||||
assertThat( Hibernate.isInitialized( coin.getCurrency() ) ).isTrue();
|
||||
assertThat( coin.getCurrency() ).isNull();
|
||||
} );
|
||||
|
||||
scope.inTransaction( (session) -> {
|
||||
final String hql = "select c from Coin c";
|
||||
final List<Coin> coins = session.createSelectionQuery( hql, Coin.class ).getResultList();
|
||||
assertThat( coins ).hasSize( 1 );
|
||||
assertThat( coins.get( 0 ).getCurrency() ).isNull();
|
||||
|
||||
assertThat( statementInspector.getSqlQueries() ).hasSize( 1 );
|
||||
assertThat( statementInspector.getSqlQueries().get( 0 ) ).contains( " Coin " );
|
||||
assertThat( statementInspector.getSqlQueries().get( 0 ) ).contains( " Currency " );
|
||||
assertThat( statementInspector.getSqlQueries().get( 0 ) ).contains( " join " );
|
||||
assertThat( statementInspector.getSqlQueries().get( 0 ) ).doesNotContain( " inner " );
|
||||
final String hql = "select c from Coin c where c.id = 2";
|
||||
final Coin coin = session.createQuery( hql, Coin.class ).uniqueResult();
|
||||
assertThat( Hibernate.isPropertyInitialized( coin, "currency" ) ).isTrue();
|
||||
assertThat( Hibernate.isInitialized( coin.getCurrency() ) ).isTrue();
|
||||
} );
|
||||
}
|
||||
|
||||
@Test
|
||||
@JiraKey( "HHH-15060" )
|
||||
// @FailureExpected( reason = "Has zero results because of bad join" )
|
||||
public void testQueryAssociationSelection(SessionFactoryScope scope) {
|
||||
final SQLStatementInspector statementInspector = scope.getCollectingStatementInspector();
|
||||
statementInspector.clear();
|
||||
|
||||
scope.inTransaction( (session) -> {
|
||||
final String hql = "select c.id, c.currency from Coin c";
|
||||
final List<Tuple> tuples = session.createQuery( hql, Tuple.class ).getResultList();
|
||||
assertThat( tuples ).hasSize( 0 );
|
||||
|
||||
assertThat( statementInspector.getSqlQueries() ).hasSize( 1 );
|
||||
assertThat( statementInspector.getSqlQueries().get( 0 ) ).contains( " Coin " );
|
||||
assertThat( statementInspector.getSqlQueries().get( 0 ) ).contains( " Currency " );
|
||||
assertThat( statementInspector.getSqlQueries().get( 0 ) ).contains( " join " );
|
||||
assertThat( statementInspector.getSqlQueries().get( 0 ) ).doesNotContain( " left " );
|
||||
assertThat( statementInspector.getSqlQueries().get( 0 ) ).doesNotContain( " cross " );
|
||||
} );
|
||||
|
||||
statementInspector.clear();
|
||||
|
||||
// I guess this one is somewhat debatable, but for consistency I think this makes the most sense
|
||||
scope.inTransaction( (session) -> {
|
||||
final String hql = "select c.currency from Coin c";
|
||||
final String hql = "select c.currency from Coin c where c.id = 1";
|
||||
final List<Currency> currencies = session.createSelectionQuery( hql, Currency.class ).getResultList();
|
||||
assertThat( currencies ).hasSize( 0 );
|
||||
|
||||
|
@ -162,9 +242,13 @@ public class NotFoundIgnoreManyToOneTest {
|
|||
scope.inTransaction( (session) -> {
|
||||
Currency euro = new Currency( 1, "Euro" );
|
||||
Coin fiveC = new Coin( 1, "Five cents", euro );
|
||||
|
||||
session.persist( euro );
|
||||
session.persist( fiveC );
|
||||
|
||||
Currency usd = new Currency( 2, "USD" );
|
||||
Coin penny = new Coin( 2, "Penny", usd );
|
||||
session.persist( usd );
|
||||
session.persist( penny );
|
||||
} );
|
||||
|
||||
scope.inTransaction( (session) -> {
|
||||
|
@ -175,7 +259,8 @@ public class NotFoundIgnoreManyToOneTest {
|
|||
@AfterEach
|
||||
protected void dropTestData(SessionFactoryScope scope) throws Exception {
|
||||
scope.inTransaction( (session) -> {
|
||||
session.createMutationQuery( "delete Coin where id = 1" ).executeUpdate();
|
||||
session.createMutationQuery( "delete Coin" ).executeUpdate();
|
||||
session.createMutationQuery( "delete Currency" ).executeUpdate();
|
||||
} );
|
||||
}
|
||||
|
||||
|
|
|
@ -48,12 +48,24 @@ public class NotFoundIgnoreOneToOneTest {
|
|||
|
||||
@Test
|
||||
@JiraKey( "HHH-15060" )
|
||||
public void testProxy(SessionFactoryScope scope) {
|
||||
public void testProxyCoin(SessionFactoryScope scope) {
|
||||
scope.inTransaction( (session) -> {
|
||||
// the non-existent Child
|
||||
// - this is the one valid deviation from treating the broken fk as null
|
||||
try {
|
||||
// Coin#1 has the broken fk
|
||||
final Coin proxy = session.byId( Coin.class ).getReference( 1 );
|
||||
assertThat( proxy ).isNotNull();
|
||||
Hibernate.initialize( proxy );
|
||||
assertThat( Hibernate.isInitialized( proxy ) ).isTrue();
|
||||
assertThat( proxy.getCurrency() ).isNull();
|
||||
} );
|
||||
}
|
||||
|
||||
@Test
|
||||
@JiraKey( "HHH-15060" )
|
||||
public void testProxyCurrency(SessionFactoryScope scope) {
|
||||
scope.inTransaction( (session) -> {
|
||||
// Currency#1 does not exist
|
||||
final Currency proxy = session.byId( Currency.class ).getReference( 1 );
|
||||
try {
|
||||
Hibernate.initialize( proxy );
|
||||
Assertions.fail( "Expecting ObjectNotFoundException" );
|
||||
}
|
||||
|
@ -73,7 +85,6 @@ public class NotFoundIgnoreOneToOneTest {
|
|||
scope.inTransaction( (session) -> {
|
||||
final Coin coin = session.get( Coin.class, 1 );
|
||||
assertThat( coin.getCurrency() ).isNull();
|
||||
|
||||
assertThat( statementInspector.getSqlQueries() ).hasSize( 1 );
|
||||
assertThat( statementInspector.getSqlQueries().get( 0 ) ).contains( " Coin " );
|
||||
assertThat( statementInspector.getSqlQueries().get( 0 ) ).contains( " Currency " );
|
||||
|
@ -81,6 +92,89 @@ public class NotFoundIgnoreOneToOneTest {
|
|||
assertThat( statementInspector.getSqlQueries().get( 0 ) ).contains( " left " );
|
||||
assertThat( statementInspector.getSqlQueries().get( 0 ) ).doesNotContain( " inner " );
|
||||
} );
|
||||
|
||||
statementInspector.clear();
|
||||
|
||||
scope.inTransaction( (session) -> {
|
||||
final Coin coin = session.get( Coin.class, 1 );
|
||||
assertThat( coin.getCurrency() ).isNull();
|
||||
assertThat( statementInspector.getSqlQueries() ).hasSize( 1 );
|
||||
assertThat( statementInspector.getSqlQueries().get( 0 ) ).contains( " join " );
|
||||
assertThat( statementInspector.getSqlQueries().get( 0 ) ).doesNotContain( " inner " );
|
||||
} );
|
||||
}
|
||||
|
||||
@Test
|
||||
@JiraKey( "HHH-15060" )
|
||||
public void testQueryImplicitPathDereferencePredicateBaseline(SessionFactoryScope scope) {
|
||||
final SQLStatementInspector statementInspector = scope.getCollectingStatementInspector();
|
||||
statementInspector.clear();
|
||||
|
||||
scope.inTransaction( (session) -> {
|
||||
final String hql = "select c from Coin c where c.currency.name = 'Euro'";
|
||||
final List<Coin> coins = session.createSelectionQuery( hql, Coin.class ).getResultList();
|
||||
assertThat( coins ).hasSize( 0 );
|
||||
assertThat( statementInspector.getSqlQueries() ).hasSize( 1 );
|
||||
assertThat( statementInspector.getSqlQueries().get( 0 ) ).contains( " join " );
|
||||
assertThat( statementInspector.getSqlQueries().get( 0 ) ).doesNotContain( " left " );
|
||||
assertThat( statementInspector.getSqlQueries().get( 0 ) ).doesNotContain( " cross " );
|
||||
} );
|
||||
}
|
||||
|
||||
@Test
|
||||
@JiraKey( "HHH-15060" )
|
||||
public void testQueryImplicitPathDereferencePredicateBaseline2(SessionFactoryScope scope) {
|
||||
final SQLStatementInspector statementInspector = scope.getCollectingStatementInspector();
|
||||
statementInspector.clear();
|
||||
|
||||
scope.inTransaction( (session) -> {
|
||||
final String hql = "select c.id from Coin c where c.currency.id = 1";
|
||||
final List<Integer> coins = session.createSelectionQuery( hql, Integer.class ).getResultList();
|
||||
assertThat( coins ).isEmpty();
|
||||
|
||||
// technically we could use a subsequent-select rather than a join...
|
||||
assertThat( statementInspector.getSqlQueries() ).hasSize( 1 );
|
||||
assertThat( statementInspector.getSqlQueries().get( 0 ) ).contains( " join " );
|
||||
assertThat( statementInspector.getSqlQueries().get( 0 ) ).doesNotContain( " left " );
|
||||
assertThat( statementInspector.getSqlQueries().get( 0 ) ).doesNotContain( " cross " );
|
||||
} );
|
||||
}
|
||||
|
||||
/**
|
||||
* Baseline for {@link #testQueryImplicitPathDereferencePredicate}. Ultimately, we want
|
||||
* SQL generated there to behave exactly the same as this query
|
||||
*/
|
||||
@Test
|
||||
@JiraKey( "HHH-15060" )
|
||||
public void testQueryImplicitPathDereferencePredicateBaseline3(SessionFactoryScope scope) {
|
||||
final SQLStatementInspector statementInspector = scope.getCollectingStatementInspector();
|
||||
statementInspector.clear();
|
||||
|
||||
scope.inTransaction( (session) -> {
|
||||
final String hql = "select c from Coin c join fetch c.currency c2 where c2.name = 'USD'";
|
||||
final List<Coin> coins = session.createQuery( hql, Coin.class ).getResultList();
|
||||
assertThat( coins ).hasSize( 1 );
|
||||
assertThat( statementInspector.getSqlQueries() ).hasSize( 1 );
|
||||
assertThat( statementInspector.getSqlQueries().get( 0 ) ).contains( " Coin " );
|
||||
assertThat( statementInspector.getSqlQueries().get( 0 ) ).contains( " Currency " );
|
||||
assertThat( statementInspector.getSqlQueries().get( 0 ) ).contains( " join " );
|
||||
assertThat( statementInspector.getSqlQueries().get( 0 ) ).doesNotContain( " left " );
|
||||
assertThat( statementInspector.getSqlQueries().get( 0 ) ).doesNotContain( " cross " );
|
||||
} );
|
||||
|
||||
statementInspector.clear();
|
||||
|
||||
scope.inTransaction( (session) -> {
|
||||
final String hql = "select c from Coin c join fetch c.currency c2 where c2.id = 1";
|
||||
final List<Coin> coins = session.createQuery( hql, Coin.class ).getResultList();
|
||||
assertThat( coins ).hasSize( 0 );
|
||||
assertThat( statementInspector.getSqlQueries() ).hasSize( 1 );
|
||||
assertThat( statementInspector.getSqlQueries().get( 0 ) ).contains( " Coin " );
|
||||
assertThat( statementInspector.getSqlQueries().get( 0 ) ).contains( " Currency " );
|
||||
assertThat( statementInspector.getSqlQueries().get( 0 ) ).contains( " join " );
|
||||
assertThat( statementInspector.getSqlQueries().get( 0 ) ).doesNotContain( " left " );
|
||||
assertThat( statementInspector.getSqlQueries().get( 0 ) ).doesNotContain( " cross " );
|
||||
} );
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -95,10 +189,9 @@ public class NotFoundIgnoreOneToOneTest {
|
|||
assertThat( coins ).isEmpty();
|
||||
|
||||
assertThat( statementInspector.getSqlQueries() ).hasSize( 1 );
|
||||
assertThat( statementInspector.getSqlQueries().get( 0 ) ).contains( " Coin " );
|
||||
assertThat( statementInspector.getSqlQueries().get( 0 ) ).contains( " Currency " );
|
||||
assertThat( statementInspector.getSqlQueries().get( 0 ) ).contains( " join " );
|
||||
assertThat( statementInspector.getSqlQueries().get( 0 ) ).doesNotContain( " inner " );
|
||||
assertThat( statementInspector.getSqlQueries().get( 0 ) ).doesNotContain( " left " );
|
||||
assertThat( statementInspector.getSqlQueries().get( 0 ) ).doesNotContain( " cross " );
|
||||
} );
|
||||
}
|
||||
|
||||
|
@ -109,16 +202,19 @@ public class NotFoundIgnoreOneToOneTest {
|
|||
statementInspector.clear();
|
||||
|
||||
scope.inTransaction( (session) -> {
|
||||
final String hql = "select c from Coin c";
|
||||
final List<Coin> coins = session.createQuery( hql, Coin.class ).getResultList();
|
||||
assertThat( coins ).hasSize( 1 );
|
||||
assertThat( coins.get( 0 ).getCurrency() ).isNull();
|
||||
final String hql = "select c from Coin c where c.id = 1";
|
||||
final Coin coin = session.createQuery( hql, Coin.class ).uniqueResult();
|
||||
assertThat( coin ).isNotNull();
|
||||
assertThat( Hibernate.isPropertyInitialized( coin, "currency" ) ).isTrue();
|
||||
assertThat( Hibernate.isInitialized( coin.getCurrency() ) ).isTrue();
|
||||
assertThat( coin.getCurrency() ).isNull();
|
||||
} );
|
||||
|
||||
assertThat( statementInspector.getSqlQueries() ).hasSize( 1 );
|
||||
assertThat( statementInspector.getSqlQueries().get( 0 ) ).contains( " Coin " );
|
||||
assertThat( statementInspector.getSqlQueries().get( 0 ) ).contains( " Currency " );
|
||||
assertThat( statementInspector.getSqlQueries().get( 0 ) ).contains( " join " );
|
||||
assertThat( statementInspector.getSqlQueries().get( 0 ) ).contains( " left " );
|
||||
scope.inTransaction( (session) -> {
|
||||
final String hql = "select c from Coin c where c.id = 2";
|
||||
final Coin coin = session.createQuery( hql, Coin.class ).uniqueResult();
|
||||
assertThat( Hibernate.isPropertyInitialized( coin, "currency" ) ).isTrue();
|
||||
assertThat( Hibernate.isInitialized( coin.getCurrency() ) ).isTrue();
|
||||
} );
|
||||
}
|
||||
|
||||
|
@ -129,22 +225,11 @@ public class NotFoundIgnoreOneToOneTest {
|
|||
statementInspector.clear();
|
||||
|
||||
scope.inTransaction( (session) -> {
|
||||
final String hql = "select c.id, c.currency from Coin c";
|
||||
final List<Tuple> tuples = session.createQuery( hql, Tuple.class ).getResultList();
|
||||
assertThat( tuples ).hasSize( 0 );
|
||||
assertThat( statementInspector.getSqlQueries().get( 0 ) ).contains( " Coin " );
|
||||
assertThat( statementInspector.getSqlQueries().get( 0 ) ).contains( " Currency " );
|
||||
assertThat( statementInspector.getSqlQueries().get( 0 ) ).contains( " join " );
|
||||
assertThat( statementInspector.getSqlQueries().get( 0 ) ).doesNotContain( " left " );
|
||||
assertThat( statementInspector.getSqlQueries().get( 0 ) ).doesNotContain( " cross " );
|
||||
} );
|
||||
|
||||
statementInspector.clear();
|
||||
|
||||
scope.inTransaction( (session) -> {
|
||||
final String hql = "select c.currency from Coin c";
|
||||
final List<Currency> currencies = session.createQuery( hql, Currency.class ).getResultList();
|
||||
final String hql = "select c.currency from Coin c where c.id = 1";
|
||||
final List<Currency> currencies = session.createSelectionQuery( hql, Currency.class ).getResultList();
|
||||
assertThat( currencies ).hasSize( 0 );
|
||||
|
||||
assertThat( statementInspector.getSqlQueries() ).hasSize( 1 );
|
||||
assertThat( statementInspector.getSqlQueries().get( 0 ) ).contains( " Coin " );
|
||||
assertThat( statementInspector.getSqlQueries().get( 0 ) ).contains( " Currency " );
|
||||
assertThat( statementInspector.getSqlQueries().get( 0 ) ).contains( " join " );
|
||||
|
@ -158,9 +243,13 @@ public class NotFoundIgnoreOneToOneTest {
|
|||
scope.inTransaction( (session) -> {
|
||||
Currency euro = new Currency( 1, "Euro" );
|
||||
Coin fiveC = new Coin( 1, "Five cents", euro );
|
||||
|
||||
session.persist( euro );
|
||||
session.persist( fiveC );
|
||||
|
||||
Currency usd = new Currency( 2, "USD" );
|
||||
Coin penny = new Coin( 2, "Penny", usd );
|
||||
session.persist( usd );
|
||||
session.persist( penny );
|
||||
} );
|
||||
|
||||
scope.inTransaction( (session) -> {
|
||||
|
@ -171,7 +260,8 @@ public class NotFoundIgnoreOneToOneTest {
|
|||
@AfterEach
|
||||
public void dropTestData(SessionFactoryScope scope) {
|
||||
scope.inTransaction( (session) -> {
|
||||
session.createMutationQuery( "delete Coin where id = 1" ).executeUpdate();
|
||||
session.createMutationQuery( "delete Coin" ).executeUpdate();
|
||||
session.createMutationQuery( "delete Currency" ).executeUpdate();
|
||||
} );
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue