re-enable tests
re-organize some tests o.h.test.hql.EntityJoinTest fixed RIGHT JOIN handling
This commit is contained in:
parent
107aab03eb
commit
cebb9d7649
|
@ -242,20 +242,32 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
|
|||
private final Stack<ParameterDeclarationContext> parameterDeclarationContextStack = new StandardStack<>();
|
||||
private final Stack<SqmCreationProcessingState> processingStateStack = new StandardStack<>();
|
||||
|
||||
private final BasicDomainType<Integer> integerDomainType;
|
||||
private final JavaTypeDescriptor<List<?>> listJavaTypeDescriptor;
|
||||
private final JavaTypeDescriptor<Map<?,?>> mapJavaTypeDescriptor;
|
||||
|
||||
private ParameterCollector parameterCollector;
|
||||
private BasicDomainType<Integer> integerDomainType;
|
||||
private JavaTypeDescriptor<List<?>> listJavaTypeDescriptor;
|
||||
private JavaTypeDescriptor<Map<?,?>> mapJavaTypeDescriptor;
|
||||
|
||||
@SuppressWarnings("WeakerAccess")
|
||||
public SemanticQueryBuilder(SqmCreationOptions creationOptions, SqmCreationContext creationContext) {
|
||||
this.creationOptions = creationOptions;
|
||||
this.creationContext = creationContext;
|
||||
this.dotIdentifierConsumerStack = new StandardStack<>( new BasicDotIdentifierConsumer( this ) );
|
||||
|
||||
this.integerDomainType = creationContext
|
||||
.getNodeBuilder()
|
||||
.getTypeConfiguration()
|
||||
.standardBasicTypeForJavaType( Integer.class );
|
||||
this.listJavaTypeDescriptor = creationContext
|
||||
.getNodeBuilder()
|
||||
.getTypeConfiguration()
|
||||
.getJavaTypeDescriptorRegistry()
|
||||
.resolveDescriptor( List.class );
|
||||
this.mapJavaTypeDescriptor = creationContext
|
||||
.getNodeBuilder()
|
||||
.getTypeConfiguration()
|
||||
.getJavaTypeDescriptorRegistry()
|
||||
.resolveDescriptor( Map.class );
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -855,24 +867,12 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
|
|||
final SqmDynamicInstantiation<?> dynamicInstantiation;
|
||||
|
||||
if ( ctx.dynamicInstantiationTarget().MAP() != null ) {
|
||||
if ( mapJavaTypeDescriptor == null ) {
|
||||
mapJavaTypeDescriptor = creationContext.getJpaMetamodel()
|
||||
.getTypeConfiguration()
|
||||
.getJavaTypeDescriptorRegistry()
|
||||
.getDescriptor( Map.class );
|
||||
}
|
||||
dynamicInstantiation = SqmDynamicInstantiation.forMapInstantiation(
|
||||
mapJavaTypeDescriptor,
|
||||
creationContext.getNodeBuilder()
|
||||
);
|
||||
}
|
||||
else if ( ctx.dynamicInstantiationTarget().LIST() != null ) {
|
||||
if ( listJavaTypeDescriptor == null ) {
|
||||
listJavaTypeDescriptor = creationContext.getJpaMetamodel()
|
||||
.getTypeConfiguration()
|
||||
.getJavaTypeDescriptorRegistry()
|
||||
.getDescriptor( List.class );
|
||||
}
|
||||
dynamicInstantiation = SqmDynamicInstantiation.forListInstantiation(
|
||||
listJavaTypeDescriptor,
|
||||
creationContext.getNodeBuilder()
|
||||
|
@ -1452,7 +1452,7 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
|
|||
throw new SemanticException( "FULL OUTER joins are not yet supported : " + parserJoin.getText() );
|
||||
}
|
||||
else if ( joinTypeQualifier.RIGHT() != null ) {
|
||||
throw new SemanticException( "RIGHT OUTER joins are not yet supported : " + parserJoin.getText() );
|
||||
joinType = SqmJoinType.RIGHT;
|
||||
}
|
||||
else if ( joinTypeQualifier.OUTER() != null || joinTypeQualifier.LEFT() != null ) {
|
||||
joinType = SqmJoinType.LEFT;
|
||||
|
@ -1475,6 +1475,7 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
|
|||
);
|
||||
|
||||
try {
|
||||
//noinspection rawtypes
|
||||
final SqmQualifiedJoin join = (SqmQualifiedJoin) parserJoin.qualifiedJoinRhs().path().accept( this );
|
||||
|
||||
// we need to set the alias here because the path could be treated - the treat operator is
|
||||
|
@ -1486,8 +1487,13 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
|
|||
sqmRoot.addSqmJoin( join );
|
||||
}
|
||||
else {
|
||||
if ( joinType == SqmJoinType.RIGHT ) {
|
||||
throw new SemanticException( "RIGHT OUTER attribute-joins are not supported : " + parserJoin.getText() );
|
||||
}
|
||||
|
||||
if ( getCreationOptions().useStrictJpaCompliance() ) {
|
||||
if ( join.getExplicitAlias() != null ){
|
||||
//noinspection rawtypes
|
||||
if ( ( (SqmAttributeJoin) join ).isFetched() ) {
|
||||
throw new StrictJpaComplianceViolation(
|
||||
"Encountered aliased fetch join, but strict JPQL compliance was requested",
|
||||
|
|
|
@ -0,0 +1,390 @@
|
|||
/*
|
||||
* Hibernate, Relational Persistence for Idiomatic Java
|
||||
*
|
||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
|
||||
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
|
||||
*/
|
||||
package org.hibernate.orm.test.query.hql;
|
||||
|
||||
import java.util.List;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.FetchType;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.JoinColumn;
|
||||
import javax.persistence.ManyToOne;
|
||||
import javax.persistence.Table;
|
||||
|
||||
import org.hibernate.annotations.NaturalId;
|
||||
import org.hibernate.dialect.SybaseDialect;
|
||||
import org.hibernate.engine.spi.LoadQueryInfluencers;
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.metamodel.mapping.EntityMappingType;
|
||||
import org.hibernate.query.hql.HqlTranslator;
|
||||
import org.hibernate.query.spi.QueryEngine;
|
||||
import org.hibernate.query.spi.QueryOptions;
|
||||
import org.hibernate.query.spi.QueryParameterBindings;
|
||||
import org.hibernate.query.sqm.internal.DomainParameterXref;
|
||||
import org.hibernate.query.sqm.sql.SqmTranslation;
|
||||
import org.hibernate.query.sqm.sql.SqmTranslator;
|
||||
import org.hibernate.query.sqm.sql.SqmTranslatorFactory;
|
||||
import org.hibernate.query.sqm.tree.SqmStatement;
|
||||
import org.hibernate.query.sqm.tree.select.SqmSelectStatement;
|
||||
import org.hibernate.sql.ast.tree.from.TableGroup;
|
||||
import org.hibernate.sql.ast.tree.from.TableGroupJoin;
|
||||
import org.hibernate.sql.ast.tree.select.SelectStatement;
|
||||
|
||||
import org.hibernate.testing.TestForIssue;
|
||||
import org.hibernate.testing.orm.junit.DomainModel;
|
||||
import org.hibernate.testing.orm.junit.SessionFactory;
|
||||
import org.hibernate.testing.orm.junit.SessionFactoryScope;
|
||||
import org.hibernate.testing.orm.junit.SkipForDialect;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.hamcrest.core.Is.is;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
/**
|
||||
* @author Steve Ebersole, Jan Martiska
|
||||
* @author Christian Beikov
|
||||
*/
|
||||
@DomainModel( annotatedClasses = { EntityJoinTest.FinancialRecord.class, EntityJoinTest.User.class, EntityJoinTest.Customer.class } )
|
||||
@SessionFactory
|
||||
public class EntityJoinTest {
|
||||
|
||||
@Test
|
||||
public void testInnerEntityJoins(SessionFactoryScope scope) {
|
||||
scope.inTransaction(
|
||||
(session) -> {
|
||||
// this should get financial records which have a lastUpdateBy user set
|
||||
List<Object[]> result = session.createQuery(
|
||||
"select r.id, c.name, u.id, u.username " +
|
||||
"from FinancialRecord r " +
|
||||
" inner join r.customer c " +
|
||||
" inner join User u on r.lastUpdateBy = u.username",
|
||||
Object[].class
|
||||
).list();
|
||||
|
||||
assertThat( result.size(), is( 1 ) );
|
||||
Object[] steveAndAcme = result.get( 0 );
|
||||
assertThat( steveAndAcme[0], is( 1 ) );
|
||||
assertThat( steveAndAcme[1], is( "Acme" ) );
|
||||
assertThat( steveAndAcme[3], is( "steve" ) );
|
||||
|
||||
// NOTE that this leads to not really valid SQL, although some databases might support it /
|
||||
// result = session.createQuery(
|
||||
// "select r.id, r.customer.name, u.id, u.username " +
|
||||
// "from FinancialRecord r " +
|
||||
// " inner join User u on r.lastUpdateBy = u.username"
|
||||
// ).list();
|
||||
// assertThat( result.size(), is( 1 ) );
|
||||
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLeftOuterEntityJoins(SessionFactoryScope scope) {
|
||||
scope.inTransaction(
|
||||
(session) -> {
|
||||
// this should get all financial records even if their lastUpdateBy user is null
|
||||
List<Object[]> result = session.createQuery(
|
||||
"select r.id, u.id, u.username " +
|
||||
"from FinancialRecord r " +
|
||||
" left join User u on r.lastUpdateBy = u.username" +
|
||||
" order by r.id",
|
||||
Object[].class
|
||||
).list();
|
||||
assertThat( result.size(), is( 2 ) );
|
||||
|
||||
Object[] stevesRecord = result.get( 0 );
|
||||
assertThat( stevesRecord[0], is( 1 ) );
|
||||
assertThat( stevesRecord[2], is( "steve" ) );
|
||||
|
||||
Object[] noOnesRecord = result.get( 1 );
|
||||
assertThat( noOnesRecord[0], is( 2 ) );
|
||||
assertNull( noOnesRecord[2] );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestForIssue(jiraKey = "HHH-11337")
|
||||
@SkipForDialect( dialectClass = SybaseDialect.class )
|
||||
public void testLeftOuterEntityJoinsWithImplicitInnerJoinInSelectClause(SessionFactoryScope scope) {
|
||||
scope.inTransaction(
|
||||
(session) -> {
|
||||
// this should get all financial records even if their lastUpdateBy user is null
|
||||
List<Object[]> result = session.createQuery(
|
||||
"select r.id, u.id, u.username, r.customer.name " +
|
||||
"from FinancialRecord r " +
|
||||
" left join User u on r.lastUpdateBy = u.username" +
|
||||
" order by r.id",
|
||||
Object[].class
|
||||
).list();
|
||||
assertThat( result.size(), is( 2 ) );
|
||||
|
||||
Object[] stevesRecord = result.get( 0 );
|
||||
assertThat( stevesRecord[0], is( 1 ) );
|
||||
assertThat( stevesRecord[2], is( "steve" ) );
|
||||
|
||||
Object[] noOnesRecord = result.get( 1 );
|
||||
assertThat( noOnesRecord[0], is( 2 ) );
|
||||
assertNull( noOnesRecord[2] );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestForIssue(jiraKey = "HHH-11340")
|
||||
public void testJoinOnEntityJoinNode(SessionFactoryScope scope) {
|
||||
scope.inTransaction(
|
||||
(session) -> {
|
||||
// this should get all financial records even if their lastUpdateBy user is null
|
||||
List<Object[]> result = session.createQuery(
|
||||
"select u.username, c.name " +
|
||||
"from FinancialRecord r " +
|
||||
" left join User u on r.lastUpdateBy = u.username " +
|
||||
" left join u.customer c " +
|
||||
" order by r.id",
|
||||
Object[].class
|
||||
).list();
|
||||
assertThat( result.size(), is( 2 ) );
|
||||
|
||||
Object[] stevesRecord = result.get( 0 );
|
||||
assertThat( stevesRecord[0], is( "steve" ) );
|
||||
assertThat( stevesRecord[1], is( "Acme" ) );
|
||||
|
||||
Object[] noOnesRecord = result.get( 1 );
|
||||
assertNull( noOnesRecord[0] );
|
||||
assertNull( noOnesRecord[1] );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestForIssue(jiraKey = "HHH-11538")
|
||||
public void testNoImpliedJoinGeneratedForEqualityComparison(SessionFactoryScope scope) {
|
||||
final String qry = "select r.id, cust.name " +
|
||||
"from FinancialRecord r " +
|
||||
" join Customer cust on r.customer = cust" +
|
||||
" order by r.id";
|
||||
|
||||
scope.inTransaction(
|
||||
(session) -> {
|
||||
final SessionFactoryImplementor factory = scope.getSessionFactory();
|
||||
|
||||
final EntityMappingType customerEntityDescriptor = factory.getRuntimeMetamodels()
|
||||
.getMappingMetamodel()
|
||||
.findEntityDescriptor( Customer.class );
|
||||
|
||||
final QueryEngine queryEngine = factory.getQueryEngine();
|
||||
final HqlTranslator hqlTranslator = queryEngine.getHqlTranslator();
|
||||
final SqmTranslatorFactory sqmTranslatorFactory = queryEngine.getSqmTranslatorFactory();
|
||||
|
||||
final SqmStatement<Object> sqm = hqlTranslator.translate( qry );
|
||||
|
||||
final SqmTranslator<SelectStatement> selectTranslator = sqmTranslatorFactory.createSelectTranslator(
|
||||
(SqmSelectStatement<?>) sqm,
|
||||
QueryOptions.NONE,
|
||||
DomainParameterXref.empty(),
|
||||
QueryParameterBindings.NO_PARAM_BINDINGS,
|
||||
LoadQueryInfluencers.NONE,
|
||||
factory
|
||||
);
|
||||
final SqmTranslation<SelectStatement> sqmTranslation = selectTranslator.translate();
|
||||
|
||||
final SelectStatement sqlAst = sqmTranslation.getSqlAst();
|
||||
final List<TableGroup> roots = sqlAst.getQuerySpec().getFromClause().getRoots();
|
||||
assertThat( roots.size(), is( 1 ) );
|
||||
|
||||
final TableGroup rootTableGroup = roots.get( 0 );
|
||||
assertThat( rootTableGroup.getTableGroupJoins().size(), is( 1 ) );
|
||||
|
||||
final TableGroupJoin tableGroupJoin = rootTableGroup.getTableGroupJoins().get( 0 );
|
||||
assertThat( tableGroupJoin.getJoinedGroup().getModelPart(), is( customerEntityDescriptor ) );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRightOuterEntityJoins(SessionFactoryScope scope) {
|
||||
scope.inTransaction(
|
||||
(session) -> {
|
||||
// this should get all users even if they have no financial records
|
||||
List<Object[]> result = session.createQuery(
|
||||
"select r.id, u.id, u.username " +
|
||||
"from FinancialRecord r " +
|
||||
" right join User u on r.lastUpdateBy = u.username" +
|
||||
" order by u.id",
|
||||
Object[].class
|
||||
).list();
|
||||
|
||||
assertThat( result.size(), is( 2 ) );
|
||||
|
||||
Object[] steveAndAcme = result.get( 0 );
|
||||
assertThat( steveAndAcme[ 0 ], is( 1 ) );
|
||||
assertThat( steveAndAcme[ 2 ], is( "steve" ) );
|
||||
|
||||
Object[] janeAndNull = result.get( 1 );
|
||||
assertNull( janeAndNull[ 0 ] );
|
||||
assertThat( janeAndNull[ 2 ], is( "jane" ) );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@BeforeEach
|
||||
public void createTestData(SessionFactoryScope scope) {
|
||||
scope.inTransaction(
|
||||
(session) -> {
|
||||
final Customer customer = new Customer( 1, "Acme" );
|
||||
session.save( customer );
|
||||
session.save( new User( 1, "steve", customer ) );
|
||||
session.save( new User( 2, "jane" ) );
|
||||
session.save( new FinancialRecord( 1, customer, "steve" ) );
|
||||
session.save( new FinancialRecord( 2, customer, null ) );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
public void dropTestData(SessionFactoryScope scope) {
|
||||
scope.inTransaction(
|
||||
(session) -> {
|
||||
session.createQuery( "delete FinancialRecord" ).executeUpdate();
|
||||
session.createQuery( "delete User" ).executeUpdate();
|
||||
session.createQuery( "delete Customer" ).executeUpdate();
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Entity(name = "Customer")
|
||||
@Table(name = "`a:customer`")
|
||||
public static class Customer {
|
||||
private Integer id;
|
||||
private String name;
|
||||
|
||||
public Customer() {
|
||||
}
|
||||
|
||||
public Customer(Integer id, String name) {
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@Id
|
||||
public Integer getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Integer id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
}
|
||||
|
||||
@Entity(name = "FinancialRecord")
|
||||
@Table(name = "`a:financial_record`")
|
||||
public static class FinancialRecord {
|
||||
private Integer id;
|
||||
private Customer customer;
|
||||
private String lastUpdateBy;
|
||||
|
||||
public FinancialRecord() {
|
||||
}
|
||||
|
||||
public FinancialRecord(Integer id, Customer customer, String lastUpdateBy) {
|
||||
this.id = id;
|
||||
this.customer = customer;
|
||||
this.lastUpdateBy = lastUpdateBy;
|
||||
}
|
||||
|
||||
@Id
|
||||
public Integer getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Integer id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
@ManyToOne
|
||||
@JoinColumn
|
||||
public Customer getCustomer() {
|
||||
return customer;
|
||||
}
|
||||
|
||||
public void setCustomer(Customer customer) {
|
||||
this.customer = customer;
|
||||
}
|
||||
|
||||
public String getLastUpdateBy() {
|
||||
return lastUpdateBy;
|
||||
}
|
||||
|
||||
public void setLastUpdateBy(String lastUpdateBy) {
|
||||
this.lastUpdateBy = lastUpdateBy;
|
||||
}
|
||||
}
|
||||
|
||||
@Entity(name = "User")
|
||||
@Table(name = "`a:user`")
|
||||
public static class User {
|
||||
private Integer id;
|
||||
private String username;
|
||||
private Customer customer;
|
||||
|
||||
public User() {
|
||||
}
|
||||
|
||||
public User(Integer id, String username) {
|
||||
this.id = id;
|
||||
this.username = username;
|
||||
}
|
||||
|
||||
public User(Integer id, String username, Customer customer) {
|
||||
this.id = id;
|
||||
this.username = username;
|
||||
this.customer = customer;
|
||||
}
|
||||
|
||||
@Id
|
||||
public Integer getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Integer id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
@NaturalId
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
|
||||
public void setUsername(String username) {
|
||||
this.username = username;
|
||||
}
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
public Customer getCustomer() {
|
||||
return customer;
|
||||
}
|
||||
|
||||
public void setCustomer(Customer customer) {
|
||||
this.customer = customer;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -1,350 +0,0 @@
|
|||
/*
|
||||
* Hibernate, Relational Persistence for Idiomatic Java
|
||||
*
|
||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
||||
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
||||
*/
|
||||
package org.hibernate.test.hql;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.FetchType;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.JoinColumn;
|
||||
import javax.persistence.ManyToOne;
|
||||
import javax.persistence.Table;
|
||||
|
||||
import org.hibernate.NotYetImplementedFor6Exception;
|
||||
import org.hibernate.annotations.NaturalId;
|
||||
import org.hibernate.dialect.SybaseDialect;
|
||||
|
||||
import org.hibernate.testing.SkipForDialect;
|
||||
import org.hibernate.testing.TestForIssue;
|
||||
import org.hibernate.testing.junit4.BaseNonConfigCoreFunctionalTestCase;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.hamcrest.core.Is.is;
|
||||
import static org.hibernate.testing.transaction.TransactionUtil.doInHibernate;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotEquals;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
/**
|
||||
* @author Steve Ebersole, Jan Martiska
|
||||
* @author Christian Beikov
|
||||
*/
|
||||
public class EntityJoinTest extends BaseNonConfigCoreFunctionalTestCase {
|
||||
@Override
|
||||
protected Class[] getAnnotatedClasses() {
|
||||
return new Class[] {FinancialRecord.class, User.class, Customer.class};
|
||||
}
|
||||
|
||||
@Before
|
||||
public void prepare() {
|
||||
createTestData();
|
||||
}
|
||||
|
||||
@After
|
||||
public void cleanup() {
|
||||
deleteTestData();
|
||||
}
|
||||
|
||||
@Test()
|
||||
public void testInnerEntityJoins() {
|
||||
doInHibernate( this::sessionFactory, session -> {
|
||||
|
||||
// this should get financial records which have a lastUpdateBy user set
|
||||
List<Object[]> result = session.createQuery(
|
||||
"select r.id, c.name, u.id, u.username " +
|
||||
"from FinancialRecord r " +
|
||||
" inner join r.customer c " +
|
||||
" inner join User u on r.lastUpdateBy = u.username"
|
||||
).list();
|
||||
|
||||
assertThat( result.size(), is( 1 ) );
|
||||
Object[] steveAndAcme = result.get( 0 );
|
||||
assertThat( steveAndAcme[0], is( 1 ) );
|
||||
assertThat( steveAndAcme[1], is( "Acme" ) );
|
||||
assertThat( steveAndAcme[3], is( "steve" ) );
|
||||
|
||||
// NOTE that this leads to not really valid SQL, although some databases might support it /
|
||||
// result = session.createQuery(
|
||||
// "select r.id, r.customer.name, u.id, u.username " +
|
||||
// "from FinancialRecord r " +
|
||||
// " inner join User u on r.lastUpdateBy = u.username"
|
||||
// ).list();
|
||||
// assertThat( result.size(), is( 1 ) );
|
||||
|
||||
} );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLeftOuterEntityJoins() {
|
||||
doInHibernate( this::sessionFactory, session -> {
|
||||
// this should get all financial records even if their lastUpdateBy user is null
|
||||
List<Object[]> result = session.createQuery(
|
||||
"select r.id, u.id, u.username " +
|
||||
"from FinancialRecord r " +
|
||||
" left join User u on r.lastUpdateBy = u.username" +
|
||||
" order by r.id"
|
||||
).list();
|
||||
assertThat( result.size(), is( 2 ) );
|
||||
|
||||
Object[] stevesRecord = result.get( 0 );
|
||||
assertThat( stevesRecord[0], is( 1 ) );
|
||||
assertThat( stevesRecord[2], is( "steve" ) );
|
||||
|
||||
Object[] noOnesRecord = result.get( 1 );
|
||||
assertThat( noOnesRecord[0], is( 2 ) );
|
||||
assertNull( noOnesRecord[2] );
|
||||
} );
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestForIssue(jiraKey = "HHH-11337")
|
||||
@SkipForDialect(SybaseDialect.class)
|
||||
public void testLeftOuterEntityJoinsWithImplicitInnerJoinInSelectClause() {
|
||||
doInHibernate( this::sessionFactory, session -> {
|
||||
// this should get all financial records even if their lastUpdateBy user is null
|
||||
List<Object[]> result = session.createQuery(
|
||||
"select r.id, u.id, u.username, r.customer.name " +
|
||||
"from FinancialRecord r " +
|
||||
" left join User u on r.lastUpdateBy = u.username" +
|
||||
" order by r.id"
|
||||
).list();
|
||||
assertThat( result.size(), is( 2 ) );
|
||||
|
||||
Object[] stevesRecord = result.get( 0 );
|
||||
assertThat( stevesRecord[0], is( 1 ) );
|
||||
assertThat( stevesRecord[2], is( "steve" ) );
|
||||
|
||||
Object[] noOnesRecord = result.get( 1 );
|
||||
assertThat( noOnesRecord[0], is( 2 ) );
|
||||
assertNull( noOnesRecord[2] );
|
||||
} );
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestForIssue(jiraKey = "HHH-11340")
|
||||
public void testJoinOnEntityJoinNode() {
|
||||
doInHibernate( this::sessionFactory, session -> {
|
||||
// this should get all financial records even if their lastUpdateBy user is null
|
||||
List<Object[]> result = session.createQuery(
|
||||
"select u.username, c.name " +
|
||||
"from FinancialRecord r " +
|
||||
" left join User u on r.lastUpdateBy = u.username " +
|
||||
" left join u.customer c " +
|
||||
" order by r.id"
|
||||
).list();
|
||||
assertThat( result.size(), is( 2 ) );
|
||||
|
||||
Object[] stevesRecord = result.get( 0 );
|
||||
assertThat( stevesRecord[0], is( "steve" ) );
|
||||
assertThat( stevesRecord[1], is( "Acme" ) );
|
||||
|
||||
Object[] noOnesRecord = result.get( 1 );
|
||||
assertNull( noOnesRecord[0] );
|
||||
assertNull( noOnesRecord[1] );
|
||||
} );
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestForIssue(jiraKey = "HHH-11538")
|
||||
public void testNoImpliedJoinGeneratedForEqualityComparison() {
|
||||
// doInHibernate( this::sessionFactory, session -> {
|
||||
// final HQLQueryPlan plan = sessionFactory().getQueryInterpretationCache().getHQLQueryPlan(
|
||||
// "select r.id, cust.name " +
|
||||
// "from FinancialRecord r " +
|
||||
// " join Customer cust on r.customer = cust" +
|
||||
// " order by r.id",
|
||||
// false,
|
||||
// Collections.EMPTY_MAP
|
||||
// );
|
||||
// assertEquals( 1, plan.getTranslators().length );
|
||||
// final QueryTranslator translator = plan.getTranslators()[0];
|
||||
// final String generatedSql = translator.getSQLString();
|
||||
//
|
||||
// int tableReferenceIndex = generatedSql.indexOf( " customer " );
|
||||
// assertNotEquals("Generated SQL doesn't contain a table reference for customer", -1, tableReferenceIndex );
|
||||
// int nextTableReferenceIndex = generatedSql.indexOf( " customer ", tableReferenceIndex + 1 );
|
||||
// assertEquals("Generated SQL wrongly joined customer twice", -1, nextTableReferenceIndex );
|
||||
// } );
|
||||
throw new NotYetImplementedFor6Exception( getClass() );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRightOuterEntityJoins() {
|
||||
doInHibernate( this::sessionFactory, session -> {
|
||||
// this should get all users even if they have no financial records
|
||||
List<Object[]> result = session.createQuery(
|
||||
"select r.id, u.id, u.username " +
|
||||
"from FinancialRecord r " +
|
||||
" right join User u on r.lastUpdateBy = u.username" +
|
||||
" order by u.id"
|
||||
).list();
|
||||
|
||||
assertThat( result.size(), is( 2 ) );
|
||||
|
||||
Object[] steveAndAcme = result.get( 0 );
|
||||
assertThat( steveAndAcme[0], is( 1 ) );
|
||||
assertThat( steveAndAcme[2], is( "steve" ) );
|
||||
|
||||
Object[] janeAndNull = result.get( 1 );
|
||||
assertNull( janeAndNull[0] );
|
||||
assertThat( janeAndNull[2], is( "jane" ) );
|
||||
} );
|
||||
}
|
||||
|
||||
private void createTestData() {
|
||||
doInHibernate( this::sessionFactory, session -> {
|
||||
|
||||
final Customer customer = new Customer( 1, "Acme" );
|
||||
session.save( customer );
|
||||
session.save( new User( 1, "steve", customer ) );
|
||||
session.save( new User( 2, "jane" ) );
|
||||
session.save( new FinancialRecord( 1, customer, "steve" ) );
|
||||
session.save( new FinancialRecord( 2, customer, null ) );
|
||||
|
||||
} );
|
||||
}
|
||||
|
||||
private void deleteTestData() {
|
||||
doInHibernate( this::sessionFactory, session -> {
|
||||
session.createQuery( "delete FinancialRecord" ).executeUpdate();
|
||||
session.createQuery( "delete User" ).executeUpdate();
|
||||
session.createQuery( "delete Customer" ).executeUpdate();
|
||||
|
||||
} );
|
||||
}
|
||||
|
||||
@Entity(name = "Customer")
|
||||
@Table(name = "customer")
|
||||
public static class Customer {
|
||||
private Integer id;
|
||||
private String name;
|
||||
|
||||
public Customer() {
|
||||
}
|
||||
|
||||
public Customer(Integer id, String name) {
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@Id
|
||||
public Integer getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Integer id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
}
|
||||
|
||||
@Entity(name = "FinancialRecord")
|
||||
@Table(name = "financial_record")
|
||||
public static class FinancialRecord {
|
||||
private Integer id;
|
||||
private Customer customer;
|
||||
private String lastUpdateBy;
|
||||
|
||||
public FinancialRecord() {
|
||||
}
|
||||
|
||||
public FinancialRecord(Integer id, Customer customer, String lastUpdateBy) {
|
||||
this.id = id;
|
||||
this.customer = customer;
|
||||
this.lastUpdateBy = lastUpdateBy;
|
||||
}
|
||||
|
||||
@Id
|
||||
public Integer getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Integer id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
@ManyToOne
|
||||
@JoinColumn
|
||||
public Customer getCustomer() {
|
||||
return customer;
|
||||
}
|
||||
|
||||
public void setCustomer(Customer customer) {
|
||||
this.customer = customer;
|
||||
}
|
||||
|
||||
public String getLastUpdateBy() {
|
||||
return lastUpdateBy;
|
||||
}
|
||||
|
||||
public void setLastUpdateBy(String lastUpdateBy) {
|
||||
this.lastUpdateBy = lastUpdateBy;
|
||||
}
|
||||
}
|
||||
|
||||
@Entity(name = "User")
|
||||
@Table(name = "`user`")
|
||||
public static class User {
|
||||
private Integer id;
|
||||
private String username;
|
||||
private Customer customer;
|
||||
|
||||
public User() {
|
||||
}
|
||||
|
||||
public User(Integer id, String username) {
|
||||
this.id = id;
|
||||
this.username = username;
|
||||
}
|
||||
|
||||
public User(Integer id, String username, Customer customer) {
|
||||
this.id = id;
|
||||
this.username = username;
|
||||
this.customer = customer;
|
||||
}
|
||||
|
||||
@Id
|
||||
public Integer getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Integer id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
@NaturalId
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
|
||||
public void setUsername(String username) {
|
||||
this.username = username;
|
||||
}
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
public Customer getCustomer() {
|
||||
return customer;
|
||||
}
|
||||
|
||||
public void setCustomer(Customer customer) {
|
||||
this.customer = customer;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -1,302 +0,0 @@
|
|||
/*
|
||||
* Hibernate, Relational Persistence for Idiomatic Java
|
||||
*
|
||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
||||
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
||||
*/
|
||||
package org.hibernate.test.hql;
|
||||
|
||||
import static org.hamcrest.core.Is.is;
|
||||
import static org.hibernate.testing.transaction.TransactionUtil.doInHibernate;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.FetchType;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.JoinColumn;
|
||||
import javax.persistence.ManyToOne;
|
||||
import javax.persistence.Table;
|
||||
|
||||
import org.hibernate.annotations.NaturalId;
|
||||
import org.hibernate.testing.Skip;
|
||||
import org.hibernate.testing.TestForIssue;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
* @author Jonathan Bregler
|
||||
*/
|
||||
public class EntityWithUnusualTableNameJoinTest extends EntityJoinTest {
|
||||
|
||||
@Override
|
||||
protected Class[] getAnnotatedClasses() {
|
||||
return new Class[]{ FinancialRecord.class, User.class, Customer.class, Account.class };
|
||||
}
|
||||
|
||||
@Override
|
||||
@Before
|
||||
public void prepare() {
|
||||
createTestData();
|
||||
}
|
||||
|
||||
@Override
|
||||
@After
|
||||
public void cleanup() {
|
||||
deleteTestData();
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestForIssue(jiraKey = "HHH-11816")
|
||||
public void testInnerEntityJoinsWithVariable() {
|
||||
doInHibernate( this::sessionFactory, session -> {
|
||||
|
||||
// this should get financial records which have a lastUpdateBy user set
|
||||
List<Object[]> result = session.createQuery(
|
||||
"select r.id, c.name, u.id, u.username " +
|
||||
"from FinancialRecord r " +
|
||||
" inner join r.customer c " +
|
||||
" inner join User u on r.lastUpdateBy = u.username and u.username=:username" )
|
||||
.setParameter( "username", "steve" ).list();
|
||||
|
||||
assertThat( Integer.valueOf( result.size() ), is( Integer.valueOf( 1 ) ) );
|
||||
Object[] steveAndAcme = result.get( 0 );
|
||||
assertThat( steveAndAcme[0], is( Integer.valueOf( 1 ) ) );
|
||||
assertThat( steveAndAcme[1], is( "Acme" ) );
|
||||
assertThat( steveAndAcme[3], is( "steve" ) );
|
||||
|
||||
} );
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestForIssue(jiraKey = "HHH-11816")
|
||||
public void testInnerEntityJoinsWithVariableSingleQuoted() {
|
||||
doInHibernate( this::sessionFactory, session -> {
|
||||
|
||||
// this should get financial records which have a lastUpdateBy user set
|
||||
List<Object[]> result = session.createQuery(
|
||||
"select r.id, c.name, a.id, a.accountname, r.lastUpdateBy " +
|
||||
"from FinancialRecord r " +
|
||||
" inner join r.customer c " +
|
||||
" inner join Account a on a.customer = c and a.accountname!='test:account' and a.accountname=:accountname and r.lastUpdateBy != null" )
|
||||
.setParameter( "accountname", "DEBIT" ).list();
|
||||
|
||||
assertThat( Integer.valueOf( result.size() ), is( Integer.valueOf( 1 ) ) );
|
||||
Object[] steveAndAcmeAndDebit = result.get( 0 );
|
||||
assertThat( steveAndAcmeAndDebit[0], is( Integer.valueOf( 1 ) ) );
|
||||
assertThat( steveAndAcmeAndDebit[1], is( "Acme" ) );
|
||||
assertThat( steveAndAcmeAndDebit[3], is( "DEBIT" ) );
|
||||
assertThat( steveAndAcmeAndDebit[4], is( "steve" ) );
|
||||
} );
|
||||
}
|
||||
|
||||
@Override
|
||||
@Skip(message = "The superclass test checks for the table name which is different in this test case and causes the test to fail", condition = Skip.AlwaysSkip.class)
|
||||
public void testNoImpliedJoinGeneratedForEqualityComparison() {
|
||||
}
|
||||
|
||||
private void createTestData() {
|
||||
doInHibernate( this::sessionFactory, session -> {
|
||||
|
||||
final Customer customer = new Customer( Integer.valueOf( 1 ), "Acme" );
|
||||
session.save( customer );
|
||||
session.save( new User( Integer.valueOf( 1 ), "steve", customer ) );
|
||||
session.save( new User( Integer.valueOf( 2 ), "jane" ) );
|
||||
session.save( new FinancialRecord( Integer.valueOf( 1 ), customer, "steve" ) );
|
||||
session.save( new FinancialRecord( Integer.valueOf( 2 ), customer, null ) );
|
||||
session.save( new Account( Integer.valueOf( 1 ), "DEBIT", customer ) );
|
||||
session.save( new Account( Integer.valueOf( 2 ), "CREDIT" ) );
|
||||
} );
|
||||
}
|
||||
|
||||
private void deleteTestData() {
|
||||
doInHibernate( this::sessionFactory, session -> {
|
||||
session.createQuery( "delete FinancialRecord" ).executeUpdate();
|
||||
session.createQuery( "delete User" ).executeUpdate();
|
||||
session.createQuery( "delete Account" ).executeUpdate();
|
||||
session.createQuery( "delete Customer" ).executeUpdate();
|
||||
|
||||
} );
|
||||
}
|
||||
|
||||
@Entity(name = "Customer")
|
||||
@Table(name = "`my::customer`")
|
||||
public static class Customer {
|
||||
|
||||
private Integer id;
|
||||
private String name;
|
||||
|
||||
public Customer() {
|
||||
}
|
||||
|
||||
public Customer(Integer id, String name) {
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@Id
|
||||
public Integer getId() {
|
||||
return this.id;
|
||||
}
|
||||
|
||||
public void setId(Integer id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
}
|
||||
|
||||
@Entity(name = "FinancialRecord")
|
||||
@Table(name = "`financial?record`")
|
||||
public static class FinancialRecord {
|
||||
|
||||
private Integer id;
|
||||
private Customer customer;
|
||||
private String lastUpdateBy;
|
||||
|
||||
public FinancialRecord() {
|
||||
}
|
||||
|
||||
public FinancialRecord(Integer id, Customer customer, String lastUpdateBy) {
|
||||
this.id = id;
|
||||
this.customer = customer;
|
||||
this.lastUpdateBy = lastUpdateBy;
|
||||
}
|
||||
|
||||
@Id
|
||||
public Integer getId() {
|
||||
return this.id;
|
||||
}
|
||||
|
||||
public void setId(Integer id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
@ManyToOne
|
||||
@JoinColumn
|
||||
public Customer getCustomer() {
|
||||
return this.customer;
|
||||
}
|
||||
|
||||
public void setCustomer(Customer customer) {
|
||||
this.customer = customer;
|
||||
}
|
||||
|
||||
public String getLastUpdateBy() {
|
||||
return this.lastUpdateBy;
|
||||
}
|
||||
|
||||
public void setLastUpdateBy(String lastUpdateBy) {
|
||||
this.lastUpdateBy = lastUpdateBy;
|
||||
}
|
||||
}
|
||||
|
||||
@Entity(name = "User")
|
||||
@Table(name = "`my::user`")
|
||||
public static class User {
|
||||
|
||||
private Integer id;
|
||||
private String username;
|
||||
private Customer customer;
|
||||
|
||||
public User() {
|
||||
}
|
||||
|
||||
public User(Integer id, String username) {
|
||||
this.id = id;
|
||||
this.username = username;
|
||||
}
|
||||
|
||||
public User(Integer id, String username, Customer customer) {
|
||||
this.id = id;
|
||||
this.username = username;
|
||||
this.customer = customer;
|
||||
}
|
||||
|
||||
@Id
|
||||
public Integer getId() {
|
||||
return this.id;
|
||||
}
|
||||
|
||||
public void setId(Integer id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
@NaturalId
|
||||
public String getUsername() {
|
||||
return this.username;
|
||||
}
|
||||
|
||||
public void setUsername(String username) {
|
||||
this.username = username;
|
||||
}
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
public Customer getCustomer() {
|
||||
return this.customer;
|
||||
}
|
||||
|
||||
public void setCustomer(Customer customer) {
|
||||
this.customer = customer;
|
||||
}
|
||||
}
|
||||
|
||||
@Entity(name = "Account")
|
||||
@Table(name = "`account`")
|
||||
public static class Account {
|
||||
|
||||
private Integer id;
|
||||
private String accountname;
|
||||
private Customer customer;
|
||||
|
||||
public Account() {
|
||||
}
|
||||
|
||||
public Account(Integer id, String accountname) {
|
||||
this.id = id;
|
||||
this.accountname = accountname;
|
||||
}
|
||||
|
||||
public Account(Integer id, String accountname, Customer customer) {
|
||||
this.id = id;
|
||||
this.accountname = accountname;
|
||||
this.customer = customer;
|
||||
}
|
||||
|
||||
@Id
|
||||
public Integer getId() {
|
||||
return this.id;
|
||||
}
|
||||
|
||||
public void setId(Integer id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
@NaturalId
|
||||
public String getAccountname() {
|
||||
return this.accountname;
|
||||
}
|
||||
|
||||
public void setAccountname(String accountname) {
|
||||
this.accountname = accountname;
|
||||
}
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
public Customer getCustomer() {
|
||||
return this.customer;
|
||||
}
|
||||
|
||||
public void setCustomer(Customer customer) {
|
||||
this.customer = customer;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue