Fix some more result set mapping issues and fix HHH-7525 as well as HHH-10504
This commit is contained in:
parent
dd650705d0
commit
b6683d2352
|
@ -182,8 +182,8 @@ include::{extrasdir}/sql-hibernate-entity-associations-query-many-to-one-join-ex
|
|||
As seen in the associated SQL query, Hibernate manages to construct the entity hierarchy without requiring any extra database roundtrip.
|
||||
====
|
||||
|
||||
By default, when using the `addJoin()` method, the result set will contain both entities that are joined.
|
||||
To construct the entity hierarchy, you need to use a `ROOT_ENTITY` or `DISTINCT_ROOT_ENTITY` `ResultTransformer`.
|
||||
Even when using the `addJoin()` method, the result list will only contain the root entity.
|
||||
Joined entities will only be present for their respective association.
|
||||
|
||||
[[sql-hibernate-entity-associations-query-many-to-one-join-result-transformer-example]]
|
||||
.Hibernate native query selecting entities with joined many-to-one association and `ResultTransformer`
|
||||
|
@ -194,11 +194,6 @@ include::{sourcedir}/SQLTest.java[tags=sql-hibernate-entity-associations-query-m
|
|||
----
|
||||
====
|
||||
|
||||
[NOTE]
|
||||
====
|
||||
Because of the `ROOT_ENTITY` `ResultTransformer`, the query above will return the parent-side as root entities.
|
||||
====
|
||||
|
||||
Notice that you added an alias name _pr_ to be able to specify the target property path of the join.
|
||||
It is possible to do the same eager joining for collections (e.g. the `Phone#calls` `one-to-many` association).
|
||||
|
||||
|
|
|
@ -15,7 +15,7 @@ import javax.persistence.PersistenceException;
|
|||
|
||||
import org.hibernate.Session;
|
||||
import org.hibernate.dialect.H2Dialect;
|
||||
import org.hibernate.dialect.Oracle8iDialect;
|
||||
import org.hibernate.dialect.OracleDialect;
|
||||
import org.hibernate.dialect.PostgreSQLDialect;
|
||||
import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase;
|
||||
import org.hibernate.loader.NonUniqueDiscoveredSqlAliasException;
|
||||
|
@ -36,6 +36,7 @@ import org.hibernate.userguide.model.PhoneType;
|
|||
import org.hibernate.userguide.model.WireTransferPayment;
|
||||
|
||||
import org.hibernate.testing.RequiresDialect;
|
||||
import org.hibernate.testing.TestForIssue;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
|
@ -335,17 +336,15 @@ public class SQLTest extends BaseEntityManagerFunctionalTestCase {
|
|||
@Test
|
||||
public void test_sql_jpa_entity_associations_query_many_to_one_join_example() {
|
||||
doInJPA( this::entityManagerFactory, entityManager -> {
|
||||
//tag::sql-jpa-entity-associations-query-many-to-one-join-example[]
|
||||
List<Phone> phones = entityManager.createNativeQuery(
|
||||
"SELECT * " +
|
||||
"SELECT ph.* " +
|
||||
"FROM Phone ph " +
|
||||
"JOIN Person pr ON ph.person_id = pr.id", Phone.class )
|
||||
.getResultList();
|
||||
|
||||
for(Phone phone : phones) {
|
||||
for( Phone phone : phones ) {
|
||||
assertNotNull( phone.getPerson().getName() );
|
||||
}
|
||||
//end::sql-jpa-entity-associations-query-many-to-one-join-example[]
|
||||
assertEquals(3, phones.size());
|
||||
});
|
||||
}
|
||||
|
@ -355,18 +354,16 @@ public class SQLTest extends BaseEntityManagerFunctionalTestCase {
|
|||
doInJPA( this::entityManagerFactory, entityManager -> {
|
||||
Session session = entityManager.unwrap( Session.class );
|
||||
//tag::sql-hibernate-entity-associations-query-many-to-one-join-example[]
|
||||
List<Object[]> tuples = session.createNativeQuery(
|
||||
"SELECT * " +
|
||||
List<Phone> tuples = session.createNativeQuery(
|
||||
"SELECT {ph.*}, {pr.*} " +
|
||||
"FROM Phone ph " +
|
||||
"JOIN Person pr ON ph.person_id = pr.id" )
|
||||
.addEntity("phone", Phone.class )
|
||||
.addJoin( "pr", "phone.person")
|
||||
.addEntity("ph", Phone.class )
|
||||
.addJoin( "pr", "ph.person" )
|
||||
.list();
|
||||
|
||||
for(Object[] tuple : tuples) {
|
||||
Phone phone = (Phone) tuple[0];
|
||||
Person person = (Person) tuple[1];
|
||||
assertNotNull( person.getName() );
|
||||
for ( Phone phone : tuples ) {
|
||||
assertNotNull( phone.getPerson().getName() );
|
||||
}
|
||||
//end::sql-hibernate-entity-associations-query-many-to-one-join-example[]
|
||||
assertEquals(3, tuples.size());
|
||||
|
@ -378,37 +375,36 @@ public class SQLTest extends BaseEntityManagerFunctionalTestCase {
|
|||
doInJPA( this::entityManagerFactory, entityManager -> {
|
||||
Session session = entityManager.unwrap( Session.class );
|
||||
//tag::sql-hibernate-entity-associations-query-many-to-one-join-result-transformer-example[]
|
||||
List<Person> persons = session.createNativeQuery(
|
||||
"SELECT * " +
|
||||
List<Phone> phones = session.createNativeQuery(
|
||||
"SELECT {ph.*}, {pr.*} " +
|
||||
"FROM Phone ph " +
|
||||
"JOIN Person pr ON ph.person_id = pr.id" )
|
||||
.addEntity("phone", Phone.class )
|
||||
.addJoin( "pr", "phone.person")
|
||||
.setResultTransformer( RootEntityResultTransformer.INSTANCE )
|
||||
.addEntity("ph", Phone.class )
|
||||
.addJoin( "pr", "ph.person")
|
||||
.list();
|
||||
|
||||
for(Person person : persons) {
|
||||
person.getPhones();
|
||||
for ( Phone person : phones ) {
|
||||
person.getPerson();
|
||||
}
|
||||
//end::sql-hibernate-entity-associations-query-many-to-one-join-result-transformer-example[]
|
||||
assertEquals(3, persons.size());
|
||||
assertEquals(3, phones.size());
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
@RequiresDialect(H2Dialect.class)
|
||||
@RequiresDialect(Oracle8iDialect.class)
|
||||
@RequiresDialect(OracleDialect.class)
|
||||
@RequiresDialect(PostgreSQLDialect.class)
|
||||
public void test_sql_jpa_entity_associations_query_one_to_many_join_example() {
|
||||
doInJPA( this::entityManagerFactory, entityManager -> {
|
||||
//tag::sql-jpa-entity-associations-query-one-to-many-join-example[]
|
||||
List<Phone> phones = entityManager.createNativeQuery(
|
||||
"SELECT * " +
|
||||
"SELECT ph.* " +
|
||||
"FROM Phone ph " +
|
||||
"JOIN phone_call c ON c.phone_id = ph.id", Phone.class )
|
||||
.getResultList();
|
||||
|
||||
for(Phone phone : phones) {
|
||||
for ( Phone phone : phones ) {
|
||||
List<Call> calls = phone.getCalls();
|
||||
}
|
||||
//end::sql-jpa-entity-associations-query-one-to-many-join-example[]
|
||||
|
@ -417,50 +413,43 @@ public class SQLTest extends BaseEntityManagerFunctionalTestCase {
|
|||
}
|
||||
|
||||
@Test
|
||||
@TestForIssue( jiraKey = "HHH-10504")
|
||||
public void test_sql_hibernate_entity_associations_query_one_to_many_join_example_1() {
|
||||
try {
|
||||
doInJPA( this::entityManagerFactory, entityManager -> {
|
||||
Session session = entityManager.unwrap( Session.class );
|
||||
List<Phone> phones = session.createNativeQuery(
|
||||
"SELECT * " +
|
||||
"FROM Phone ph " +
|
||||
"JOIN phone_call c ON c.phone_id = ph.id" )
|
||||
.addEntity("phone", Phone.class )
|
||||
.addJoin( "c", "phone.calls")
|
||||
.setResultTransformer( DistinctRootEntityResultTransformer.INSTANCE)
|
||||
.list();
|
||||
doInJPA( this::entityManagerFactory, entityManager -> {
|
||||
Session session = entityManager.unwrap( Session.class );
|
||||
List<Phone> phones = session.createNativeQuery(
|
||||
"SELECT {ph.*}, {c.*} " +
|
||||
"FROM Phone ph " +
|
||||
"JOIN phone_call c ON c.phone_id = ph.id" )
|
||||
.addEntity("ph", Phone.class )
|
||||
.addJoin( "c", "ph.calls" )
|
||||
.list();
|
||||
|
||||
for(Phone phone : phones) {
|
||||
List<Call> calls = phone.getCalls();
|
||||
}
|
||||
assertEquals(2, phones.size());
|
||||
});
|
||||
}
|
||||
catch (Exception e) {
|
||||
log.error( "HHH-10504", e );
|
||||
//See issue https://hibernate.atlassian.net/browse/HHH-10504
|
||||
}
|
||||
for ( Phone phone : phones ) {
|
||||
List<Call> calls = phone.getCalls();
|
||||
}
|
||||
assertEquals( 2, phones.size() );
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
@RequiresDialect(H2Dialect.class)
|
||||
@RequiresDialect(Oracle8iDialect.class)
|
||||
@RequiresDialect(OracleDialect.class)
|
||||
@RequiresDialect(PostgreSQLDialect.class)
|
||||
public void test_sql_hibernate_entity_associations_query_one_to_many_join_example_2() {
|
||||
doInJPA( this::entityManagerFactory, entityManager -> {
|
||||
Session session = entityManager.unwrap( Session.class );
|
||||
//tag::sql-hibernate-entity-associations-query-one-to-many-join-example[]
|
||||
List<Object[]> tuples = session.createNativeQuery(
|
||||
"SELECT * " +
|
||||
List<Phone> tuples = session.createNativeQuery(
|
||||
"SELECT {ph.*}, {c.*} " +
|
||||
"FROM Phone ph " +
|
||||
"JOIN phone_call c ON c.phone_id = ph.id" )
|
||||
.addEntity("phone", Phone.class )
|
||||
.addJoin( "c", "phone.calls")
|
||||
.addEntity("ph", Phone.class )
|
||||
.addJoin( "c", "ph.calls")
|
||||
.list();
|
||||
|
||||
for(Object[] tuple : tuples) {
|
||||
Phone phone = (Phone) tuple[0];
|
||||
Call call = (Call) tuple[1];
|
||||
for ( Phone phone : tuples ) {
|
||||
List<Call> calls = phone.getCalls();
|
||||
}
|
||||
//end::sql-hibernate-entity-associations-query-one-to-many-join-example[]
|
||||
assertEquals(2, tuples.size());
|
||||
|
@ -472,10 +461,10 @@ public class SQLTest extends BaseEntityManagerFunctionalTestCase {
|
|||
try {
|
||||
doInJPA( this::entityManagerFactory, entityManager -> {
|
||||
//tag::sql-jpa-multi-entity-query-example[]
|
||||
List<Object> entities = entityManager.createNativeQuery(
|
||||
List<Person> entities = entityManager.createNativeQuery(
|
||||
"SELECT * " +
|
||||
"FROM Person pr, Partner pt " +
|
||||
"WHERE pr.name = pt.name" )
|
||||
"WHERE pr.name = pt.name", Person.class )
|
||||
.getResultList();
|
||||
//end::sql-jpa-multi-entity-query-example[]
|
||||
assertEquals(2, entities.size());
|
||||
|
@ -493,10 +482,10 @@ public class SQLTest extends BaseEntityManagerFunctionalTestCase {
|
|||
doInJPA( this::entityManagerFactory, entityManager -> {
|
||||
Session session = entityManager.unwrap( Session.class );
|
||||
//tag::sql-hibernate-multi-entity-query-example[]
|
||||
List<Object> entities = session.createNativeQuery(
|
||||
List<Person> entities = session.createNativeQuery(
|
||||
"SELECT * " +
|
||||
"FROM Person pr, Partner pt " +
|
||||
"WHERE pr.name = pt.name" )
|
||||
"WHERE pr.name = pt.name", Person.class )
|
||||
.list();
|
||||
//end::sql-hibernate-multi-entity-query-example[]
|
||||
assertEquals( 2, entities.size() );
|
||||
|
@ -720,7 +709,7 @@ public class SQLTest extends BaseEntityManagerFunctionalTestCase {
|
|||
|
||||
@Test
|
||||
@RequiresDialect(H2Dialect.class)
|
||||
@RequiresDialect(Oracle8iDialect.class)
|
||||
@RequiresDialect(OracleDialect.class)
|
||||
@RequiresDialect(PostgreSQLDialect.class)
|
||||
public void test_sql_jpa_entity_associations_named_query_example() {
|
||||
doInJPA( this::entityManagerFactory, entityManager -> {
|
||||
|
@ -741,7 +730,7 @@ public class SQLTest extends BaseEntityManagerFunctionalTestCase {
|
|||
|
||||
@Test
|
||||
@RequiresDialect(H2Dialect.class)
|
||||
@RequiresDialect(Oracle8iDialect.class)
|
||||
@RequiresDialect(OracleDialect.class)
|
||||
@RequiresDialect(PostgreSQLDialect.class)
|
||||
public void test_sql_hibernate_entity_associations_named_query_example() {
|
||||
doInJPA( this::entityManagerFactory, entityManager -> {
|
||||
|
|
|
@ -26,6 +26,7 @@ import org.hibernate.FlushMode;
|
|||
import org.hibernate.Hibernate;
|
||||
import org.hibernate.HibernateException;
|
||||
import org.hibernate.Interceptor;
|
||||
import org.hibernate.LockMode;
|
||||
import org.hibernate.MultiTenancyStrategy;
|
||||
import org.hibernate.NotYetImplementedFor6Exception;
|
||||
import org.hibernate.SessionEventListener;
|
||||
|
@ -705,7 +706,7 @@ public abstract class AbstractSharedSessionContract implements SharedSessionCont
|
|||
|
||||
try {
|
||||
NativeQueryImplementor query = createNativeQuery( sqlString );
|
||||
// query.addEntity( "alias1", resultClass.getName(), LockMode.READ );
|
||||
query.addEntity( "alias1", resultClass.getName(), LockMode.READ );
|
||||
return query;
|
||||
}
|
||||
catch (RuntimeException he) {
|
||||
|
|
|
@ -13,6 +13,7 @@ import org.hibernate.NotYetImplementedFor6Exception;
|
|||
import org.hibernate.engine.spi.LoadQueryInfluencers;
|
||||
import org.hibernate.metamodel.mapping.ModelPart;
|
||||
import org.hibernate.query.NavigablePath;
|
||||
import org.hibernate.sql.ast.spi.SqlAliasBase;
|
||||
import org.hibernate.sql.ast.spi.SqlAstCreationState;
|
||||
import org.hibernate.sql.ast.spi.SqlAstCreationContext;
|
||||
import org.hibernate.sql.ast.tree.from.RootTableGroupProducer;
|
||||
|
@ -44,4 +45,15 @@ public interface Loadable extends ModelPart, RootTableGroupProducer {
|
|||
SqlAstCreationState creationState, SqlAstCreationContext creationContext) {
|
||||
throw new NotYetImplementedFor6Exception( getClass() );
|
||||
}
|
||||
|
||||
@Override
|
||||
default TableGroup createRootTableGroup(
|
||||
boolean canUseInnerJoins,
|
||||
NavigablePath navigablePath,
|
||||
String explicitSourceAlias,
|
||||
Supplier<Consumer<Predicate>> additionalPredicateCollectorAccess,
|
||||
SqlAliasBase sqlAliasBase,
|
||||
SqlAstCreationState creationState, SqlAstCreationContext creationContext) {
|
||||
throw new NotYetImplementedFor6Exception( getClass() );
|
||||
}
|
||||
}
|
||||
|
|
|
@ -282,11 +282,32 @@ public interface EntityMappingType extends ManagedMappingType, EntityValuedModel
|
|||
Supplier<Consumer<Predicate>> additionalPredicateCollectorAccess,
|
||||
SqlAstCreationState creationState,
|
||||
SqlAstCreationContext creationContext) {
|
||||
return createRootTableGroup(
|
||||
canUseInnerJoins,
|
||||
navigablePath,
|
||||
explicitSourceAlias,
|
||||
additionalPredicateCollectorAccess,
|
||||
creationState.getSqlAliasBaseGenerator().createSqlAliasBase( getSqlAliasStem() ),
|
||||
creationState,
|
||||
creationContext
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
default TableGroup createRootTableGroup(
|
||||
boolean canUseInnerJoins,
|
||||
NavigablePath navigablePath,
|
||||
String explicitSourceAlias,
|
||||
Supplier<Consumer<Predicate>> additionalPredicateCollectorAccess,
|
||||
SqlAliasBase sqlAliasBase,
|
||||
SqlAstCreationState creationState,
|
||||
SqlAstCreationContext creationContext) {
|
||||
return getEntityPersister().createRootTableGroup(
|
||||
canUseInnerJoins,
|
||||
navigablePath,
|
||||
explicitSourceAlias,
|
||||
additionalPredicateCollectorAccess,
|
||||
sqlAliasBase,
|
||||
creationState,
|
||||
creationContext
|
||||
);
|
||||
|
|
|
@ -1372,11 +1372,10 @@ public abstract class AbstractEntityPersister
|
|||
NavigablePath navigablePath,
|
||||
String explicitSourceAlias,
|
||||
Supplier<Consumer<Predicate>> additionalPredicateCollectorAccess,
|
||||
SqlAliasBase sqlAliasBase,
|
||||
SqlAstCreationState creationState,
|
||||
SqlAstCreationContext creationContext) {
|
||||
final SqlExpressionResolver sqlExpressionResolver = creationState.getSqlExpressionResolver();
|
||||
final SqlAliasBaseGenerator aliasBaseGenerator = creationState.getSqlAliasBaseGenerator();
|
||||
final SqlAliasBase sqlAliasBase = aliasBaseGenerator.createSqlAliasBase( getSqlAliasStem() );
|
||||
|
||||
final TableReference primaryTableReference = createPrimaryTableReference(
|
||||
sqlAliasBase,
|
||||
|
@ -1411,7 +1410,9 @@ public abstract class AbstractEntityPersister
|
|||
Collections.emptySet()
|
||||
),
|
||||
joinedTableReference,
|
||||
generateJoinPredicate(
|
||||
additionalPredicateCollectorAccess == null
|
||||
? null
|
||||
: generateJoinPredicate(
|
||||
primaryTableReference,
|
||||
joinedTableReference,
|
||||
getSubclassTableKeyColumns( i ),
|
||||
|
|
|
@ -47,6 +47,7 @@ import org.hibernate.query.NavigablePath;
|
|||
import org.hibernate.sql.InFragment;
|
||||
import org.hibernate.sql.Insert;
|
||||
import org.hibernate.sql.SelectFragment;
|
||||
import org.hibernate.sql.ast.spi.SqlAliasBase;
|
||||
import org.hibernate.sql.ast.spi.SqlAstCreationContext;
|
||||
import org.hibernate.sql.ast.spi.SqlAstCreationState;
|
||||
import org.hibernate.sql.ast.spi.SqlExpressionResolver;
|
||||
|
@ -931,6 +932,7 @@ public class SingleTableEntityPersister extends AbstractEntityPersister {
|
|||
NavigablePath navigablePath,
|
||||
String explicitSourceAlias,
|
||||
Supplier<Consumer<Predicate>> additionalPredicateCollectorAccess,
|
||||
SqlAliasBase sqlAliasBase,
|
||||
SqlAstCreationState creationState,
|
||||
SqlAstCreationContext creationContext) {
|
||||
final TableGroup tableGroup = super.createRootTableGroup(
|
||||
|
@ -938,11 +940,12 @@ public class SingleTableEntityPersister extends AbstractEntityPersister {
|
|||
navigablePath,
|
||||
explicitSourceAlias,
|
||||
additionalPredicateCollectorAccess,
|
||||
sqlAliasBase,
|
||||
creationState,
|
||||
creationContext
|
||||
);
|
||||
|
||||
if ( needsDiscriminator() ) {
|
||||
if ( additionalPredicateCollectorAccess != null && needsDiscriminator() ) {
|
||||
final Predicate discriminatorPredicate = createDiscriminatorPredicate(
|
||||
tableGroup,
|
||||
creationState.getSqlExpressionResolver(),
|
||||
|
|
|
@ -245,11 +245,10 @@ public class UnionSubclassEntityPersister extends AbstractEntityPersister {
|
|||
NavigablePath navigablePath,
|
||||
String explicitSourceAlias,
|
||||
Supplier<Consumer<Predicate>> additionalPredicateCollectorAccess,
|
||||
SqlAliasBase sqlAliasBase,
|
||||
SqlAstCreationState creationState,
|
||||
SqlAstCreationContext creationContext) {
|
||||
final SqlAliasBase sqlAliasBase = creationState.getSqlAliasBaseGenerator().createSqlAliasBase( getSqlAliasStem() );
|
||||
|
||||
final TableReference tableReference = resolvePrimaryTableReference(sqlAliasBase);
|
||||
final TableReference tableReference = resolvePrimaryTableReference( sqlAliasBase );
|
||||
|
||||
return new UnionTableGroup( canUseInnerJoins, navigablePath, tableReference, this, explicitSourceAlias );
|
||||
}
|
||||
|
|
|
@ -12,6 +12,7 @@ import java.util.HashMap;
|
|||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.TreeSet;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
|
@ -19,15 +20,22 @@ import org.hibernate.Incubating;
|
|||
import org.hibernate.Internal;
|
||||
import org.hibernate.LockMode;
|
||||
import org.hibernate.NotYetImplementedFor6Exception;
|
||||
import org.hibernate.engine.FetchTiming;
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.internal.util.StringHelper;
|
||||
import org.hibernate.loader.NonUniqueDiscoveredSqlAliasException;
|
||||
import org.hibernate.metamodel.mapping.BasicValuedMapping;
|
||||
import org.hibernate.persister.entity.AbstractEntityPersister;
|
||||
import org.hibernate.persister.entity.EntityPersister;
|
||||
import org.hibernate.query.named.NamedResultSetMappingMemento;
|
||||
import org.hibernate.query.results.dynamic.DynamicFetchBuilderLegacy;
|
||||
import org.hibernate.sql.ast.spi.SqlSelection;
|
||||
import org.hibernate.sql.results.graph.DomainResult;
|
||||
import org.hibernate.sql.results.graph.Fetch;
|
||||
import org.hibernate.sql.results.graph.basic.BasicFetch;
|
||||
import org.hibernate.sql.results.graph.basic.BasicResult;
|
||||
import org.hibernate.sql.results.graph.embeddable.EmbeddableResultGraphNode;
|
||||
import org.hibernate.sql.results.graph.entity.EntityResult;
|
||||
import org.hibernate.sql.results.jdbc.spi.JdbcValuesMapping;
|
||||
import org.hibernate.sql.results.jdbc.spi.JdbcValuesMetadata;
|
||||
import org.hibernate.type.BasicType;
|
||||
|
@ -183,10 +191,92 @@ public class ResultSetMappingImpl implements ResultSetMapping {
|
|||
|
||||
domainResults.add( domainResult );
|
||||
}
|
||||
// We only need this check when we actually have result builders
|
||||
// As people should be able to just run native queries and work with tuples
|
||||
if ( resultBuilders != null ) {
|
||||
final Set<String> knownDuplicateAliases = new TreeSet<>( String.CASE_INSENSITIVE_ORDER );
|
||||
if ( resultBuilders.size() == 1 && domainResults.size() == 1 && domainResults.get( 0 ) instanceof EntityResult ) {
|
||||
// Special case for result set mappings that just fetch a single polymorphic entity
|
||||
final EntityResult entityResult = (EntityResult) domainResults.get( 0 );
|
||||
final boolean polymorphic = entityResult.getReferencedMappingContainer()
|
||||
.getEntityPersister()
|
||||
.getEntityMetamodel()
|
||||
.isPolymorphic();
|
||||
// We only need to check for duplicate aliases if we have join fetches,
|
||||
// otherwise we assume that even if there are duplicate aliases, the values are equivalent.
|
||||
// If we don't do that, there is no way to fetch joined inheritance entities
|
||||
if ( polymorphic && ( legacyFetchBuilders == null || legacyFetchBuilders.isEmpty() )
|
||||
&& !hasJoinFetches( entityResult.getFetches() ) ) {
|
||||
final Set<String> aliases = new TreeSet<>( String.CASE_INSENSITIVE_ORDER );
|
||||
final AbstractEntityPersister entityPersister = (AbstractEntityPersister) entityResult.getReferencedMappingContainer()
|
||||
.getEntityPersister();
|
||||
for ( String[] columns : entityPersister.getContraintOrderedTableKeyColumnClosure() ) {
|
||||
addColumns( aliases, knownDuplicateAliases, columns );
|
||||
}
|
||||
addColumn( aliases, knownDuplicateAliases, entityPersister.getDiscriminatorColumnName() );
|
||||
addColumn( aliases, knownDuplicateAliases, entityPersister.getVersionColumnName() );
|
||||
for ( int i = 0; i < entityPersister.countSubclassProperties(); i++ ) {
|
||||
addColumns(
|
||||
aliases,
|
||||
knownDuplicateAliases,
|
||||
entityPersister.getSubclassPropertyColumnNames( i )
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
final String[] aliases = new String[rowSize];
|
||||
final Map<String, Boolean> aliasHasDuplicates = new HashMap<>( rowSize );
|
||||
for ( int i = 0; i < rowSize; i++ ) {
|
||||
aliasHasDuplicates.compute(
|
||||
aliases[i] = jdbcResultsMetadata.resolveColumnName( i + 1 ),
|
||||
(k, v) -> v == null ? Boolean.FALSE : Boolean.TRUE
|
||||
);
|
||||
}
|
||||
// Only check for duplicates for the selections that we actually use
|
||||
for ( SqlSelection sqlSelection : sqlSelections ) {
|
||||
final String alias = aliases[sqlSelection.getValuesArrayPosition()];
|
||||
if ( !knownDuplicateAliases.contains( alias ) && aliasHasDuplicates.get( alias ) == Boolean.TRUE ) {
|
||||
throw new NonUniqueDiscoveredSqlAliasException(
|
||||
"Encountered a duplicated sql alias [" + alias + "] during auto-discovery of a native-sql query"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
final Map<String, LockMode> registeredLockModes = creationState.getRegisteredLockModes();
|
||||
return new JdbcValuesMappingImpl( sqlSelections, domainResults, rowSize, registeredLockModes );
|
||||
}
|
||||
|
||||
private static void addColumns(Set<String> aliases, Set<String> knownDuplicateAliases, String[] columns) {
|
||||
for ( int i = 0; i < columns.length; i++ ) {
|
||||
addColumn( aliases, knownDuplicateAliases, columns[i] );
|
||||
}
|
||||
}
|
||||
|
||||
private static void addColumn(Set<String> aliases, Set<String> knownDuplicateAliases, String column) {
|
||||
if ( column != null && !aliases.add( column ) ) {
|
||||
knownDuplicateAliases.add( column );
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean hasJoinFetches(List<Fetch> fetches) {
|
||||
for ( int i = 0; i < fetches.size(); i++ ) {
|
||||
final Fetch fetch = fetches.get( i );
|
||||
if ( fetch instanceof BasicFetch<?> || fetch.getTiming() == FetchTiming.DELAYED ) {
|
||||
// That's fine
|
||||
}
|
||||
else if ( fetch instanceof EmbeddableResultGraphNode ) {
|
||||
// Check all these fetches as well
|
||||
if ( hasJoinFetches( ( (EmbeddableResultGraphNode) fetch ).getFetches() ) ) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private DomainResult<?> makeImplicitDomainResult(
|
||||
int valuesArrayPosition,
|
||||
Consumer<SqlSelection> sqlSelectionConsumer,
|
||||
|
|
|
@ -92,7 +92,7 @@ public class CompleteResultBuilderCollectionStandard implements CompleteResultBu
|
|||
false,
|
||||
navigablePath,
|
||||
tableAlias,
|
||||
() -> predicate -> {},
|
||||
null,
|
||||
creationStateImpl,
|
||||
sessionFactory
|
||||
);
|
||||
|
|
|
@ -84,7 +84,7 @@ public class CompleteResultBuilderEntityJpa implements CompleteResultBuilderEnti
|
|||
true,
|
||||
navigablePath,
|
||||
null,
|
||||
() -> predicate -> {},
|
||||
null,
|
||||
impl.getSqlAstCreationState(),
|
||||
impl.getSqlAstCreationState().getCreationContext()
|
||||
)
|
||||
|
|
|
@ -120,7 +120,7 @@ public class CompleteResultBuilderEntityStandard implements CompleteResultBuilde
|
|||
true,
|
||||
navigablePath,
|
||||
null,
|
||||
() -> predicate -> {},
|
||||
null,
|
||||
impl,
|
||||
impl.getCreationContext()
|
||||
)
|
||||
|
|
|
@ -12,6 +12,7 @@ import java.util.function.BiConsumer;
|
|||
import java.util.function.BiFunction;
|
||||
|
||||
import org.hibernate.LockMode;
|
||||
import org.hibernate.engine.FetchTiming;
|
||||
import org.hibernate.metamodel.mapping.AttributeMapping;
|
||||
import org.hibernate.metamodel.mapping.ForeignKeyDescriptor;
|
||||
import org.hibernate.metamodel.mapping.JdbcMapping;
|
||||
|
@ -127,43 +128,55 @@ public class DynamicFetchBuilderLegacy implements DynamicFetchBuilder, NativeQue
|
|||
tableGroup = ownerTableGroup;
|
||||
}
|
||||
|
||||
final ForeignKeyDescriptor keyDescriptor;
|
||||
if ( attributeMapping instanceof PluralAttributeMapping ) {
|
||||
final PluralAttributeMapping pluralAttributeMapping = (PluralAttributeMapping) attributeMapping;
|
||||
keyDescriptor = pluralAttributeMapping.getKeyDescriptor();
|
||||
if ( columnNames != null ) {
|
||||
final ForeignKeyDescriptor keyDescriptor;
|
||||
if ( attributeMapping instanceof PluralAttributeMapping ) {
|
||||
final PluralAttributeMapping pluralAttributeMapping = (PluralAttributeMapping) attributeMapping;
|
||||
keyDescriptor = pluralAttributeMapping.getKeyDescriptor();
|
||||
}
|
||||
else {
|
||||
// Not sure if this fetch builder can also be used with other attribute mappings
|
||||
assert attributeMapping instanceof ToOneAttributeMapping;
|
||||
|
||||
final ToOneAttributeMapping toOneAttributeMapping = (ToOneAttributeMapping) attributeMapping;
|
||||
keyDescriptor = toOneAttributeMapping.getForeignKeyDescriptor();
|
||||
}
|
||||
|
||||
keyDescriptor.forEachSelectable(
|
||||
(selectionIndex, selectableMapping) -> {
|
||||
resolveSqlSelection(
|
||||
columnNames.get( selectionIndex ),
|
||||
createColumnReferenceKey(
|
||||
tableGroup.getTableReference( selectableMapping.getContainingTableExpression() ),
|
||||
selectableMapping.getSelectionExpression()
|
||||
),
|
||||
selectableMapping.getJdbcMapping(),
|
||||
jdbcResultsMetadata,
|
||||
domainResultCreationState
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
// We process the fetch builder such that it contains a resultBuilderEntity before calling this method in ResultSetMappingProcessor
|
||||
assert resultBuilderEntity != null;
|
||||
|
||||
return resultBuilderEntity.buildFetch(
|
||||
parent,
|
||||
attributeMapping,
|
||||
jdbcResultsMetadata,
|
||||
creationState
|
||||
);
|
||||
}
|
||||
else {
|
||||
// Not sure if this fetch builder can also be used with other attribute mappings
|
||||
assert attributeMapping instanceof ToOneAttributeMapping;
|
||||
|
||||
final ToOneAttributeMapping toOneAttributeMapping = (ToOneAttributeMapping) attributeMapping;
|
||||
keyDescriptor = toOneAttributeMapping.getForeignKeyDescriptor();
|
||||
return parent.generateFetchableFetch(
|
||||
attributeMapping,
|
||||
fetchPath,
|
||||
FetchTiming.IMMEDIATE,
|
||||
true,
|
||||
null,
|
||||
domainResultCreationState
|
||||
);
|
||||
}
|
||||
|
||||
keyDescriptor.forEachSelectable(
|
||||
(selectionIndex, selectableMapping) -> {
|
||||
resolveSqlSelection(
|
||||
columnNames.get( selectionIndex ),
|
||||
createColumnReferenceKey(
|
||||
tableGroup.getTableReference( selectableMapping.getContainingTableExpression() ),
|
||||
selectableMapping.getSelectionExpression()
|
||||
),
|
||||
selectableMapping.getJdbcMapping(),
|
||||
jdbcResultsMetadata,
|
||||
domainResultCreationState
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
// We process the fetch builder such that it contains a resultBuilderEntity before calling this method in ResultSetMappingProcessor
|
||||
assert resultBuilderEntity != null;
|
||||
|
||||
return resultBuilderEntity.buildFetch(
|
||||
parent,
|
||||
attributeMapping,
|
||||
jdbcResultsMetadata,
|
||||
creationState
|
||||
);
|
||||
}
|
||||
|
||||
private void resolveSqlSelection(
|
||||
|
|
|
@ -15,9 +15,8 @@ import org.hibernate.query.NativeQuery;
|
|||
import org.hibernate.query.NavigablePath;
|
||||
import org.hibernate.query.results.DomainResultCreationStateImpl;
|
||||
import org.hibernate.query.results.ResultsHelper;
|
||||
import org.hibernate.query.results.TableGroupImpl;
|
||||
import org.hibernate.sql.ast.spi.SqlAliasBaseConstant;
|
||||
import org.hibernate.sql.ast.tree.from.TableReference;
|
||||
import org.hibernate.sql.ast.tree.from.TableGroup;
|
||||
import org.hibernate.sql.results.graph.DomainResultCreationState;
|
||||
import org.hibernate.sql.results.graph.entity.EntityResult;
|
||||
import org.hibernate.sql.results.jdbc.spi.JdbcValuesMetadata;
|
||||
|
@ -103,18 +102,14 @@ public class DynamicResultBuilderEntityCalculated implements DynamicResultBuilde
|
|||
DomainResultCreationState domainResultCreationState) {
|
||||
final DomainResultCreationStateImpl creationStateImpl = ResultsHelper.impl( domainResultCreationState );
|
||||
|
||||
final TableReference tableReference = entityMapping.createPrimaryTableReference(
|
||||
new SqlAliasBaseConstant( tableAlias ),
|
||||
creationStateImpl.getSqlExpressionResolver(),
|
||||
creationStateImpl.getCreationContext()
|
||||
);
|
||||
|
||||
final TableGroupImpl tableGroup = new TableGroupImpl(
|
||||
final TableGroup tableGroup = entityMapping.createRootTableGroup(
|
||||
true,
|
||||
navigablePath,
|
||||
tableAlias,
|
||||
tableReference,
|
||||
entityMapping,
|
||||
tableAlias
|
||||
null,
|
||||
new SqlAliasBaseConstant( tableAlias ),
|
||||
creationStateImpl,
|
||||
creationStateImpl.getCreationContext()
|
||||
);
|
||||
|
||||
creationStateImpl.getFromClauseAccess().registerTableGroup( navigablePath, tableGroup );
|
||||
|
|
|
@ -53,11 +53,22 @@ public class ImplicitFetchBuilderBasic implements ImplicitFetchBuilder {
|
|||
.getFromClauseAccess()
|
||||
.getTableGroup( parent.getNavigablePath() );
|
||||
|
||||
final String column = fetchable.getSelectionExpression();
|
||||
final String table = fetchable.getContainingTableExpression();
|
||||
final String column;
|
||||
|
||||
// In case of a formula we look for a result set position with the fetchable name
|
||||
if ( fetchable.isFormula() ) {
|
||||
column = fetchable.getFetchableName();
|
||||
}
|
||||
else {
|
||||
column = fetchable.getSelectionExpression();
|
||||
}
|
||||
|
||||
final Expression expression = creationStateImpl.resolveSqlExpression(
|
||||
createColumnReferenceKey( parentTableGroup.getTableReference( fetchPath, table ), column ),
|
||||
createColumnReferenceKey(
|
||||
parentTableGroup.getTableReference( fetchPath, table ),
|
||||
fetchable.getSelectionExpression()
|
||||
),
|
||||
processingState -> {
|
||||
final int jdbcPosition = jdbcResultsMetadata.resolveColumnPosition( column );
|
||||
final int valuesArrayPosition = jdbcPositionToValuesArrayPosition( jdbcPosition );
|
||||
|
|
|
@ -61,8 +61,7 @@ public class ImplicitModelPartResultBuilderEntity
|
|||
true,
|
||||
navigablePath,
|
||||
null,
|
||||
() -> predicate -> {
|
||||
},
|
||||
null,
|
||||
creationStateImpl,
|
||||
creationStateImpl.getCreationContext()
|
||||
);
|
||||
|
|
|
@ -238,7 +238,7 @@ public class ResultSetMappingProcessor implements SQLQueryParser.ParserContext {
|
|||
final List<String> columnNames;
|
||||
final String[] columnAliases = loadable.getSubclassPropertyColumnAliases(
|
||||
fetchBuilder.getFetchableName(),
|
||||
suffix
|
||||
alias2Suffix.get( fetchBuilder.getOwnerAlias() )
|
||||
);
|
||||
if ( columnAliases.length == 0 ) {
|
||||
final CollectionPersister collectionPersister = alias2CollectionPersister.get( fetchBuilder.getTableAlias() );
|
||||
|
|
|
@ -106,8 +106,9 @@ public class MultiTableSqmMutationConverter extends BaseSqmToSqlAstConverter<Sta
|
|||
true,
|
||||
navigablePath,
|
||||
sourceAlias,
|
||||
() -> predicate -> {
|
||||
},
|
||||
// We don't care about the discriminator predicate,
|
||||
// but we pass non-null to ensure table reference join predicates are generated
|
||||
() -> predicate -> {},
|
||||
this,
|
||||
creationContext.getSessionFactory() );
|
||||
|
||||
|
|
|
@ -331,7 +331,7 @@ public class RestrictedDeleteExecutionDelegate implements TableBasedDeleteHandle
|
|||
final Expression expression = sqlExpressionResolver.resolveSqlExpression(
|
||||
SqlExpressionResolver.createColumnReferenceKey( targetTableReference, selection.getSelectionExpression() ),
|
||||
sqlAstProcessingState -> new ColumnReference(
|
||||
rootTableGroup.getPrimaryTableReference(),
|
||||
targetTableReference,
|
||||
selection,
|
||||
sessionFactory
|
||||
)
|
||||
|
|
|
@ -12,6 +12,7 @@ import java.util.function.Supplier;
|
|||
import org.hibernate.LockMode;
|
||||
import org.hibernate.metamodel.mapping.ModelPartContainer;
|
||||
import org.hibernate.query.NavigablePath;
|
||||
import org.hibernate.sql.ast.spi.SqlAliasBase;
|
||||
import org.hibernate.sql.ast.spi.SqlAstCreationContext;
|
||||
import org.hibernate.sql.ast.spi.SqlAstCreationState;
|
||||
import org.hibernate.sql.ast.tree.predicate.Predicate;
|
||||
|
@ -32,4 +33,12 @@ public interface RootTableGroupProducer extends TableGroupProducer, ModelPartCon
|
|||
String explicitSourceAlias,
|
||||
Supplier<Consumer<Predicate>> additionalPredicateCollectorAccess,
|
||||
SqlAstCreationState creationState, SqlAstCreationContext creationContext);
|
||||
|
||||
TableGroup createRootTableGroup(
|
||||
boolean canUseInnerJoins,
|
||||
NavigablePath navigablePath,
|
||||
String explicitSourceAlias,
|
||||
Supplier<Consumer<Predicate>> additionalPredicateCollectorAccess,
|
||||
SqlAliasBase sqlAliasBase,
|
||||
SqlAstCreationState creationState, SqlAstCreationContext creationContext);
|
||||
}
|
||||
|
|
|
@ -67,7 +67,7 @@ public abstract class AbstractResultSetAccess implements ResultSetAccess {
|
|||
catch (SQLException e) {
|
||||
throw getFactory().getJdbcServices().getJdbcEnvironment().getSqlExceptionHelper().convert(
|
||||
e,
|
||||
"Unable to find column position by name"
|
||||
"Unable to find column position by name: " + columnName
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
*/
|
||||
package org.hibernate.orm.test.mapping.formula;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.GeneratedValue;
|
||||
|
@ -13,6 +14,7 @@ import javax.persistence.Id;
|
|||
import javax.persistence.Table;
|
||||
|
||||
import org.hibernate.annotations.Formula;
|
||||
import org.hibernate.query.NativeQuery;
|
||||
import org.hibernate.query.Query;
|
||||
|
||||
import org.hibernate.testing.TestForIssue;
|
||||
|
@ -20,11 +22,13 @@ import org.hibernate.testing.orm.junit.DomainModel;
|
|||
import org.hibernate.testing.orm.junit.SessionFactory;
|
||||
import org.hibernate.testing.orm.junit.SessionFactoryScope;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.hibernate.testing.hamcrest.CollectionMatchers.hasSize;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
/**
|
||||
* @author Алексей Макаров
|
||||
|
@ -52,20 +56,75 @@ public class FormulaNativeQueryTest {
|
|||
|
||||
@Test
|
||||
void testNativeQuery(SessionFactoryScope scope) {
|
||||
scope.inTransaction( session -> {
|
||||
final Query<Foo> query = session.createNativeQuery( "SELECT ft.* FROM foo_table ft", Foo.class );
|
||||
final List<Foo> list = query.getResultList();
|
||||
assertThat( list, hasSize( 3 ) );
|
||||
} );
|
||||
scope.inTransaction(
|
||||
session -> {
|
||||
final Query<Foo> query = session.createNativeQuery( "SELECT ft.* FROM foo_table ft", Foo.class );
|
||||
try {
|
||||
query.getResultList();
|
||||
Assertions.fail(
|
||||
"the native query result mapping should fail if the formula field is not returned from the query" );
|
||||
}
|
||||
catch (Exception ex) {
|
||||
Assertions.assertTrue( ex.getMessage().contains( "distance" ) );
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNativeQueryWithAllFields(SessionFactoryScope scope) {
|
||||
scope.inTransaction(
|
||||
session -> {
|
||||
Query query = session.createNativeQuery(
|
||||
"SELECT ft.*, abs(locationEnd - locationStart) as distance FROM foo_table ft",
|
||||
Foo.class
|
||||
);
|
||||
List<Foo> list = query.getResultList();
|
||||
assertThat( list, hasSize( 3 ) );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNativeQueryWithAliasProperties(SessionFactoryScope scope) {
|
||||
scope.inTransaction(
|
||||
session -> {
|
||||
NativeQuery query = session.createNativeQuery(
|
||||
"SELECT ft.*, abs(ft.locationEnd - locationStart) as d FROM foo_table ft" );
|
||||
query.addRoot( "ft", Foo.class )
|
||||
.addProperty( "id", "id" )
|
||||
.addProperty( "locationStart", "locationStart" )
|
||||
.addProperty( "locationEnd", "locationEnd" )
|
||||
.addProperty( "distance", "d" );
|
||||
List<Foo> list = query.getResultList();
|
||||
assertThat( list, hasSize( 3 ) );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNativeQueryWithAliasSyntax(SessionFactoryScope scope) {
|
||||
scope.inTransaction(
|
||||
session -> {
|
||||
NativeQuery query = session.createNativeQuery(
|
||||
"SELECT ft.id as {ft.id}, ft.locationStart as {ft.locationStart}, ft.locationEnd as {ft.locationEnd}, abs(ft.locationEnd - locationStart) as {ft.distance} FROM foo_table ft" )
|
||||
.addEntity( "ft", Foo.class );
|
||||
query.setProperties( Collections.singletonMap( "distance", "distance" ) );
|
||||
List<Foo> list = query.getResultList();
|
||||
assertThat( list, hasSize( 3 ) );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testHql(SessionFactoryScope scope) {
|
||||
scope.inTransaction( session -> {
|
||||
final Query<Foo> query = session.createQuery( "SELECT ft FROM Foo ft", Foo.class );
|
||||
final List<Foo> list = query.getResultList();
|
||||
assertThat( list, hasSize( 3 ) );
|
||||
} );
|
||||
scope.inTransaction(
|
||||
session -> {
|
||||
final Query<Foo> query = session.createQuery( "SELECT ft FROM Foo ft", Foo.class );
|
||||
final List<Foo> list = query.getResultList();
|
||||
assertThat( list, hasSize( 3 ) );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Entity(name = "Foo")
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
*/
|
||||
package org.hibernate.orm.test.sql.autodiscovery;
|
||||
|
||||
import javax.persistence.PersistenceException;
|
||||
import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
|
@ -19,7 +18,6 @@ import org.hibernate.boot.model.naming.ImplicitNamingStrategyJpaCompliantImpl;
|
|||
import org.hibernate.cfg.Configuration;
|
||||
import org.hibernate.engine.spi.SessionImplementor;
|
||||
import org.hibernate.jdbc.Work;
|
||||
import org.hibernate.loader.NonUniqueDiscoveredSqlAliasException;
|
||||
|
||||
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
|
||||
import org.junit.Assert;
|
||||
|
@ -59,31 +57,25 @@ public class AutoDiscoveryTest extends BaseCoreFunctionalTestCase {
|
|||
session.close();
|
||||
|
||||
session = openSession();
|
||||
try {
|
||||
session.beginTransaction();
|
||||
List results = session.createNativeQuery(
|
||||
"select u.name, u2.name from t_user u, t_user u2 where u.name='steve'" ).list();
|
||||
// this should result in a result set like:
|
||||
// [0] steve, steve
|
||||
// [1] steve, stliu
|
||||
// although the rows could be reversed
|
||||
assertEquals( 2, results.size() );
|
||||
final Object[] row1 = (Object[]) results.get( 0 );
|
||||
final Object[] row2 = (Object[]) results.get( 1 );
|
||||
assertEquals( "steve", row1[0] );
|
||||
assertEquals( "steve", row2[0] );
|
||||
if ( "steve".equals( row1[1] ) ) {
|
||||
assertEquals( "stliu", row2[1] );
|
||||
}
|
||||
else {
|
||||
assertEquals( "stliu", row1[1] );
|
||||
}
|
||||
session.getTransaction().commit();
|
||||
session.beginTransaction();
|
||||
List results = session.createNativeQuery(
|
||||
"select u.name, u2.name from t_user u, t_user u2 where u.name='steve'" ).list();
|
||||
// this should result in a result set like:
|
||||
// [0] steve, steve
|
||||
// [1] steve, stliu
|
||||
// although the rows could be reversed
|
||||
assertEquals( 2, results.size() );
|
||||
final Object[] row1 = (Object[]) results.get( 0 );
|
||||
final Object[] row2 = (Object[]) results.get( 1 );
|
||||
assertEquals( "steve", row1[0] );
|
||||
assertEquals( "steve", row2[0] );
|
||||
if ( "steve".equals( row1[1] ) ) {
|
||||
assertEquals( "stliu", row2[1] );
|
||||
}
|
||||
catch (PersistenceException e) {
|
||||
//expected
|
||||
assertTyping( NonUniqueDiscoveredSqlAliasException.class, e.getCause() );
|
||||
else {
|
||||
assertEquals( "stliu", row1[1] );
|
||||
}
|
||||
session.getTransaction().commit();
|
||||
session.close();
|
||||
|
||||
session = openSession();
|
||||
|
|
Loading…
Reference in New Issue