HHH-15868 Added `crossJoin` methods and logic
This commit is contained in:
parent
977587dd67
commit
2c2ea7163b
|
@ -0,0 +1,16 @@
|
|||
/*
|
||||
* 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.query.criteria;
|
||||
|
||||
import org.hibernate.Incubating;
|
||||
|
||||
/**
|
||||
* @author Marco Belladelli
|
||||
*/
|
||||
@Incubating
|
||||
public interface JpaCrossJoin<T> extends JpaFrom<T, T> {
|
||||
}
|
|
@ -10,13 +10,8 @@ import org.hibernate.Incubating;
|
|||
import org.hibernate.metamodel.model.domain.EntityDomainType;
|
||||
import org.hibernate.query.sqm.tree.SqmJoinType;
|
||||
|
||||
import jakarta.persistence.criteria.CollectionJoin;
|
||||
import jakarta.persistence.criteria.From;
|
||||
import jakarta.persistence.criteria.Join;
|
||||
import jakarta.persistence.criteria.JoinType;
|
||||
import jakarta.persistence.criteria.ListJoin;
|
||||
import jakarta.persistence.criteria.MapJoin;
|
||||
import jakarta.persistence.criteria.SetJoin;
|
||||
import jakarta.persistence.criteria.Subquery;
|
||||
import jakarta.persistence.metamodel.CollectionAttribute;
|
||||
import jakarta.persistence.metamodel.ListAttribute;
|
||||
|
@ -62,6 +57,12 @@ public interface JpaFrom<O,T> extends JpaPath<T>, JpaFetchParent<O,T>, From<O,T>
|
|||
@Incubating
|
||||
<X> JpaJoinedFrom<?, X> join(JpaCteCriteria<X> cte, SqmJoinType joinType);
|
||||
|
||||
@Incubating
|
||||
<X> JpaCrossJoin<X> crossJoin(Class<X> entityJavaType);
|
||||
|
||||
@Incubating
|
||||
<X> JpaCrossJoin<X> crossJoin(EntityDomainType<X> entity);
|
||||
|
||||
// Covariant overrides
|
||||
|
||||
@Override
|
||||
|
|
|
@ -24,6 +24,7 @@ import org.hibernate.metamodel.model.domain.MapPersistentAttribute;
|
|||
import org.hibernate.metamodel.model.domain.PluralPersistentAttribute;
|
||||
import org.hibernate.metamodel.model.domain.SetPersistentAttribute;
|
||||
import org.hibernate.metamodel.model.domain.SingularPersistentAttribute;
|
||||
import org.hibernate.query.criteria.JpaCrossJoin;
|
||||
import org.hibernate.query.criteria.JpaCteCriteria;
|
||||
import org.hibernate.query.criteria.JpaDerivedJoin;
|
||||
import org.hibernate.query.criteria.JpaJoinedFrom;
|
||||
|
@ -622,6 +623,19 @@ public abstract class AbstractSqmFrom<O,T> extends AbstractSqmPath<T> implements
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public <X> JpaCrossJoin<X> crossJoin(Class<X> entityJavaType) {
|
||||
return crossJoin( nodeBuilder().getDomainModel().entity( entityJavaType ) );
|
||||
}
|
||||
|
||||
@Override
|
||||
public <X> JpaCrossJoin<X> crossJoin(EntityDomainType<X> entity) {
|
||||
final SqmCrossJoin<X> crossJoin = new SqmCrossJoin<>( entity, null, findRoot() );
|
||||
// noinspection unchecked
|
||||
addSqmJoin( (SqmJoin<T, ?>) crossJoin );
|
||||
return crossJoin;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Fetch<T, ?>> getFetches() {
|
||||
//noinspection unchecked
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
package org.hibernate.query.sqm.tree.from;
|
||||
|
||||
import org.hibernate.metamodel.model.domain.EntityDomainType;
|
||||
import org.hibernate.query.criteria.JpaCrossJoin;
|
||||
import org.hibernate.spi.NavigablePath;
|
||||
import org.hibernate.query.PathException;
|
||||
import org.hibernate.query.hql.spi.SqmCreationProcessingState;
|
||||
|
@ -24,7 +25,7 @@ import static org.hibernate.query.sqm.spi.SqmCreationHelper.buildRootNavigablePa
|
|||
/**
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public class SqmCrossJoin<T> extends AbstractSqmFrom<T, T> implements SqmJoin<T, T> {
|
||||
public class SqmCrossJoin<T> extends AbstractSqmFrom<T, T> implements JpaCrossJoin<T>, SqmJoin<T, T> {
|
||||
private final SqmRoot<?> sqmRoot;
|
||||
|
||||
public SqmCrossJoin(
|
||||
|
|
|
@ -0,0 +1,110 @@
|
|||
/*
|
||||
* 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.criteria;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
import org.hibernate.query.criteria.HibernateCriteriaBuilder;
|
||||
import org.hibernate.query.criteria.JpaCrossJoin;
|
||||
import org.hibernate.query.criteria.JpaRoot;
|
||||
|
||||
import org.hibernate.testing.orm.domain.gambit.EntityOfBasics;
|
||||
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.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import jakarta.persistence.Tuple;
|
||||
import jakarta.persistence.criteria.CriteriaQuery;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
/**
|
||||
* @author Marco Belladelli
|
||||
*/
|
||||
@DomainModel(annotatedClasses = EntityOfBasics.class)
|
||||
@SessionFactory
|
||||
public class CriteriaCrossJoinTest {
|
||||
|
||||
@BeforeEach
|
||||
public void setUp(SessionFactoryScope scope) {
|
||||
scope.inTransaction( em -> {
|
||||
Date now = new Date();
|
||||
|
||||
EntityOfBasics entity1 = new EntityOfBasics();
|
||||
entity1.setId( 1 );
|
||||
entity1.setTheString( "5" );
|
||||
entity1.setTheInt( 5 );
|
||||
entity1.setTheInteger( -1 );
|
||||
entity1.setTheDouble( 1.0 );
|
||||
entity1.setTheDate( now );
|
||||
entity1.setTheLocalDateTime( LocalDateTime.now() );
|
||||
entity1.setTheBoolean( true );
|
||||
em.persist( entity1 );
|
||||
|
||||
EntityOfBasics entity2 = new EntityOfBasics();
|
||||
entity2.setId( 2 );
|
||||
entity2.setTheString( "6" );
|
||||
entity2.setTheInt( 6 );
|
||||
entity2.setTheInteger( -2 );
|
||||
entity2.setTheDouble( 6.0 );
|
||||
entity2.setTheBoolean( true );
|
||||
em.persist( entity2 );
|
||||
|
||||
EntityOfBasics entity3 = new EntityOfBasics();
|
||||
entity3.setId( 3 );
|
||||
entity3.setTheString( "7" );
|
||||
entity3.setTheInt( 7 );
|
||||
entity3.setTheInteger( 3 );
|
||||
entity3.setTheDouble( 7.0 );
|
||||
entity3.setTheBoolean( false );
|
||||
entity3.setTheDate( new Date( now.getTime() + 200000L ) );
|
||||
em.persist( entity3 );
|
||||
|
||||
EntityOfBasics entity4 = new EntityOfBasics();
|
||||
entity4.setId( 4 );
|
||||
entity4.setTheString( "thirteen" );
|
||||
entity4.setTheInt( 13 );
|
||||
entity4.setTheInteger( 4 );
|
||||
entity4.setTheDouble( 13.0 );
|
||||
entity4.setTheBoolean( false );
|
||||
entity4.setTheDate( new Date( now.getTime() + 300000L ) );
|
||||
em.persist( entity4 );
|
||||
|
||||
EntityOfBasics entity5 = new EntityOfBasics();
|
||||
entity5.setId( 5 );
|
||||
entity5.setTheString( "5" );
|
||||
entity5.setTheInt( 5 );
|
||||
entity5.setTheInteger( 5 );
|
||||
entity5.setTheDouble( 9.0 );
|
||||
entity5.setTheBoolean( false );
|
||||
em.persist( entity5 );
|
||||
} );
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
public void tearDown(SessionFactoryScope scope) {
|
||||
scope.inTransaction( session -> session.createMutationQuery( "delete from EntityOfBasics" ).executeUpdate() );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSimpleCrossJoin(SessionFactoryScope scope) {
|
||||
scope.inTransaction( session -> {
|
||||
HibernateCriteriaBuilder cb = session.getCriteriaBuilder();
|
||||
CriteriaQuery<Tuple> query = cb.createTupleQuery();
|
||||
JpaRoot<EntityOfBasics> from = (JpaRoot<EntityOfBasics>) query.from( EntityOfBasics.class );
|
||||
JpaCrossJoin<EntityOfBasics> crossJoin = from.crossJoin( EntityOfBasics.class );
|
||||
query.multiselect( from.get( "id" ), crossJoin.get( "id" ) ).where( cb.gt( crossJoin.get( "theInt" ), 5 ) );
|
||||
List<Tuple> resultList = session.createQuery( query ).getResultList();
|
||||
assertEquals( 15, resultList.size() );
|
||||
} );
|
||||
}
|
||||
}
|
|
@ -15,7 +15,9 @@ import org.hibernate.dialect.H2Dialect;
|
|||
import org.hibernate.dialect.PostgreSQLDialect;
|
||||
import org.hibernate.dialect.PostgresPlusDialect;
|
||||
import org.hibernate.query.criteria.HibernateCriteriaBuilder;
|
||||
import org.hibernate.query.criteria.JpaCrossJoin;
|
||||
import org.hibernate.query.criteria.JpaExpression;
|
||||
import org.hibernate.query.criteria.JpaRoot;
|
||||
import org.hibernate.query.criteria.JpaWindow;
|
||||
import org.hibernate.query.sqm.NullPrecedence;
|
||||
import org.hibernate.query.sqm.SortOrder;
|
||||
|
@ -309,17 +311,16 @@ public class CriteriaOrderedSetAggregateTest {
|
|||
@RequiresDialectFeature(feature = DialectFeatureChecks.SupportsHypotheticalSetFunctions.class)
|
||||
@RequiresDialect(H2Dialect.class)
|
||||
public void testHypotheticalSetRankWithGroupByHavingOrderByLimit(SessionFactoryScope scope) {
|
||||
// note : cross joins are not supported in criteria and this query structure causes problems with many dbs
|
||||
scope.inTransaction( session -> {
|
||||
HibernateCriteriaBuilder cb = session.getCriteriaBuilder();
|
||||
CriteriaQuery<Tuple> cr = cb.createQuery( Tuple.class );
|
||||
Root<EntityOfBasics> root1 = cr.from( EntityOfBasics.class );
|
||||
Root<EntityOfBasics> root2 = cr.from( EntityOfBasics.class );
|
||||
JpaRoot<EntityOfBasics> e1 = (JpaRoot<EntityOfBasics>) cr.from( EntityOfBasics.class );
|
||||
JpaCrossJoin<EntityOfBasics> e2 = e1.crossJoin( EntityOfBasics.class );
|
||||
|
||||
JpaExpression<Long> function = cb.rank( cb.asc( root1.get( "theInt" ) ), cb.literal( 5 ) );
|
||||
JpaExpression<Long> function = cb.rank( cb.asc( e1.get( "theInt" ) ), cb.literal( 5 ) );
|
||||
|
||||
cr.multiselect( root2.get( "id" ), function )
|
||||
.groupBy( root2.get( "id" ) ).having( cb.gt( root2.get( "id" ), cb.literal( 1 ) ) )
|
||||
cr.multiselect( e2.get( "id" ), function )
|
||||
.groupBy( e2.get( "id" ) ).having( cb.gt( e2.get( "id" ), cb.literal( 1 ) ) )
|
||||
.orderBy( cb.asc( cb.literal( 1 ) ), cb.asc( cb.literal( 2 ) ) );
|
||||
|
||||
List<Tuple> resultList = session.createQuery( cr ).setFirstResult( 1 ).getResultList();
|
||||
|
|
Loading…
Reference in New Issue