HHH-16413 Add test for issue
This commit is contained in:
parent
288242a10f
commit
f6e3a56b8e
|
@ -0,0 +1,225 @@
|
||||||
|
/*
|
||||||
|
* 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.jpa.criteria.subquery;
|
||||||
|
|
||||||
|
import org.hibernate.testing.orm.junit.DomainModel;
|
||||||
|
import org.hibernate.testing.orm.junit.Jira;
|
||||||
|
import org.hibernate.testing.orm.junit.SessionFactory;
|
||||||
|
import org.hibernate.testing.orm.junit.SessionFactoryScope;
|
||||||
|
import org.junit.jupiter.api.AfterAll;
|
||||||
|
import org.junit.jupiter.api.BeforeAll;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import jakarta.persistence.Entity;
|
||||||
|
import jakarta.persistence.Id;
|
||||||
|
import jakarta.persistence.ManyToOne;
|
||||||
|
import jakarta.persistence.criteria.CriteriaBuilder;
|
||||||
|
import jakarta.persistence.criteria.CriteriaQuery;
|
||||||
|
import jakarta.persistence.criteria.Join;
|
||||||
|
import jakarta.persistence.criteria.JoinType;
|
||||||
|
import jakarta.persistence.criteria.Root;
|
||||||
|
import jakarta.persistence.criteria.Subquery;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Andreas Asplund
|
||||||
|
* @author Marco Belladelli
|
||||||
|
*/
|
||||||
|
@DomainModel( annotatedClasses = {
|
||||||
|
SubqueryMultipleLeftJoinsTest.MyUnrelatedEntity.class,
|
||||||
|
SubqueryMultipleLeftJoinsTest.MyEntity.class,
|
||||||
|
SubqueryMultipleLeftJoinsTest.AnotherEntity.class,
|
||||||
|
SubqueryMultipleLeftJoinsTest.AgainAnotherEntity.class
|
||||||
|
} )
|
||||||
|
@SessionFactory
|
||||||
|
@Jira( "https://hibernate.atlassian.net/browse/HHH-16413" )
|
||||||
|
public class SubqueryMultipleLeftJoinsTest {
|
||||||
|
private static final long ENTITY_WITH_ASSOCIATION_ID_1 = 1L;
|
||||||
|
private static final long ENTITY_WITH_ASSOCIATION_ID_2 = 2L;
|
||||||
|
private static final long ANOTHER_ENTITY_ID_1 = 3L;
|
||||||
|
private static final long ANOTHER_ENTITY_ID_2 = 4L;
|
||||||
|
private static final long AGAIN_ANOTHER_ENTITY_ID = 5L;
|
||||||
|
|
||||||
|
@BeforeAll
|
||||||
|
public void setUp(SessionFactoryScope scope) {
|
||||||
|
scope.inTransaction( session -> {
|
||||||
|
final AgainAnotherEntity againAnotherEntity = new AgainAnotherEntity(
|
||||||
|
AGAIN_ANOTHER_ENTITY_ID,
|
||||||
|
"again"
|
||||||
|
);
|
||||||
|
session.persist( againAnotherEntity );
|
||||||
|
final AnotherEntity anotherEntity1 = new AnotherEntity(
|
||||||
|
ANOTHER_ENTITY_ID_1,
|
||||||
|
"another 1",
|
||||||
|
true,
|
||||||
|
null
|
||||||
|
);
|
||||||
|
session.persist( anotherEntity1 );
|
||||||
|
final AnotherEntity anotherEntity2 = new AnotherEntity(
|
||||||
|
ANOTHER_ENTITY_ID_2,
|
||||||
|
"another 2",
|
||||||
|
false,
|
||||||
|
againAnotherEntity
|
||||||
|
);
|
||||||
|
session.persist( anotherEntity2 );
|
||||||
|
session.persist( new MyEntity( ENTITY_WITH_ASSOCIATION_ID_1, "without association", anotherEntity1 ) );
|
||||||
|
session.persist( new MyEntity( ENTITY_WITH_ASSOCIATION_ID_2, "with association", anotherEntity2 ) );
|
||||||
|
session.persist( new MyUnrelatedEntity( ENTITY_WITH_ASSOCIATION_ID_1, "unrelated 1" ) );
|
||||||
|
session.persist( new MyUnrelatedEntity( ENTITY_WITH_ASSOCIATION_ID_2, "unrelated 2" ) );
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
|
||||||
|
@AfterAll
|
||||||
|
public void tearDown(SessionFactoryScope scope) {
|
||||||
|
scope.inTransaction( session -> {
|
||||||
|
session.createMutationQuery( "delete from MyUnrelatedEntity" ).executeUpdate();
|
||||||
|
session.createMutationQuery( "delete from MyEntity" ).executeUpdate();
|
||||||
|
session.createMutationQuery( "delete from AnotherEntity" ).executeUpdate();
|
||||||
|
session.createMutationQuery( "delete from AgainAnotherEntity" ).executeUpdate();
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void subqueryWithLeftJoinsCriteriaApi(SessionFactoryScope sessionFactoryScope) {
|
||||||
|
sessionFactoryScope.inTransaction( session -> {
|
||||||
|
final CriteriaBuilder cb = session.getCriteriaBuilder();
|
||||||
|
final CriteriaQuery<MyUnrelatedEntity> cq = cb.createQuery( MyUnrelatedEntity.class );
|
||||||
|
final Root<MyUnrelatedEntity> root = cq.from( MyUnrelatedEntity.class );
|
||||||
|
final Subquery<Long> subquery = cq.subquery( Long.class );
|
||||||
|
final Root<MyEntity> myEntityRoot = subquery.from( MyEntity.class );
|
||||||
|
final Join<MyEntity, AnotherEntity> anotherEntityJoin = myEntityRoot.join(
|
||||||
|
"otherEntity",
|
||||||
|
JoinType.LEFT
|
||||||
|
);
|
||||||
|
final Join<AnotherEntity, AgainAnotherEntity> againAnotherEntityJoin = anotherEntityJoin.join(
|
||||||
|
"otherEntity",
|
||||||
|
JoinType.LEFT
|
||||||
|
);
|
||||||
|
subquery.select( myEntityRoot.get( "id" ) ).where( cb.and(
|
||||||
|
cb.equal( anotherEntityJoin.get( "aString" ), "another 1" ),
|
||||||
|
cb.or(
|
||||||
|
cb.and(
|
||||||
|
cb.equal( anotherEntityJoin.get( "aBoolean" ), false ),
|
||||||
|
cb.equal( againAnotherEntityJoin.get( "aString" ), "again" )
|
||||||
|
),
|
||||||
|
cb.and(
|
||||||
|
// This should be true since "another 1" has no association and the join is LEFT
|
||||||
|
cb.equal( anotherEntityJoin.get( "aBoolean" ), true ),
|
||||||
|
cb.isNull( againAnotherEntityJoin.get( "aString" ) )
|
||||||
|
)
|
||||||
|
)
|
||||||
|
) );
|
||||||
|
final MyUnrelatedEntity result = session.createQuery(
|
||||||
|
cq.select( root ).where( root.get( "id" ).in( subquery ) )
|
||||||
|
).getSingleResult();
|
||||||
|
assertThat( result.getId() ).isEqualTo( 1L );
|
||||||
|
assertThat( result.getaString() ).isEqualTo( "unrelated 1" );
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Entity( name = "MyUnrelatedEntity" )
|
||||||
|
public static class MyUnrelatedEntity {
|
||||||
|
@Id
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
private String aString;
|
||||||
|
|
||||||
|
public MyUnrelatedEntity() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public MyUnrelatedEntity(Long id, String aString) {
|
||||||
|
this.id = id;
|
||||||
|
this.aString = aString;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Long getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getaString() {
|
||||||
|
return aString;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Entity( name = "MyEntity" )
|
||||||
|
public static class MyEntity {
|
||||||
|
@Id
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
private String aString;
|
||||||
|
|
||||||
|
@ManyToOne
|
||||||
|
private AnotherEntity otherEntity;
|
||||||
|
|
||||||
|
public MyEntity() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public MyEntity(Long id, String aString, AnotherEntity otherEntity) {
|
||||||
|
this.id = id;
|
||||||
|
this.aString = aString;
|
||||||
|
this.otherEntity = otherEntity;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Long getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getaString() {
|
||||||
|
return aString;
|
||||||
|
}
|
||||||
|
|
||||||
|
public AnotherEntity getOtherEntity() {
|
||||||
|
return otherEntity;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Entity( name = "AnotherEntity" )
|
||||||
|
public static class AnotherEntity {
|
||||||
|
@Id
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
private String aString;
|
||||||
|
|
||||||
|
private boolean aBoolean;
|
||||||
|
@ManyToOne
|
||||||
|
private AgainAnotherEntity otherEntity;
|
||||||
|
|
||||||
|
public AnotherEntity() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public AnotherEntity(Long id, String aString, boolean aBoolean, AgainAnotherEntity otherEntity) {
|
||||||
|
this.id = id;
|
||||||
|
this.aString = aString;
|
||||||
|
this.otherEntity = otherEntity;
|
||||||
|
this.aBoolean = aBoolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getaString() {
|
||||||
|
return aString;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Entity( name = "AgainAnotherEntity" )
|
||||||
|
public static class AgainAnotherEntity {
|
||||||
|
@Id
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
private String aString;
|
||||||
|
|
||||||
|
@ManyToOne
|
||||||
|
private AnotherEntity otherEntity;
|
||||||
|
|
||||||
|
public AgainAnotherEntity() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public AgainAnotherEntity(Long id, String aString) {
|
||||||
|
this.id = id;
|
||||||
|
this.aString = aString;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue