HHH-14897 Allow ordering with nulls first/last in JPA Criteria
This commit is contained in:
parent
69cd716e37
commit
2d871d64f2
|
@ -9,6 +9,7 @@ package org.hibernate.query.criteria;
|
|||
import java.util.Map;
|
||||
import javax.persistence.criteria.CriteriaBuilder;
|
||||
import javax.persistence.criteria.Expression;
|
||||
import javax.persistence.criteria.Order;
|
||||
import javax.persistence.criteria.Predicate;
|
||||
|
||||
/**
|
||||
|
@ -67,4 +68,20 @@ public interface HibernateCriteriaBuilder extends CriteriaBuilder {
|
|||
*/
|
||||
<M extends Map<?,?>> Expression<Integer> mapSize(M map);
|
||||
|
||||
/**
|
||||
* Create an ordering by the ascending value of the expression.
|
||||
* @param x expression used to define the ordering
|
||||
* @param nullsFirst Whether <code>null</code> should be sorted first
|
||||
* @return ascending ordering corresponding to the expression
|
||||
*/
|
||||
Order asc(Expression<?> x, boolean nullsFirst);
|
||||
|
||||
/**
|
||||
* Create an ordering by the descending value of the expression.
|
||||
* @param x expression used to define the ordering
|
||||
* @param nullsFirst Whether <code>null</code> should be sorted first
|
||||
* @return descending ordering corresponding to the expression
|
||||
*/
|
||||
Order desc(Expression<?> x, boolean nullsFirst);
|
||||
|
||||
}
|
||||
|
|
|
@ -250,8 +250,16 @@ public class CriteriaBuilderImpl implements HibernateCriteriaBuilder, Serializab
|
|||
return new OrderImpl( x, false );
|
||||
}
|
||||
|
||||
@Override
|
||||
public Order asc(Expression<?> x, boolean nullsFirst) {
|
||||
return new OrderImpl( x, true, nullsFirst );
|
||||
}
|
||||
|
||||
// predicates ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
@Override
|
||||
public Order desc(Expression<?> x, boolean nullsFirst) {
|
||||
return new OrderImpl( x, false, nullsFirst );
|
||||
}
|
||||
// predicates ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
public Predicate wrap(Expression<Boolean> expression) {
|
||||
if ( Predicate.class.isInstance( expression ) ) {
|
||||
|
|
|
@ -395,6 +395,17 @@ public class CriteriaQueryImpl<T> extends AbstractNode implements CriteriaQuery<
|
|||
jpaqlBuffer.append( sep )
|
||||
.append( ( (Renderable) orderSpec.getExpression() ).render( renderingContext ) )
|
||||
.append( orderSpec.isAscending() ? " asc" : " desc" );
|
||||
if ( orderSpec instanceof OrderImpl ) {
|
||||
Boolean nullsFirst = ( (OrderImpl) orderSpec ).getNullsFirst();
|
||||
if ( nullsFirst != null ) {
|
||||
if ( nullsFirst ) {
|
||||
jpaqlBuffer.append( " nulls first" );
|
||||
}
|
||||
else {
|
||||
jpaqlBuffer.append( " nulls last" );
|
||||
}
|
||||
}
|
||||
}
|
||||
sep = ", ";
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,14 +19,20 @@ public class OrderImpl implements Order, Serializable {
|
|||
|
||||
private final Expression<?> expression;
|
||||
private final boolean ascending;
|
||||
private final Boolean nullsFirst;
|
||||
|
||||
public OrderImpl(Expression<?> expression) {
|
||||
this( expression, true );
|
||||
this( expression, true, null );
|
||||
}
|
||||
|
||||
public OrderImpl(Expression<?> expression, boolean ascending) {
|
||||
this(expression, ascending, null);
|
||||
}
|
||||
|
||||
public OrderImpl(Expression<?> expression, boolean ascending, Boolean nullsFirst) {
|
||||
this.expression = expression;
|
||||
this.ascending = ascending;
|
||||
this.nullsFirst = nullsFirst;
|
||||
}
|
||||
|
||||
public Order reverse() {
|
||||
|
@ -40,4 +46,8 @@ public class OrderImpl implements Order, Serializable {
|
|||
public Expression<?> getExpression() {
|
||||
return expression;
|
||||
}
|
||||
|
||||
public Boolean getNullsFirst() {
|
||||
return nullsFirst;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,90 @@
|
|||
package org.hibernate.query.criteria.internal;
|
||||
|
||||
import java.util.List;
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.TypedQuery;
|
||||
import javax.persistence.criteria.CriteriaQuery;
|
||||
import javax.persistence.criteria.Root;
|
||||
|
||||
import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase;
|
||||
import org.hibernate.query.criteria.HibernateCriteriaBuilder;
|
||||
|
||||
import org.hibernate.testing.TestForIssue;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.hibernate.testing.transaction.TransactionUtil.doInJPA;
|
||||
|
||||
/**
|
||||
* @author Christian Beikov
|
||||
*/
|
||||
@TestForIssue( jiraKey = "HHH-14897" )
|
||||
public class NullPrecedenceTest extends BaseEntityManagerFunctionalTestCase {
|
||||
|
||||
@Override
|
||||
protected Class<?>[] getAnnotatedClasses() {
|
||||
return new Class<?>[] { Foo.class };
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNullPrecedence() {
|
||||
doInJPA( this::entityManagerFactory, entityManager -> {
|
||||
entityManager.persist( new Foo( 1L, null ) );
|
||||
entityManager.persist( new Foo( 2L, "ABC" ) );
|
||||
entityManager.persist( new Foo( 3L, "DEF" ) );
|
||||
entityManager.persist( new Foo( 4L, "DEF" ) );
|
||||
final HibernateCriteriaBuilder cb = (HibernateCriteriaBuilder) entityManager.getCriteriaBuilder();
|
||||
|
||||
final CriteriaQuery<Foo> cq = cb.createQuery( Foo.class );
|
||||
final Root<Foo> foo = cq.from( Foo.class );
|
||||
|
||||
cq.orderBy(
|
||||
cb.desc( foo.get( "bar" ), true ),
|
||||
cb.desc( foo.get( "id" ) )
|
||||
);
|
||||
|
||||
final TypedQuery<Foo> tq = entityManager.createQuery( cq );
|
||||
|
||||
final List<Foo> resultList = tq.getResultList();
|
||||
Assert.assertEquals( 4, resultList.size() );
|
||||
Assert.assertEquals( 1L, resultList.get( 0 ).getId() );
|
||||
Assert.assertEquals( 4L, resultList.get( 1 ).getId() );
|
||||
Assert.assertEquals( 3L, resultList.get( 2 ).getId() );
|
||||
Assert.assertEquals( 2L, resultList.get( 3 ).getId() );
|
||||
} );
|
||||
}
|
||||
|
||||
@Entity(name = "Foo")
|
||||
public static class Foo {
|
||||
|
||||
private long id;
|
||||
private String bar;
|
||||
|
||||
public Foo() {
|
||||
}
|
||||
|
||||
public Foo(long id, String bar) {
|
||||
this.id = id;
|
||||
this.bar = bar;
|
||||
}
|
||||
|
||||
@Id
|
||||
@Column(nullable = false)
|
||||
public long getId() {
|
||||
return this.id;
|
||||
}
|
||||
public void setId(final long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getBar() {
|
||||
return bar;
|
||||
}
|
||||
|
||||
public void setBar(String bar) {
|
||||
this.bar = bar;
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue