Skip to content

Commit

Permalink
HHH-17068 introduce JoinType to replace use of SqmJoinType in criteri…
Browse files Browse the repository at this point in the history
…a API

fis the layer-breakage

Signed-off-by: Gavin King <[email protected]>
  • Loading branch information
gavinking committed Sep 7, 2024
1 parent 75a41d8 commit 10c835d
Show file tree
Hide file tree
Showing 20 changed files with 183 additions and 121 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/*
* 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.common;

/**
* Enumerates the possible kinds of join in HQL and ANSI SQL.
*
* @apiNote This enumeration competes with
* {@link jakarta.persistence.criteria.JoinType},
* adding {@link #FULL} and {@link #CROSS} joins.
*
* @author Gavin King
*
* @since 7
*
* @see jakarta.persistence.criteria.JoinType
*/
public enum JoinType {
/**
* @see jakarta.persistence.criteria.JoinType#INNER
*/
INNER,
/**
* @see jakarta.persistence.criteria.JoinType#LEFT
*/
LEFT,
/**
* @see jakarta.persistence.criteria.JoinType#RIGHT
*/
RIGHT,
FULL,
CROSS
}
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,17 @@ public interface JpaFrom<O,T> extends JpaPath<T>, JpaFetchParent<O,T>, From<O,T>
/**
* @deprecated This method is a layer-breaker, leaking the SQM type
* {@link SqmJoinType} onto an API. It will be removed.
* Use {@link #join(Class, org.hibernate.query.common.JoinType)}
*/
@Deprecated(since="7", forRemoval = true)
default <X> JpaEntityJoin<T, X> join(Class<X> entityJavaType, SqmJoinType joinType) {
return join( entityJavaType, joinType.getCorrespondingJpaJoinType() );
}

default <X> JpaEntityJoin<T, X> join(Class<X> entityJavaType, org.hibernate.query.common.JoinType joinType) {
return join( entityJavaType, SqmJoinType.from(joinType) );

Check notice

Code scanning / CodeQL

Deprecated method or constructor invocation Note

Invoking
JpaFrom.join
should be avoided because it has been deprecated.
}

@Override
<Y> JpaJoin<T, Y> join(EntityType<Y> entity);

Expand All @@ -55,47 +60,72 @@ default <X> JpaEntityJoin<T, X> join(Class<X> entityJavaType, SqmJoinType joinTy
/**
* @deprecated This method is a layer-breaker, leaking the SQM type
* {@link SqmJoinType} onto an API. It will be removed.
* Use {@link #join(EntityDomainType, org.hibernate.query.common.JoinType)}
*/
@Deprecated(since = "7", forRemoval = true)
<X> JpaEntityJoin<T,X> join(EntityDomainType<X> entity, SqmJoinType joinType);

default <X> JpaEntityJoin<T,X> join(EntityDomainType<X> entity, org.hibernate.query.common.JoinType joinType) {
return join( entity, SqmJoinType.from(joinType) );

Check notice

Code scanning / CodeQL

Deprecated method or constructor invocation Note

Invoking
JpaFrom.join
should be avoided because it has been deprecated.
}

@Incubating
<X> JpaDerivedJoin<X> join(Subquery<X> subquery);

/**
* @deprecated This method is a layer-breaker, leaking the SQM type
* {@link SqmJoinType} onto an API. It will be removed.
* Use {@link #join(Subquery, org.hibernate.query.common.JoinType)}
*/
@Incubating @Deprecated(since = "7", forRemoval = true)
<X> JpaDerivedJoin<X> join(Subquery<X> subquery, SqmJoinType joinType);

default <X> JpaDerivedJoin<X> join(Subquery<X> subquery, org.hibernate.query.common.JoinType joinType) {
return join( subquery, SqmJoinType.from(joinType) );

Check notice

Code scanning / CodeQL

Deprecated method or constructor invocation Note

Invoking
JpaFrom.join
should be avoided because it has been deprecated.
}

@Incubating
<X> JpaDerivedJoin<X> joinLateral(Subquery<X> subquery);

/**
* @deprecated This method is a layer-breaker, leaking the SQM type
* {@link SqmJoinType} onto an API. It will be removed.
* Use {@link #joinLateral(Subquery, org.hibernate.query.common.JoinType)}
*/
@Incubating @Deprecated(since = "7", forRemoval = true)
<X> JpaDerivedJoin<X> joinLateral(Subquery<X> subquery, SqmJoinType joinType);

default <X> JpaDerivedJoin<X> joinLateral(Subquery<X> subquery, org.hibernate.query.common.JoinType joinType) {
return joinLateral( subquery, SqmJoinType.from(joinType) );

Check notice

Code scanning / CodeQL

Deprecated method or constructor invocation Note

Invoking
JpaFrom.joinLateral
should be avoided because it has been deprecated.
}

/**
* @deprecated This method is a layer-breaker, leaking the SQM type
* {@link SqmJoinType} onto an API. It will be removed.
* Use {@link #join(Subquery, org.hibernate.query.common.JoinType, boolean)}
*/
@Incubating @Deprecated(since = "7", forRemoval = true)
<X> JpaDerivedJoin<X> join(Subquery<X> subquery, SqmJoinType joinType, boolean lateral);

default <X> JpaDerivedJoin<X> join(Subquery<X> subquery, org.hibernate.query.common.JoinType joinType, boolean lateral) {
return join( subquery, SqmJoinType.from(joinType), lateral );

Check notice

Code scanning / CodeQL

Deprecated method or constructor invocation Note

Invoking
JpaFrom.join
should be avoided because it has been deprecated.
}

@Incubating
<X> JpaJoin<?, X> join(JpaCteCriteria<X> cte);

/**
* @deprecated This method is a layer-breaker, leaking the SQM type
* {@link SqmJoinType} onto an API. It will be removed.
* Use {@link #join(JpaCteCriteria, org.hibernate.query.common.JoinType)}
*/
@Incubating @Deprecated(since = "7", forRemoval = true)
<X> JpaJoin<?, X> join(JpaCteCriteria<X> cte, SqmJoinType joinType);

default <X> JpaJoin<?, X> join(JpaCteCriteria<X> cte, org.hibernate.query.common.JoinType joinType) {
return join( cte, SqmJoinType.from(joinType) );

Check notice

Code scanning / CodeQL

Deprecated method or constructor invocation Note

Invoking
JpaFrom.join
should be avoided because it has been deprecated.
}

@Incubating
<X> JpaCrossJoin<X> crossJoin(Class<X> entityJavaType);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,92 +6,102 @@
*/
package org.hibernate.query.sqm.tree;

import org.hibernate.query.common.JoinType;
import org.hibernate.sql.ast.SqlAstJoinType;

/**
* Represents a canonical join type.
* <p>
* Note that currently HQL really only supports inner and left outer joins
* (though cross joins can also be achieved). This is because joins in HQL
* are always defined in relation to a mapped association. However, when we
* start allowing users to specify ad-hoc joins this may need to change to
* allow the full spectrum of join types. Thus the others are provided here
* currently just for completeness and for future expansion.
*
* @author Steve Ebersole
*
* @see JoinType
* @see SqlAstJoinType
*/
public enum SqmJoinType {
/**
* Represents an inner join.
*/
INNER( "inner", SqlAstJoinType.INNER, jakarta.persistence.criteria.JoinType.INNER ),
INNER,

/**
* Represents a left outer join.
*/
LEFT( "left outer", SqlAstJoinType.LEFT, jakarta.persistence.criteria.JoinType.LEFT ),
LEFT,

/**
* Represents a right outer join.
*/
RIGHT( "right outer", SqlAstJoinType.RIGHT, jakarta.persistence.criteria.JoinType.RIGHT ),
RIGHT,

/**
* Represents a cross join (aka a cartesian product).
*/
CROSS( "cross", SqlAstJoinType.CROSS, null ),
CROSS,

/**
* Represents a full join.
*/
FULL( "full", SqlAstJoinType.FULL, null );

private final String text;
private final SqlAstJoinType correspondingSqlAstJoinType;
private final jakarta.persistence.criteria.JoinType correspondingJpaJoinType;

SqmJoinType(
String text,
SqlAstJoinType correspondingSqlAstJoinType,
jakarta.persistence.criteria.JoinType correspondingJpaJoinType) {
this.text = text;
this.correspondingSqlAstJoinType = correspondingSqlAstJoinType;
this.correspondingJpaJoinType = correspondingJpaJoinType;
}
FULL;

@Override
public String toString() {
return text;
return getText();
}

public String getText() {
return text;
return switch (this) {
case RIGHT -> "right outer";
case LEFT -> "left outer";
case INNER -> "inner";
case FULL -> "full";
case CROSS -> "cross";
};
}

public SqlAstJoinType getCorrespondingSqlJoinType() {
return correspondingSqlAstJoinType;
return switch (this) {
case RIGHT -> SqlAstJoinType.RIGHT;
case LEFT -> SqlAstJoinType.LEFT;
case INNER -> SqlAstJoinType.INNER;
case FULL -> SqlAstJoinType.FULL;
case CROSS -> SqlAstJoinType.CROSS;
};
}

public jakarta.persistence.criteria.JoinType getCorrespondingJpaJoinType() {
return correspondingJpaJoinType;
return switch (this) {
case RIGHT -> jakarta.persistence.criteria.JoinType.RIGHT;
case LEFT -> jakarta.persistence.criteria.JoinType.LEFT;
case INNER -> jakarta.persistence.criteria.JoinType.INNER;
default -> null;
};
}

public JoinType getCorrespondingJoinType() {
return switch (this) {
case RIGHT -> JoinType.RIGHT;
case LEFT -> JoinType.LEFT;
case INNER -> JoinType.INNER;
case FULL -> JoinType.FULL;
case CROSS -> JoinType.CROSS;
};
}

public static SqmJoinType from(JoinType joinType) {
return switch ( joinType ) {
case INNER -> INNER;
case LEFT -> LEFT;
case RIGHT -> RIGHT;
case CROSS -> CROSS;
case FULL -> FULL;
};
}

@SuppressWarnings("DuplicateBranchesInSwitch")
public static SqmJoinType from(jakarta.persistence.criteria.JoinType jpaJoinType) {
switch ( jpaJoinType ) {
case INNER: {
return INNER;
}
case LEFT: {
return LEFT;
}
case RIGHT: {
return RIGHT;
}
default: {
// generally speaking, the default for JPA JoinType is INNER
return INNER;
}
}
return switch ( jpaJoinType ) {
case INNER -> INNER;
case LEFT -> LEFT;
case RIGHT -> RIGHT;
};
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import java.util.List;

import org.hibernate.Session;
import org.hibernate.query.common.JoinType;
import org.hibernate.query.criteria.HibernateCriteriaBuilder;
import org.hibernate.query.criteria.JpaCriteriaQuery;
import org.hibernate.query.criteria.JpaCrossJoin;
Expand All @@ -20,9 +21,7 @@
import org.hibernate.query.criteria.JpaPath;
import org.hibernate.query.criteria.JpaRoot;
import org.hibernate.query.criteria.JpaSubQuery;
import org.hibernate.query.sqm.tree.SqmJoinType;

import org.hibernate.testing.orm.junit.DialectFeatureCheck;
import org.hibernate.testing.orm.junit.DialectFeatureChecks;
import org.hibernate.testing.orm.junit.EntityManagerFactoryScope;
import org.hibernate.testing.orm.junit.Jira;
Expand Down Expand Up @@ -97,7 +96,7 @@ public void testEntityJoin(EntityManagerFactoryScope scope) {
final HibernateCriteriaBuilder cb = entityManager.unwrap( Session.class ).getCriteriaBuilder();
final JpaCriteriaQuery<Tuple> query = cb.createTupleQuery();
final JpaRoot<Primary> root = query.from( Primary.class );
final JpaEntityJoin<Primary,Secondary> entityJoin = root.join( Secondary.class, SqmJoinType.INNER );
final JpaEntityJoin<Primary,Secondary> entityJoin = root.join( Secondary.class, JoinType.INNER );
final JpaPath<Integer> id = root.get( "id" );
entityJoin.on( cb.equal( id, entityJoin.get( "id" ) ) );
final JpaPath<String> name = entityJoin.get( "name" );
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,13 @@

import java.util.function.Consumer;

import org.hibernate.query.common.JoinType;
import org.hibernate.query.criteria.HibernateCriteriaBuilder;
import org.hibernate.query.criteria.JpaCriteriaQuery;
import org.hibernate.query.criteria.JpaDerivedJoin;
import org.hibernate.query.criteria.JpaRoot;
import org.hibernate.query.criteria.JpaSubQuery;
import org.hibernate.query.spi.QueryImplementor;
import org.hibernate.query.sqm.tree.SqmJoinType;
import org.hibernate.query.sqm.tree.from.SqmAttributeJoin;

import org.hibernate.testing.TestForIssue;
import org.hibernate.testing.orm.junit.DialectFeatureChecks;
Expand Down Expand Up @@ -67,7 +66,7 @@ public void testEntity(SessionFactoryScope scope) {
subquery.orderBy( cb.asc( alternativeContact.get( "name" ).get( "first" ) ) );
subquery.fetch( 1 );

final JpaDerivedJoin<Tuple> a = root.joinLateral( subquery, SqmJoinType.LEFT );
final JpaDerivedJoin<Tuple> a = root.joinLateral( subquery, JoinType.LEFT );

cq.multiselect( root.get( "name" ), a.get( "contact" ).get( "id" ) );
cq.orderBy( cb.asc( root.get( "id" ) ) );
Expand Down Expand Up @@ -117,7 +116,7 @@ public void testEntityJoin(SessionFactoryScope scope) {
subquery.orderBy( cb.desc( alternativeContact.get( "name" ).get( "first" ) ) );
subquery.fetch( 1 );

final JpaDerivedJoin<Tuple> a = root.joinLateral( subquery, SqmJoinType.LEFT );
final JpaDerivedJoin<Tuple> a = root.joinLateral( subquery, JoinType.LEFT );
final Join<Object, Object> alt = a.join( "contact" );

cq.multiselect( root.get( "name" ), alt.get( "name" ) );
Expand Down Expand Up @@ -165,7 +164,7 @@ public void testEntityImplicit(SessionFactoryScope scope) {
subquery.orderBy( cb.desc( alternativeContact.get( "name" ).get( "first" ) ) );
subquery.fetch( 1 );

final JpaDerivedJoin<Tuple> a = root.joinLateral( subquery, SqmJoinType.LEFT );
final JpaDerivedJoin<Tuple> a = root.joinLateral( subquery, JoinType.LEFT );

cq.multiselect( root.get( "name" ), a.get( "contact" ).get( "name" ) );
cq.orderBy( cb.asc( root.get( "id" ) ) );
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,13 @@

import java.util.function.Consumer;

import org.hibernate.query.common.JoinType;
import org.hibernate.query.criteria.HibernateCriteriaBuilder;
import org.hibernate.query.criteria.JpaCriteriaQuery;
import org.hibernate.query.criteria.JpaDerivedJoin;
import org.hibernate.query.criteria.JpaRoot;
import org.hibernate.query.criteria.JpaSubQuery;
import org.hibernate.query.spi.QueryImplementor;
import org.hibernate.query.sqm.tree.SqmJoinType;
import org.hibernate.query.sqm.tree.from.SqmAttributeJoin;

import org.hibernate.testing.orm.junit.DialectFeatureChecks;
import org.hibernate.testing.orm.junit.DomainModel;
Expand Down Expand Up @@ -65,7 +64,7 @@ public void testEntity(SessionFactoryScope scope) {
subquery.orderBy( cb.asc( alternativeContact.get( "name" ).get( "first" ) ) );
subquery.fetch( 1 );

final JpaDerivedJoin<Tuple> a = root.joinLateral( subquery, SqmJoinType.LEFT );
final JpaDerivedJoin<Tuple> a = root.joinLateral( subquery, JoinType.LEFT );

cq.multiselect( root.get( "name" ), a.get( "contact" ).get( "id1" ), a.get( "contact" ).get( "id2" ) );
cq.orderBy( cb.asc( root.get( "id1" ) ) );
Expand Down Expand Up @@ -116,7 +115,7 @@ public void testEntityJoin(SessionFactoryScope scope) {
subquery.orderBy( cb.desc( alternativeContact.get( "name" ).get( "first" ) ) );
subquery.fetch( 1 );

final JpaDerivedJoin<Tuple> a = root.joinLateral( subquery, SqmJoinType.LEFT );
final JpaDerivedJoin<Tuple> a = root.joinLateral( subquery, JoinType.LEFT );
final Join<Object, Object> alt = a.join( "contact" );

cq.multiselect( root.get( "name" ), alt.get( "name" ) );
Expand Down Expand Up @@ -164,7 +163,7 @@ public void testEntityImplicit(SessionFactoryScope scope) {
subquery.orderBy( cb.desc( alternativeContact.get( "name" ).get( "first" ) ) );
subquery.fetch( 1 );

final JpaDerivedJoin<Tuple> a = root.joinLateral( subquery, SqmJoinType.LEFT );
final JpaDerivedJoin<Tuple> a = root.joinLateral( subquery, JoinType.LEFT );

cq.multiselect( root.get( "name" ), a.get( "contact" ).get( "name" ) );
cq.orderBy( cb.asc( root.get( "id1" ) ) );
Expand Down
Loading

0 comments on commit 10c835d

Please sign in to comment.