HHH-13001 - NPE rendering nested criteria expressions
This commit is contained in:
parent
23c6565f72
commit
a2b26b81b3
|
@ -0,0 +1,65 @@
|
||||||
|
/*
|
||||||
|
* 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.internal.util.collections;
|
||||||
|
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Steve Ebersole
|
||||||
|
*/
|
||||||
|
public class SingletonStack<T> implements Stack<T> {
|
||||||
|
private final T instance;
|
||||||
|
|
||||||
|
public SingletonStack(T instance) {
|
||||||
|
this.instance = instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void push(T newCurrent) {
|
||||||
|
throw new UnsupportedOperationException( "Cannot push to a singleton Stack" );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public T pop() {
|
||||||
|
throw new UnsupportedOperationException( "Cannot pop from a singleton Stack" );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public T getCurrent() {
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public T getPrevious() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int depth() {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isEmpty() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void clear() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visitCurrentFirst(Consumer<T> action) {
|
||||||
|
action.accept( instance );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <X> X findCurrentFirst(Function<T, X> action) {
|
||||||
|
return action.apply( instance );
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,65 @@
|
||||||
|
/*
|
||||||
|
* 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.internal.util.collections;
|
||||||
|
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stack implementation exposing useful methods for Hibernate needs.
|
||||||
|
*
|
||||||
|
* @param <T> The type of things stored in the stack
|
||||||
|
*
|
||||||
|
* @author Steve Ebersole
|
||||||
|
*/
|
||||||
|
public interface Stack<T> {
|
||||||
|
/**
|
||||||
|
* Push the new element on the top of the stack
|
||||||
|
*/
|
||||||
|
void push(T newCurrent);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pop (remove and return) the current element off the stack
|
||||||
|
*/
|
||||||
|
T pop();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The element currently at the top of the stack
|
||||||
|
*/
|
||||||
|
T getCurrent();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The element previously at the top of the stack before the current one
|
||||||
|
*/
|
||||||
|
T getPrevious();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* How many elements are currently on the stack?
|
||||||
|
*/
|
||||||
|
int depth();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Are there no elements currently in the stack?
|
||||||
|
*/
|
||||||
|
boolean isEmpty();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remmove all elements from the stack
|
||||||
|
*/
|
||||||
|
void clear();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Visit all elements in the stack, starting with the current and working back
|
||||||
|
*/
|
||||||
|
void visitCurrentFirst(Consumer<T> action);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find an element on the stack and return a value. The first non-null element
|
||||||
|
* returned from `action` stops the iteration and is returned from here
|
||||||
|
*/
|
||||||
|
<X> X findCurrentFirst(Function<T, X> action);
|
||||||
|
}
|
|
@ -0,0 +1,83 @@
|
||||||
|
/*
|
||||||
|
* 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.internal.util.collections;
|
||||||
|
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A general-purpose stack impl.
|
||||||
|
*
|
||||||
|
* @param <T> The type of things stored in the stack
|
||||||
|
*
|
||||||
|
* @author Steve Ebersole
|
||||||
|
*/
|
||||||
|
public class StandardStack<T> implements Stack<T> {
|
||||||
|
private LinkedList<T> internalStack = new LinkedList<>();
|
||||||
|
|
||||||
|
public StandardStack() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public StandardStack(T initial) {
|
||||||
|
internalStack.add( initial );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void push(T newCurrent) {
|
||||||
|
internalStack.addFirst( newCurrent );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public T pop() {
|
||||||
|
return internalStack.removeFirst();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public T getCurrent() {
|
||||||
|
return internalStack.peek();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public T getPrevious() {
|
||||||
|
if ( internalStack.size() < 2 ) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return internalStack.get( internalStack.size() - 2 );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int depth() {
|
||||||
|
return internalStack.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isEmpty() {
|
||||||
|
return internalStack.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void clear() {
|
||||||
|
internalStack.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visitCurrentFirst(Consumer<T> action) {
|
||||||
|
internalStack.forEach( action );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <X> X findCurrentFirst(Function<T, X> function) {
|
||||||
|
for ( T t : internalStack ) {
|
||||||
|
final X result = function.apply( t );
|
||||||
|
if ( result != null ) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
|
@ -25,6 +25,7 @@ import org.hibernate.query.criteria.internal.compile.InterpretedParameterMetadat
|
||||||
import org.hibernate.query.criteria.internal.compile.RenderingContext;
|
import org.hibernate.query.criteria.internal.compile.RenderingContext;
|
||||||
import org.hibernate.query.criteria.internal.path.RootImpl;
|
import org.hibernate.query.criteria.internal.path.RootImpl;
|
||||||
import org.hibernate.query.spi.QueryImplementor;
|
import org.hibernate.query.spi.QueryImplementor;
|
||||||
|
import org.hibernate.sql.ast.Clause;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Base class for commonality between {@link javax.persistence.criteria.CriteriaUpdate} and
|
* Base class for commonality between {@link javax.persistence.criteria.CriteriaUpdate} and
|
||||||
|
@ -155,9 +156,17 @@ public abstract class AbstractManipulationCriteriaQuery<T> implements Compilable
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void renderRestrictions(StringBuilder jpaql, RenderingContext renderingContext) {
|
protected void renderRestrictions(StringBuilder jpaql, RenderingContext renderingContext) {
|
||||||
if ( getRestriction() != null) {
|
if ( getRestriction() == null ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
renderingContext.getClauseStack().push( Clause.WHERE );
|
||||||
|
try {
|
||||||
jpaql.append( " where " )
|
jpaql.append( " where " )
|
||||||
.append( ( (Renderable) getRestriction() ).render( renderingContext ) );
|
.append( ( (Renderable) getRestriction() ).render( renderingContext ) );
|
||||||
}
|
}
|
||||||
|
finally {
|
||||||
|
renderingContext.getClauseStack().pop();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,6 +33,7 @@ import org.hibernate.query.criteria.internal.compile.ImplicitParameterBinding;
|
||||||
import org.hibernate.query.criteria.internal.compile.InterpretedParameterMetadata;
|
import org.hibernate.query.criteria.internal.compile.InterpretedParameterMetadata;
|
||||||
import org.hibernate.query.criteria.internal.compile.RenderingContext;
|
import org.hibernate.query.criteria.internal.compile.RenderingContext;
|
||||||
import org.hibernate.query.spi.QueryImplementor;
|
import org.hibernate.query.spi.QueryImplementor;
|
||||||
|
import org.hibernate.sql.ast.Clause;
|
||||||
import org.hibernate.type.Type;
|
import org.hibernate.type.Type;
|
||||||
|
|
||||||
import org.jboss.logging.Logger;
|
import org.jboss.logging.Logger;
|
||||||
|
@ -291,16 +292,7 @@ public class CriteriaQueryImpl<T> extends AbstractNode implements CriteriaQuery<
|
||||||
|
|
||||||
queryStructure.render( jpaqlBuffer, renderingContext );
|
queryStructure.render( jpaqlBuffer, renderingContext );
|
||||||
|
|
||||||
if ( ! getOrderList().isEmpty() ) {
|
renderOrderByClause( renderingContext, jpaqlBuffer );
|
||||||
jpaqlBuffer.append( " order by " );
|
|
||||||
String sep = "";
|
|
||||||
for ( Order orderSpec : getOrderList() ) {
|
|
||||||
jpaqlBuffer.append( sep )
|
|
||||||
.append( ( ( Renderable ) orderSpec.getExpression() ).render( renderingContext ) )
|
|
||||||
.append( orderSpec.isAscending() ? " asc" : " desc" );
|
|
||||||
sep = ", ";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
final String jpaqlString = jpaqlBuffer.toString();
|
final String jpaqlString = jpaqlBuffer.toString();
|
||||||
|
|
||||||
|
@ -385,4 +377,25 @@ public class CriteriaQueryImpl<T> extends AbstractNode implements CriteriaQuery<
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected void renderOrderByClause(RenderingContext renderingContext, StringBuilder jpaqlBuffer) {
|
||||||
|
if ( getOrderList().isEmpty() ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
renderingContext.getClauseStack().push( Clause.ORDER );
|
||||||
|
try {
|
||||||
|
jpaqlBuffer.append( " order by " );
|
||||||
|
String sep = "";
|
||||||
|
for ( Order orderSpec : getOrderList() ) {
|
||||||
|
jpaqlBuffer.append( sep )
|
||||||
|
.append( ( (Renderable) orderSpec.getExpression() ).render( renderingContext ) )
|
||||||
|
.append( orderSpec.isAscending() ? " asc" : " desc" );
|
||||||
|
sep = ", ";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
renderingContext.getClauseStack().pop();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,6 +27,7 @@ import org.hibernate.query.criteria.internal.compile.RenderingContext;
|
||||||
import org.hibernate.query.criteria.internal.expression.DelegatedExpressionImpl;
|
import org.hibernate.query.criteria.internal.expression.DelegatedExpressionImpl;
|
||||||
import org.hibernate.query.criteria.internal.expression.ExpressionImpl;
|
import org.hibernate.query.criteria.internal.expression.ExpressionImpl;
|
||||||
import org.hibernate.query.criteria.internal.path.RootImpl;
|
import org.hibernate.query.criteria.internal.path.RootImpl;
|
||||||
|
import org.hibernate.sql.ast.Clause;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The Hibernate implementation of the JPA {@link Subquery} contract. Mostlty a set of delegation to its internal
|
* The Hibernate implementation of the JPA {@link Subquery} contract. Mostlty a set of delegation to its internal
|
||||||
|
@ -257,14 +258,13 @@ public class CriteriaSubqueryImpl<T> extends ExpressionImpl<T> implements Subque
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String render(RenderingContext renderingContext) {
|
public String render(RenderingContext renderingContext) {
|
||||||
|
if ( renderingContext.getClauseStack().getCurrent() == Clause.SELECT ) {
|
||||||
|
throw new IllegalStateException( "Subquery cannot occur in select clause" );
|
||||||
|
}
|
||||||
|
|
||||||
StringBuilder subqueryBuffer = new StringBuilder( "(" );
|
StringBuilder subqueryBuffer = new StringBuilder( "(" );
|
||||||
queryStructure.render( subqueryBuffer, renderingContext );
|
queryStructure.render( subqueryBuffer, renderingContext );
|
||||||
subqueryBuffer.append( ')' );
|
subqueryBuffer.append( ')' );
|
||||||
return subqueryBuffer.toString();
|
return subqueryBuffer.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public String renderProjection(RenderingContext renderingContext) {
|
|
||||||
throw new IllegalStateException( "Subquery cannot occur in select clause" );
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,6 +16,7 @@ import javax.persistence.metamodel.SingularAttribute;
|
||||||
|
|
||||||
import org.hibernate.query.criteria.internal.compile.RenderingContext;
|
import org.hibernate.query.criteria.internal.compile.RenderingContext;
|
||||||
import org.hibernate.query.criteria.internal.path.SingularAttributePath;
|
import org.hibernate.query.criteria.internal.path.SingularAttributePath;
|
||||||
|
import org.hibernate.sql.ast.Clause;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Hibernate implementation of the JPA 2.1 {@link CriteriaUpdate} contract.
|
* Hibernate implementation of the JPA 2.1 {@link CriteriaUpdate} contract.
|
||||||
|
@ -122,16 +123,23 @@ public class CriteriaUpdateImpl<T> extends AbstractManipulationCriteriaQuery<T>
|
||||||
}
|
}
|
||||||
|
|
||||||
private void renderAssignments(StringBuilder jpaql, RenderingContext renderingContext) {
|
private void renderAssignments(StringBuilder jpaql, RenderingContext renderingContext) {
|
||||||
jpaql.append( " set " );
|
renderingContext.getClauseStack().push( Clause.UPDATE );
|
||||||
boolean first = true;
|
|
||||||
for ( Assignment assignment : assignments ) {
|
try {
|
||||||
if ( ! first ) {
|
jpaql.append( " set " );
|
||||||
jpaql.append( ", " );
|
boolean first = true;
|
||||||
|
for ( Assignment assignment : assignments ) {
|
||||||
|
if ( !first ) {
|
||||||
|
jpaql.append( ", " );
|
||||||
|
}
|
||||||
|
jpaql.append( assignment.attributePath.render( renderingContext ) )
|
||||||
|
.append( " = " )
|
||||||
|
.append( assignment.value.render( renderingContext ) );
|
||||||
|
first = false;
|
||||||
}
|
}
|
||||||
jpaql.append( assignment.attributePath.render( renderingContext ) )
|
}
|
||||||
.append( " = " )
|
finally {
|
||||||
.append( assignment.value.render( renderingContext ) );
|
renderingContext.getClauseStack().pop();
|
||||||
first = false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -31,6 +31,7 @@ import javax.persistence.metamodel.EntityType;
|
||||||
import org.hibernate.query.criteria.internal.compile.RenderingContext;
|
import org.hibernate.query.criteria.internal.compile.RenderingContext;
|
||||||
import org.hibernate.query.criteria.internal.path.RootImpl;
|
import org.hibernate.query.criteria.internal.path.RootImpl;
|
||||||
import org.hibernate.query.criteria.internal.path.RootImpl.TreatedRoot;
|
import org.hibernate.query.criteria.internal.path.RootImpl.TreatedRoot;
|
||||||
|
import org.hibernate.sql.ast.Clause;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Models basic query structure. Used as a delegate in implementing both
|
* Models basic query structure. Used as a delegate in implementing both
|
||||||
|
@ -230,37 +231,34 @@ public class QueryStructure<T> implements Serializable {
|
||||||
|
|
||||||
@SuppressWarnings({ "unchecked" })
|
@SuppressWarnings({ "unchecked" })
|
||||||
public void render(StringBuilder jpaqlQuery, RenderingContext renderingContext) {
|
public void render(StringBuilder jpaqlQuery, RenderingContext renderingContext) {
|
||||||
jpaqlQuery.append( "select " );
|
renderSelectClause( jpaqlQuery, renderingContext );
|
||||||
if ( isDistinct() ) {
|
|
||||||
jpaqlQuery.append( "distinct " );
|
|
||||||
}
|
|
||||||
if ( getSelection() == null ) {
|
|
||||||
jpaqlQuery.append( locateImplicitSelection().renderProjection( renderingContext ) );
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
jpaqlQuery.append( ( (Renderable) getSelection() ).renderProjection( renderingContext ) );
|
|
||||||
}
|
|
||||||
|
|
||||||
renderFromClause( jpaqlQuery, renderingContext );
|
renderFromClause( jpaqlQuery, renderingContext );
|
||||||
|
|
||||||
if ( getRestriction() != null) {
|
renderWhereClause( jpaqlQuery, renderingContext );
|
||||||
jpaqlQuery.append( " where " )
|
|
||||||
.append( ( (Renderable) getRestriction() ).render( renderingContext ) );
|
renderGroupByClause( jpaqlQuery, renderingContext );
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void renderSelectClause(StringBuilder jpaqlQuery, RenderingContext renderingContext) {
|
||||||
|
renderingContext.getClauseStack().push( Clause.SELECT );
|
||||||
|
|
||||||
|
try {
|
||||||
|
jpaqlQuery.append( "select " );
|
||||||
|
|
||||||
|
if ( isDistinct() ) {
|
||||||
|
jpaqlQuery.append( "distinct " );
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( getSelection() == null ) {
|
||||||
|
jpaqlQuery.append( locateImplicitSelection().render( renderingContext ) );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
jpaqlQuery.append( ( (Renderable) getSelection() ).render( renderingContext ) );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
finally {
|
||||||
if ( ! getGroupings().isEmpty() ) {
|
renderingContext.getClauseStack().pop();
|
||||||
jpaqlQuery.append( " group by " );
|
|
||||||
String sep = "";
|
|
||||||
for ( Expression grouping : getGroupings() ) {
|
|
||||||
jpaqlQuery.append( sep )
|
|
||||||
.append( ( (Renderable) grouping ).renderGroupBy( renderingContext ) );
|
|
||||||
sep = ", ";
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( getHaving() != null ) {
|
|
||||||
jpaqlQuery.append( " having " )
|
|
||||||
.append( ( (Renderable) getHaving() ).render( renderingContext ) );
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -290,48 +288,106 @@ public class QueryStructure<T> implements Serializable {
|
||||||
|
|
||||||
@SuppressWarnings({ "unchecked" })
|
@SuppressWarnings({ "unchecked" })
|
||||||
private void renderFromClause(StringBuilder jpaqlQuery, RenderingContext renderingContext) {
|
private void renderFromClause(StringBuilder jpaqlQuery, RenderingContext renderingContext) {
|
||||||
jpaqlQuery.append( " from " );
|
renderingContext.getClauseStack().push( Clause.FROM );
|
||||||
String sep = "";
|
|
||||||
for ( Root root : getRoots() ) {
|
|
||||||
( (FromImplementor) root ).prepareAlias( renderingContext );
|
|
||||||
jpaqlQuery.append( sep );
|
|
||||||
jpaqlQuery.append( ( (FromImplementor) root ).renderTableExpression( renderingContext ) );
|
|
||||||
sep = ", ";
|
|
||||||
}
|
|
||||||
|
|
||||||
for ( Root root : getRoots() ) {
|
try {
|
||||||
renderJoins( jpaqlQuery, renderingContext, root.getJoins() );
|
jpaqlQuery.append( " from " );
|
||||||
if (root instanceof RootImpl) {
|
String sep = "";
|
||||||
Set<TreatedRoot> treats = ((RootImpl)root).getTreats();
|
for ( Root root : getRoots() ) {
|
||||||
for ( TreatedRoot treat : treats ) {
|
( (FromImplementor) root ).prepareAlias( renderingContext );
|
||||||
renderJoins( jpaqlQuery, renderingContext, treat.getJoins() );
|
jpaqlQuery.append( sep );
|
||||||
}
|
sep = ", ";
|
||||||
|
jpaqlQuery.append( ( (FromImplementor) root ).renderTableExpression( renderingContext ) );
|
||||||
}
|
}
|
||||||
renderFetches( jpaqlQuery, renderingContext, root.getFetches() );
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( isSubQuery ) {
|
for ( Root root : getRoots() ) {
|
||||||
if ( correlationRoots != null ) {
|
renderJoins( jpaqlQuery, renderingContext, root.getJoins() );
|
||||||
for ( FromImplementor<?,?> correlationRoot : correlationRoots ) {
|
if ( root instanceof RootImpl ) {
|
||||||
final FromImplementor correlationParent = correlationRoot.getCorrelationParent();
|
Set<TreatedRoot> treats = ( (RootImpl) root ).getTreats();
|
||||||
correlationParent.prepareAlias( renderingContext );
|
for ( TreatedRoot treat : treats ) {
|
||||||
final String correlationRootAlias = correlationParent.getAlias();
|
renderJoins( jpaqlQuery, renderingContext, treat.getJoins() );
|
||||||
for ( Join<?,?> correlationJoin : correlationRoot.getJoins() ) {
|
}
|
||||||
final JoinImplementor correlationJoinImpl = (JoinImplementor) correlationJoin;
|
}
|
||||||
// IMPL NOTE: reuse the sep from above!
|
renderFetches( jpaqlQuery, renderingContext, root.getFetches() );
|
||||||
jpaqlQuery.append( sep );
|
}
|
||||||
correlationJoinImpl.prepareAlias( renderingContext );
|
|
||||||
jpaqlQuery.append( correlationRootAlias )
|
if ( isSubQuery ) {
|
||||||
.append( '.' )
|
if ( correlationRoots != null ) {
|
||||||
.append( correlationJoinImpl.getAttribute().getName() )
|
for ( FromImplementor<?, ?> correlationRoot : correlationRoots ) {
|
||||||
.append( " as " )
|
final FromImplementor correlationParent = correlationRoot.getCorrelationParent();
|
||||||
.append( correlationJoinImpl.getAlias() );
|
correlationParent.prepareAlias( renderingContext );
|
||||||
sep = ", ";
|
final String correlationRootAlias = correlationParent.getAlias();
|
||||||
renderJoins( jpaqlQuery, renderingContext, correlationJoinImpl.getJoins() );
|
for ( Join<?, ?> correlationJoin : correlationRoot.getJoins() ) {
|
||||||
|
final JoinImplementor correlationJoinImpl = (JoinImplementor) correlationJoin;
|
||||||
|
// IMPL NOTE: reuse the sep from above!
|
||||||
|
jpaqlQuery.append( sep );
|
||||||
|
correlationJoinImpl.prepareAlias( renderingContext );
|
||||||
|
jpaqlQuery.append( correlationRootAlias )
|
||||||
|
.append( '.' )
|
||||||
|
.append( correlationJoinImpl.getAttribute().getName() )
|
||||||
|
.append( " as " )
|
||||||
|
.append( correlationJoinImpl.getAlias() );
|
||||||
|
sep = ", ";
|
||||||
|
renderJoins( jpaqlQuery, renderingContext, correlationJoinImpl.getJoins() );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
finally {
|
||||||
|
renderingContext.getClauseStack().pop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void renderWhereClause(StringBuilder jpaqlQuery, RenderingContext renderingContext) {
|
||||||
|
if ( getRestriction() == null ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
renderingContext.getClauseStack().push( Clause.WHERE );
|
||||||
|
try {
|
||||||
|
jpaqlQuery.append( " where " )
|
||||||
|
.append( ( (Renderable) getRestriction() ).render( renderingContext ) );
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
renderingContext.getClauseStack().pop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void renderGroupByClause(StringBuilder jpaqlQuery, RenderingContext renderingContext) {
|
||||||
|
if ( getGroupings().isEmpty() ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
renderingContext.getClauseStack().push( Clause.GROUP );
|
||||||
|
try {
|
||||||
|
jpaqlQuery.append( " group by " );
|
||||||
|
String sep = "";
|
||||||
|
for ( Expression grouping : getGroupings() ) {
|
||||||
|
jpaqlQuery.append( sep )
|
||||||
|
.append( ( (Renderable) grouping ).render( renderingContext ) );
|
||||||
|
sep = ", ";
|
||||||
|
}
|
||||||
|
|
||||||
|
renderHavingClause( jpaqlQuery, renderingContext );
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
renderingContext.getClauseStack().pop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void renderHavingClause(StringBuilder jpaqlQuery, RenderingContext renderingContext) {
|
||||||
|
if ( getHaving() == null ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
renderingContext.getClauseStack().push( Clause.HAVING );
|
||||||
|
try {
|
||||||
|
jpaqlQuery.append( " having " ).append( ( (Renderable) getHaving() ).render( renderingContext ) );
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
renderingContext.getClauseStack().pop();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings({ "unchecked" })
|
@SuppressWarnings({ "unchecked" })
|
||||||
|
|
|
@ -10,38 +10,15 @@ package org.hibernate.query.criteria.internal;
|
||||||
import org.hibernate.query.criteria.internal.compile.RenderingContext;
|
import org.hibernate.query.criteria.internal.compile.RenderingContext;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TODO : javadoc
|
* Contract for nodes in the JPA Criteria tree that can be rendered
|
||||||
|
* as part of criteria "compilation"
|
||||||
*
|
*
|
||||||
* @author Steve Ebersole
|
* @author Steve Ebersole
|
||||||
*/
|
*/
|
||||||
public interface Renderable {
|
public interface Renderable {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Render clause
|
* Perform the rendering, returning the rendition
|
||||||
*
|
|
||||||
* @param renderingContext context
|
|
||||||
* @return rendered expression
|
|
||||||
*/
|
*/
|
||||||
String render(RenderingContext renderingContext);
|
String render(RenderingContext renderingContext);
|
||||||
|
|
||||||
/**
|
|
||||||
* Render SELECT clause
|
|
||||||
*
|
|
||||||
* @param renderingContext context
|
|
||||||
* @return rendered expression
|
|
||||||
*/
|
|
||||||
default String renderProjection(RenderingContext renderingContext) {
|
|
||||||
return render( renderingContext );
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Render GROUP BY clause
|
|
||||||
*
|
|
||||||
* @param renderingContext context
|
|
||||||
*
|
|
||||||
* @return rendered expression
|
|
||||||
*/
|
|
||||||
default String renderGroupBy(RenderingContext renderingContext) {
|
|
||||||
return render( renderingContext );
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,8 +20,12 @@ import org.hibernate.engine.jdbc.spi.JdbcServices;
|
||||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||||
import org.hibernate.engine.spi.SessionImplementor;
|
import org.hibernate.engine.spi.SessionImplementor;
|
||||||
import org.hibernate.internal.util.StringHelper;
|
import org.hibernate.internal.util.StringHelper;
|
||||||
|
import org.hibernate.internal.util.collections.Stack;
|
||||||
|
import org.hibernate.internal.util.collections.StandardStack;
|
||||||
import org.hibernate.query.criteria.LiteralHandlingMode;
|
import org.hibernate.query.criteria.LiteralHandlingMode;
|
||||||
|
import org.hibernate.query.criteria.internal.expression.function.FunctionExpression;
|
||||||
import org.hibernate.query.spi.QueryImplementor;
|
import org.hibernate.query.spi.QueryImplementor;
|
||||||
|
import org.hibernate.sql.ast.Clause;
|
||||||
import org.hibernate.type.Type;
|
import org.hibernate.type.Type;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -63,6 +67,9 @@ public class CriteriaCompiler implements Serializable {
|
||||||
private int aliasCount;
|
private int aliasCount;
|
||||||
private int explicitParameterCount;
|
private int explicitParameterCount;
|
||||||
|
|
||||||
|
private final Stack<Clause> clauseStack = new StandardStack<>();
|
||||||
|
private final Stack<FunctionExpression> functionContextStack = new StandardStack<>();
|
||||||
|
|
||||||
public String generateAlias() {
|
public String generateAlias() {
|
||||||
return "generatedAlias" + aliasCount++;
|
return "generatedAlias" + aliasCount++;
|
||||||
}
|
}
|
||||||
|
@ -71,6 +78,16 @@ public class CriteriaCompiler implements Serializable {
|
||||||
return "param" + explicitParameterCount++;
|
return "param" + explicitParameterCount++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Stack<Clause> getClauseStack() {
|
||||||
|
return clauseStack;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Stack<FunctionExpression> getFunctionStack() {
|
||||||
|
return functionContextStack;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public ExplicitParameterInfo registerExplicitParameter(ParameterExpression<?> criteriaQueryParameter) {
|
public ExplicitParameterInfo registerExplicitParameter(ParameterExpression<?> criteriaQueryParameter) {
|
||||||
|
|
|
@ -9,7 +9,10 @@ package org.hibernate.query.criteria.internal.compile;
|
||||||
import javax.persistence.criteria.ParameterExpression;
|
import javax.persistence.criteria.ParameterExpression;
|
||||||
|
|
||||||
import org.hibernate.dialect.Dialect;
|
import org.hibernate.dialect.Dialect;
|
||||||
|
import org.hibernate.internal.util.collections.Stack;
|
||||||
import org.hibernate.query.criteria.LiteralHandlingMode;
|
import org.hibernate.query.criteria.LiteralHandlingMode;
|
||||||
|
import org.hibernate.query.criteria.internal.expression.function.FunctionExpression;
|
||||||
|
import org.hibernate.sql.ast.Clause;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Used to provide a context and services to the rendering.
|
* Used to provide a context and services to the rendering.
|
||||||
|
@ -67,4 +70,8 @@ public interface RenderingContext {
|
||||||
default LiteralHandlingMode getCriteriaLiteralHandlingMode() {
|
default LiteralHandlingMode getCriteriaLiteralHandlingMode() {
|
||||||
return LiteralHandlingMode.AUTO;
|
return LiteralHandlingMode.AUTO;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Stack<Clause> getClauseStack();
|
||||||
|
|
||||||
|
Stack<FunctionExpression> getFunctionStack();
|
||||||
}
|
}
|
||||||
|
|
|
@ -77,15 +77,17 @@ public class CompoundSelectionImpl<X>
|
||||||
if ( isConstructor ) {
|
if ( isConstructor ) {
|
||||||
buff.append( "new " ).append( getJavaType().getName() ).append( '(' );
|
buff.append( "new " ).append( getJavaType().getName() ).append( '(' );
|
||||||
}
|
}
|
||||||
|
|
||||||
String sep = "";
|
String sep = "";
|
||||||
for ( Selection selection : selectionItems ) {
|
for ( Selection selection : selectionItems ) {
|
||||||
buff.append( sep )
|
buff.append( sep ).append( ( (Renderable) selection ).render( renderingContext ) );
|
||||||
.append( ( (Renderable) selection ).renderProjection( renderingContext ) );
|
|
||||||
sep = ", ";
|
sep = ", ";
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( isConstructor ) {
|
if ( isConstructor ) {
|
||||||
buff.append( ')' );
|
buff.append( ')' );
|
||||||
}
|
}
|
||||||
|
|
||||||
return buff.toString();
|
return buff.toString();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -47,37 +47,54 @@ public class LiteralExpression<T> extends ExpressionImpl<T> implements Serializa
|
||||||
|
|
||||||
@SuppressWarnings({ "unchecked" })
|
@SuppressWarnings({ "unchecked" })
|
||||||
public String render(RenderingContext renderingContext) {
|
public String render(RenderingContext renderingContext) {
|
||||||
|
switch ( renderingContext.getClauseStack().getCurrent() ) {
|
||||||
|
case SELECT: {
|
||||||
|
return renderProjection();
|
||||||
|
}
|
||||||
|
case GROUP: {
|
||||||
|
// technically a literal in the group-by clause
|
||||||
|
// would be a reference to the position of a selection
|
||||||
|
//
|
||||||
|
// but this is what the code used to do...
|
||||||
|
return renderProjection();
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
return normalRender( renderingContext );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
private String normalRender(RenderingContext renderingContext) {
|
||||||
LiteralHandlingMode literalHandlingMode = renderingContext.getCriteriaLiteralHandlingMode();
|
LiteralHandlingMode literalHandlingMode = renderingContext.getCriteriaLiteralHandlingMode();
|
||||||
|
|
||||||
switch ( literalHandlingMode ) {
|
switch ( literalHandlingMode ) {
|
||||||
case AUTO:
|
case AUTO: {
|
||||||
if ( ValueHandlerFactory.isNumeric( literal ) ) {
|
if ( ValueHandlerFactory.isNumeric( literal ) ) {
|
||||||
return ValueHandlerFactory.determineAppropriateHandler( (Class) literal.getClass() ).render( literal );
|
return ValueHandlerFactory.determineAppropriateHandler( (Class) literal.getClass() ).render( literal );
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
return bindLiteral( renderingContext );
|
return bindLiteral( renderingContext );
|
||||||
}
|
}
|
||||||
case BIND:
|
}
|
||||||
|
case BIND: {
|
||||||
return bindLiteral( renderingContext );
|
return bindLiteral( renderingContext );
|
||||||
case INLINE:
|
}
|
||||||
|
case INLINE: {
|
||||||
Object literalValue = literal;
|
Object literalValue = literal;
|
||||||
if ( String.class.equals( literal.getClass() ) ) {
|
if ( String.class.equals( literal.getClass() ) ) {
|
||||||
literalValue = renderingContext.getDialect().inlineLiteral( (String) literal );
|
literalValue = renderingContext.getDialect().inlineLiteral( (String) literal );
|
||||||
}
|
}
|
||||||
|
|
||||||
return ValueHandlerFactory.determineAppropriateHandler( (Class) literal.getClass() ).render( literalValue );
|
return ValueHandlerFactory.determineAppropriateHandler( (Class) literal.getClass() ).render( literalValue );
|
||||||
default:
|
}
|
||||||
|
default: {
|
||||||
throw new IllegalArgumentException( "Unexpected LiteralHandlingMode: " + literalHandlingMode );
|
throw new IllegalArgumentException( "Unexpected LiteralHandlingMode: " + literalHandlingMode );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private String bindLiteral(RenderingContext renderingContext) {
|
private String renderProjection() {
|
||||||
final String parameterName = renderingContext.registerLiteralParameterBinding( getLiteral(), getJavaType() );
|
|
||||||
return ':' + parameterName;
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings({ "unchecked" })
|
|
||||||
public String renderProjection(RenderingContext renderingContext) {
|
|
||||||
// some drivers/servers do not like parameters in the select clause
|
// some drivers/servers do not like parameters in the select clause
|
||||||
final ValueHandlerFactory.ValueHandler handler =
|
final ValueHandlerFactory.ValueHandler handler =
|
||||||
ValueHandlerFactory.determineAppropriateHandler( literal.getClass() );
|
ValueHandlerFactory.determineAppropriateHandler( literal.getClass() );
|
||||||
|
@ -89,9 +106,9 @@ public class LiteralExpression<T> extends ExpressionImpl<T> implements Serializa
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
private String bindLiteral(RenderingContext renderingContext) {
|
||||||
public String renderGroupBy(RenderingContext renderingContext) {
|
final String parameterName = renderingContext.registerLiteralParameterBinding( getLiteral(), getJavaType() );
|
||||||
return renderProjection( renderingContext );
|
return ':' + parameterName;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -16,6 +16,7 @@ import org.hibernate.query.criteria.internal.ParameterRegistry;
|
||||||
import org.hibernate.query.criteria.internal.PathImplementor;
|
import org.hibernate.query.criteria.internal.PathImplementor;
|
||||||
import org.hibernate.query.criteria.internal.Renderable;
|
import org.hibernate.query.criteria.internal.Renderable;
|
||||||
import org.hibernate.query.criteria.internal.compile.RenderingContext;
|
import org.hibernate.query.criteria.internal.compile.RenderingContext;
|
||||||
|
import org.hibernate.sql.ast.Clause;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TODO : javadoc
|
* TODO : javadoc
|
||||||
|
@ -48,17 +49,17 @@ public class MapEntryExpression<K,V>
|
||||||
}
|
}
|
||||||
|
|
||||||
public String render(RenderingContext renderingContext) {
|
public String render(RenderingContext renderingContext) {
|
||||||
|
if ( renderingContext.getClauseStack().getCurrent() == Clause.SELECT ) {
|
||||||
|
return "entry(" + path( renderingContext ) + ")";
|
||||||
|
}
|
||||||
|
|
||||||
// don't think this is valid outside of select clause...
|
// don't think this is valid outside of select clause...
|
||||||
throw new IllegalStateException( "illegal reference to map entry outside of select clause." );
|
throw new IllegalStateException( "illegal reference to map entry outside of select clause." );
|
||||||
}
|
}
|
||||||
|
|
||||||
public String renderProjection(RenderingContext renderingContext) {
|
|
||||||
return "entry(" + path( renderingContext ) + ")";
|
|
||||||
}
|
|
||||||
|
|
||||||
private String path(RenderingContext renderingContext) {
|
private String path(RenderingContext renderingContext) {
|
||||||
return origin.getPathIdentifier()
|
return origin.getPathIdentifier()
|
||||||
+ '.'
|
+ '.'
|
||||||
+ ( (Renderable) getAttribute() ).renderProjection( renderingContext );
|
+ ( (Renderable) getAttribute() ).render( renderingContext );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,6 +12,7 @@ import org.hibernate.query.criteria.internal.CriteriaBuilderImpl;
|
||||||
import org.hibernate.query.criteria.internal.ParameterRegistry;
|
import org.hibernate.query.criteria.internal.ParameterRegistry;
|
||||||
import org.hibernate.query.criteria.internal.compile.RenderingContext;
|
import org.hibernate.query.criteria.internal.compile.RenderingContext;
|
||||||
import org.hibernate.query.criteria.internal.expression.function.CastFunction;
|
import org.hibernate.query.criteria.internal.expression.function.CastFunction;
|
||||||
|
import org.hibernate.sql.ast.Clause;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a <tt>NULL</tt>literal expression.
|
* Represents a <tt>NULL</tt>literal expression.
|
||||||
|
@ -28,10 +29,13 @@ public class NullLiteralExpression<T> extends ExpressionImpl<T> implements Seria
|
||||||
}
|
}
|
||||||
|
|
||||||
public String render(RenderingContext renderingContext) {
|
public String render(RenderingContext renderingContext) {
|
||||||
|
if ( renderingContext.getClauseStack().getCurrent() == Clause.SELECT ) {
|
||||||
|
// in the select clause render the ``null` using a cast so the db analyzer/optimizer
|
||||||
|
// understands the type
|
||||||
|
return CastFunction.CAST_NAME + "( null as " + renderingContext.getCastType( getJavaType() ) + ')';
|
||||||
|
}
|
||||||
|
|
||||||
|
// otherwise, just render `null`
|
||||||
return "null";
|
return "null";
|
||||||
}
|
}
|
||||||
|
|
||||||
public String renderProjection(RenderingContext renderingContext) {
|
|
||||||
return CastFunction.CAST_NAME + "( null as " + renderingContext.getCastType( getJavaType() ) + ')';
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -106,41 +106,18 @@ public class SearchedCaseExpression<R>
|
||||||
}
|
}
|
||||||
|
|
||||||
public String render(RenderingContext renderingContext) {
|
public String render(RenderingContext renderingContext) {
|
||||||
return render(
|
|
||||||
renderingContext,
|
|
||||||
(Renderable expression, RenderingContext context) -> expression.render( context )
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
public String renderProjection(RenderingContext renderingContext) {
|
|
||||||
return render(
|
|
||||||
renderingContext,
|
|
||||||
(Renderable expression, RenderingContext context) -> expression.renderProjection( context )
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String renderGroupBy(RenderingContext renderingContext) {
|
|
||||||
return render(
|
|
||||||
renderingContext,
|
|
||||||
(Renderable expression, RenderingContext context) -> expression.renderGroupBy( context )
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private String render(
|
|
||||||
RenderingContext renderingContext,
|
|
||||||
BiFunction<Renderable, RenderingContext, String> formatter) {
|
|
||||||
StringBuilder caseStatement = new StringBuilder( "case" );
|
StringBuilder caseStatement = new StringBuilder( "case" );
|
||||||
for ( WhenClause whenClause : getWhenClauses() ) {
|
for ( WhenClause whenClause : getWhenClauses() ) {
|
||||||
caseStatement.append( " when " )
|
caseStatement.append( " when " )
|
||||||
.append( formatter.apply( (Renderable) whenClause.getCondition(), renderingContext ) )
|
.append( ( (Renderable) whenClause.getCondition() ).render( renderingContext ) )
|
||||||
.append( " then " )
|
.append( " then " )
|
||||||
.append( formatter.apply( ((Renderable) whenClause.getResult()), renderingContext ) );
|
.append( ( (Renderable) whenClause.getResult() ).render( renderingContext ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
caseStatement.append( " else " )
|
caseStatement.append( " else " )
|
||||||
.append( formatter.apply( (Renderable) getOtherwiseResult(), renderingContext ) )
|
.append( ( (Renderable) getOtherwiseResult() ).render( renderingContext ) )
|
||||||
.append( " end" );
|
.append( " end" );
|
||||||
|
|
||||||
return caseStatement.toString();
|
return caseStatement.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,7 +9,6 @@ package org.hibernate.query.criteria.internal.expression;
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.function.BiFunction;
|
|
||||||
import javax.persistence.criteria.CriteriaBuilder.SimpleCase;
|
import javax.persistence.criteria.CriteriaBuilder.SimpleCase;
|
||||||
import javax.persistence.criteria.Expression;
|
import javax.persistence.criteria.Expression;
|
||||||
|
|
||||||
|
@ -118,44 +117,21 @@ public class SimpleCaseExpression<C,R>
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String render(RenderingContext renderingContext) {
|
public String render(RenderingContext renderingContext) {
|
||||||
return render(
|
|
||||||
renderingContext,
|
|
||||||
(Renderable expression, RenderingContext context) -> expression.render( context )
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String renderProjection(RenderingContext renderingContext) {
|
|
||||||
return render(
|
|
||||||
renderingContext,
|
|
||||||
(Renderable expression, RenderingContext context) -> expression.renderProjection( context )
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String renderGroupBy(RenderingContext renderingContext) {
|
|
||||||
return render(
|
|
||||||
renderingContext,
|
|
||||||
(Renderable expression, RenderingContext context) -> expression.renderGroupBy( context )
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private String render(
|
|
||||||
RenderingContext renderingContext,
|
|
||||||
BiFunction<Renderable, RenderingContext, String> formatter) {
|
|
||||||
StringBuilder caseExpr = new StringBuilder();
|
StringBuilder caseExpr = new StringBuilder();
|
||||||
caseExpr.append( "case " )
|
caseExpr.append( "case " )
|
||||||
.append( formatter.apply( (Renderable) getExpression(), renderingContext ) );
|
.append( ( (Renderable) getExpression() ).render( renderingContext ) );
|
||||||
|
|
||||||
for ( WhenClause whenClause : getWhenClauses() ) {
|
for ( WhenClause whenClause : getWhenClauses() ) {
|
||||||
caseExpr.append( " when " )
|
caseExpr.append( " when " )
|
||||||
.append( formatter.apply( whenClause.getCondition(), renderingContext ) )
|
.append( whenClause.getCondition().render( renderingContext ) )
|
||||||
.append( " then " )
|
.append( " then " )
|
||||||
.append( formatter.apply( (Renderable) whenClause.getResult(), renderingContext ) );
|
.append( ( (Renderable) whenClause.getResult() ).render( renderingContext ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
caseExpr.append( " else " )
|
caseExpr.append( " else " )
|
||||||
.append( formatter.apply( (Renderable) getOtherwiseResult(), renderingContext ) )
|
.append( ( (Renderable) getOtherwiseResult() ).render( renderingContext ) )
|
||||||
.append( " end" );
|
.append( " end" );
|
||||||
|
|
||||||
return caseExpr.toString();
|
return caseExpr.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
package org.hibernate.query.criteria.internal.expression.function;
|
package org.hibernate.query.criteria.internal.expression.function;
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
import org.hibernate.query.criteria.internal.CriteriaBuilderImpl;
|
import org.hibernate.query.criteria.internal.CriteriaBuilderImpl;
|
||||||
import org.hibernate.query.criteria.internal.ParameterRegistry;
|
import org.hibernate.query.criteria.internal.ParameterRegistry;
|
||||||
|
@ -47,10 +48,17 @@ public class CastFunction<T,Y>
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String render(RenderingContext renderingContext) {
|
public String render(RenderingContext renderingContext) {
|
||||||
return CAST_NAME + '(' +
|
renderingContext.getFunctionStack().push( this );
|
||||||
castSource.render( renderingContext ) +
|
try {
|
||||||
" as " +
|
return String.format(
|
||||||
renderingContext.getCastType( getJavaType() ) +
|
Locale.ROOT,
|
||||||
')';
|
"cast(%s as %s)",
|
||||||
|
castSource.render( renderingContext ),
|
||||||
|
renderingContext.getCastType( getJavaType() )
|
||||||
|
);
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
renderingContext.getFunctionStack().pop();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -86,16 +86,26 @@ public class LocateFunction
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String render(RenderingContext renderingContext) {
|
public String render(RenderingContext renderingContext) {
|
||||||
StringBuilder buffer = new StringBuilder();
|
renderingContext.getFunctionStack().push( this );
|
||||||
buffer.append( "locate(" )
|
|
||||||
.append( ( (Renderable) getPattern() ).render( renderingContext ) )
|
try {
|
||||||
.append( ',' )
|
final StringBuilder buffer = new StringBuilder();
|
||||||
.append( ( (Renderable) getString() ).render( renderingContext ) );
|
buffer.append( "locate(" )
|
||||||
if ( getStart() != null ) {
|
.append( ( (Renderable) getPattern() ).render( renderingContext ) )
|
||||||
buffer.append( ',' )
|
.append( ',' )
|
||||||
.append( ( (Renderable) getStart() ).render( renderingContext ) );
|
.append( ( (Renderable) getString() ).render( renderingContext ) );
|
||||||
|
|
||||||
|
if ( getStart() != null ) {
|
||||||
|
buffer.append( ',' )
|
||||||
|
.append( ( (Renderable) getStart() ).render( renderingContext ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer.append( ')' );
|
||||||
|
|
||||||
|
return buffer.toString();
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
renderingContext.getFunctionStack().pop();
|
||||||
}
|
}
|
||||||
buffer.append( ')' );
|
|
||||||
return buffer.toString();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -93,19 +93,26 @@ public class ParameterizedFunctionExpression<X>
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String render(RenderingContext renderingContext) {
|
public String render(RenderingContext renderingContext) {
|
||||||
StringBuilder buffer = new StringBuilder();
|
renderingContext.getFunctionStack().push( this );
|
||||||
if ( isStandardJpaFunction() ) {
|
|
||||||
buffer.append( getFunctionName() )
|
try {
|
||||||
.append( "(" );
|
final StringBuilder buffer = new StringBuilder();
|
||||||
|
if ( isStandardJpaFunction() ) {
|
||||||
|
buffer.append( getFunctionName() ).append( "(" );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
buffer.append( "function('" )
|
||||||
|
.append( getFunctionName() )
|
||||||
|
.append( "', " );
|
||||||
|
}
|
||||||
|
|
||||||
|
renderArguments( buffer, renderingContext );
|
||||||
|
|
||||||
|
return buffer.append( ')' ).toString();
|
||||||
}
|
}
|
||||||
else {
|
finally {
|
||||||
buffer.append( "function('" )
|
renderingContext.getFunctionStack().pop();
|
||||||
.append( getFunctionName() )
|
|
||||||
.append( "', " );
|
|
||||||
}
|
}
|
||||||
renderArguments( buffer, renderingContext );
|
|
||||||
buffer.append( ')' );
|
|
||||||
return buffer.toString();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void renderArguments(StringBuilder buffer, RenderingContext renderingContext) {
|
protected void renderArguments(StringBuilder buffer, RenderingContext renderingContext) {
|
||||||
|
|
|
@ -92,16 +92,24 @@ public class SubstringFunction
|
||||||
}
|
}
|
||||||
|
|
||||||
public String render(RenderingContext renderingContext) {
|
public String render(RenderingContext renderingContext) {
|
||||||
StringBuilder buffer = new StringBuilder();
|
renderingContext.getFunctionStack().push( this );
|
||||||
buffer.append( "substring(" )
|
|
||||||
.append( ( (Renderable) getValue() ).render( renderingContext ) )
|
try {
|
||||||
.append( ',' )
|
final StringBuilder buffer = new StringBuilder();
|
||||||
.append( ( (Renderable) getStart() ).render( renderingContext ) );
|
buffer.append( "substring(" )
|
||||||
if ( getLength() != null ) {
|
.append( ( (Renderable) getValue() ).render( renderingContext ) )
|
||||||
buffer.append( ',' )
|
.append( ',' )
|
||||||
.append( ( (Renderable) getLength() ).render( renderingContext ) );
|
.append( ( (Renderable) getStart() ).render( renderingContext ) );
|
||||||
|
|
||||||
|
if ( getLength() != null ) {
|
||||||
|
buffer.append( ',' )
|
||||||
|
.append( ( (Renderable) getLength() ).render( renderingContext ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
return buffer.append( ')' ).toString();
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
renderingContext.getFunctionStack().pop();
|
||||||
}
|
}
|
||||||
buffer.append( ')' );
|
|
||||||
return buffer.toString();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -102,26 +102,31 @@ public class TrimFunction
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String render(RenderingContext renderingContext) {
|
public String render(RenderingContext renderingContext) {
|
||||||
String renderedTrimChar;
|
renderingContext.getFunctionStack().push( this );
|
||||||
if ( trimCharacter.getClass().isAssignableFrom(
|
|
||||||
LiteralExpression.class ) ) {
|
try {
|
||||||
// If the character is a literal, treat it as one. A few dialects
|
String renderedTrimChar;
|
||||||
// do not support parameters as trim() arguments.
|
if ( trimCharacter.getClass().isAssignableFrom( LiteralExpression.class ) ) {
|
||||||
renderedTrimChar = '\'' + ( (LiteralExpression<Character>)
|
// If the character is a literal, treat it as one. A few dialects
|
||||||
trimCharacter ).getLiteral().toString() + '\'';
|
// do not support parameters as trim() arguments.
|
||||||
|
renderedTrimChar = '\'' + ( (LiteralExpression<Character>)
|
||||||
|
trimCharacter ).getLiteral().toString() + '\'';
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
renderedTrimChar = ( (Renderable) trimCharacter ).render( renderingContext );
|
||||||
|
}
|
||||||
|
return new StringBuilder()
|
||||||
|
.append( "trim(" )
|
||||||
|
.append( trimspec.name() )
|
||||||
|
.append( ' ' )
|
||||||
|
.append( renderedTrimChar )
|
||||||
|
.append( " from " )
|
||||||
|
.append( ( (Renderable) trimSource ).render( renderingContext ) )
|
||||||
|
.append( ')' )
|
||||||
|
.toString();
|
||||||
}
|
}
|
||||||
else {
|
finally {
|
||||||
renderedTrimChar = ( (Renderable) trimCharacter ).render(
|
renderingContext.getFunctionStack().pop();
|
||||||
renderingContext );
|
|
||||||
}
|
}
|
||||||
return new StringBuilder()
|
|
||||||
.append( "trim(" )
|
|
||||||
.append( trimspec.name() )
|
|
||||||
.append( ' ' )
|
|
||||||
.append( renderedTrimChar )
|
|
||||||
.append( " from " )
|
|
||||||
.append( ( (Renderable) trimSource ).render( renderingContext ) )
|
|
||||||
.append( ')' )
|
|
||||||
.toString();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,49 @@
|
||||||
|
/*
|
||||||
|
* 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.sql.ast;
|
||||||
|
|
||||||
|
import org.hibernate.Incubating;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used to indicate which query clause we are currently processing
|
||||||
|
*
|
||||||
|
* @author Steve Ebersole
|
||||||
|
*/
|
||||||
|
@Incubating
|
||||||
|
public enum Clause {
|
||||||
|
/**
|
||||||
|
* The insert values clause
|
||||||
|
*/
|
||||||
|
INSERT,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The update set clause
|
||||||
|
*/
|
||||||
|
UPDATE,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Not used in 5.x. Intended for use in 6+ as indicator
|
||||||
|
* of processing predicates (where clause) that occur in a
|
||||||
|
* delete
|
||||||
|
*/
|
||||||
|
DELETE,
|
||||||
|
|
||||||
|
SELECT,
|
||||||
|
FROM,
|
||||||
|
WHERE,
|
||||||
|
GROUP,
|
||||||
|
HAVING,
|
||||||
|
ORDER,
|
||||||
|
LIMIT,
|
||||||
|
CALL,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Again, not used in 5.x. Used in 6+
|
||||||
|
*/
|
||||||
|
IRRELEVANT
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue