mirror of
https://github.com/ebean-orm/ebean.git
synced 2024-04-21 10:51:47 +00:00
auomatically append "order by id" when paging and query.orderById = true (#1275)
Add ServerConfig.setDefaultOrderById() and query.orderById(boolean) ... to control automatically append "order by id" with paging query
This commit is contained in:
committed by
Rob Bygrave
parent
0cba0664f8
commit
a712be2917
@@ -1507,4 +1507,12 @@ public interface Query<T> {
|
||||
*/
|
||||
Set<String> validate();
|
||||
|
||||
/**
|
||||
* Controls, if paginated queries should always append an 'order by id' statement at the end to
|
||||
* guarantee a deterministic sort result. This may affect performance.
|
||||
* If this is not enabled, and an orderBy is set on the query, it's up to the programmer that
|
||||
* this query provides a deterministic result.
|
||||
*/
|
||||
Query<T> orderById(boolean orderById);
|
||||
|
||||
}
|
||||
|
||||
@@ -488,6 +488,11 @@ public class ServerConfig {
|
||||
|
||||
private ProfilingConfig profilingConfig = new ProfilingConfig();
|
||||
|
||||
/**
|
||||
* Controls the default order by id setting of queries. See {@link Query#orderById(boolean)}
|
||||
*/
|
||||
private boolean defaultOrderById = false;
|
||||
|
||||
/**
|
||||
* Construct a Server Configuration for programmatically creating an EbeanServer.
|
||||
*/
|
||||
@@ -523,6 +528,21 @@ public class ServerConfig {
|
||||
this.slowQueryListener = slowQueryListener;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sets the default orderById setting for queries.
|
||||
*/
|
||||
public void setDefaultOrderById(boolean defaultOrderById) {
|
||||
this.defaultOrderById = defaultOrderById;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the default orderById setting for queries.
|
||||
*/
|
||||
public boolean isDefaultOrderById() {
|
||||
return defaultOrderById;
|
||||
}
|
||||
|
||||
/**
|
||||
* Put a service object into configuration such that it can be passed to a plugin.
|
||||
* <p>
|
||||
@@ -2767,6 +2787,7 @@ public class ServerConfig {
|
||||
databaseBooleanTrue = p.get("databaseBooleanTrue", databaseBooleanTrue);
|
||||
databaseBooleanFalse = p.get("databaseBooleanFalse", databaseBooleanFalse);
|
||||
databasePlatformName = p.get("databasePlatformName", databasePlatformName);
|
||||
defaultOrderById = p.getBoolean("defaultOrderById", defaultOrderById);
|
||||
|
||||
DbUuid dbUuid = p.getEnum(DbUuid.class, "dbuuid", null);
|
||||
if (dbUuid != null) {
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package io.ebeaninternal.api;
|
||||
|
||||
import io.ebean.CacheMode;
|
||||
import io.ebean.EbeanServer;
|
||||
import io.ebean.ExpressionList;
|
||||
import io.ebean.OrderBy;
|
||||
import io.ebean.PersistenceContextScope;
|
||||
@@ -338,7 +337,7 @@ public interface SpiQuery<T> extends Query<T>, TxnProfileEventCodes {
|
||||
/**
|
||||
* Return a copy of the query attaching to a different EbeanServer.
|
||||
*/
|
||||
SpiQuery<T> copy(EbeanServer server);
|
||||
SpiQuery<T> copy(SpiEbeanServer server);
|
||||
|
||||
/**
|
||||
* Return the type of query (List, Set, Map, Bean, rowCount etc).
|
||||
@@ -593,6 +592,11 @@ public interface SpiQuery<T> extends Query<T>, TxnProfileEventCodes {
|
||||
*/
|
||||
boolean checkPagingOrderBy();
|
||||
|
||||
/**
|
||||
* Return true if there is no Order By clause.
|
||||
*/
|
||||
boolean orderByIsEmpty();
|
||||
|
||||
/**
|
||||
* Return the Order By clause or null if there is none defined.
|
||||
*/
|
||||
|
||||
@@ -3053,13 +3053,8 @@ public class BeanDescriptor<T> implements BeanType<T> {
|
||||
*/
|
||||
public void appendOrderById(SpiQuery<T> query) {
|
||||
|
||||
if (idProperty != null && !idProperty.isEmbedded()) {
|
||||
SpiRawSql rawSql = query.getRawSql();
|
||||
if (rawSql != null) {
|
||||
query.order(rawSql.getSql().getOrderBy());
|
||||
} else {
|
||||
query.order().asc(idProperty.getName());
|
||||
}
|
||||
if (idProperty != null && !idProperty.isEmbedded() && !query.order().containsProperty(idProperty.getName())) {
|
||||
query.order().asc(idProperty.getName());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -190,7 +190,6 @@ public class CQueryEngine {
|
||||
*/
|
||||
public <T> QueryIterator<T> findIterate(OrmQueryRequest<T> request) {
|
||||
|
||||
prepareForPaging(request);
|
||||
CQuery<T> cquery = queryBuilder.buildQuery(request);
|
||||
request.setCancelableQuery(cquery);
|
||||
|
||||
@@ -326,24 +325,12 @@ public class CQueryEngine {
|
||||
return historySupport.getSysPeriodLower(rootTableAlias);
|
||||
}
|
||||
|
||||
/**
|
||||
* deemed to be a be a paging query - check that the order by contains the id
|
||||
* property to ensure unique row ordering for predicable paging but only in
|
||||
* case, this is not a distinct query
|
||||
*/
|
||||
private <T> void prepareForPaging(OrmQueryRequest<T> request) {
|
||||
SpiQuery<T> query = request.getQuery();
|
||||
if (query.checkPagingOrderBy()) {
|
||||
request.getBeanDescriptor().appendOrderById(query);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Find a list/map/set of beans.
|
||||
*/
|
||||
<T> BeanCollection<T> findMany(OrmQueryRequest<T> request) {
|
||||
|
||||
prepareForPaging(request);
|
||||
|
||||
CQuery<T> cquery = queryBuilder.buildQuery(request);
|
||||
request.setCancelableQuery(cquery);
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package io.ebeaninternal.server.querydefn;
|
||||
|
||||
import io.ebean.CacheMode;
|
||||
import io.ebean.EbeanServer;
|
||||
import io.ebean.Expression;
|
||||
import io.ebean.ExpressionFactory;
|
||||
import io.ebean.ExpressionList;
|
||||
@@ -32,6 +31,7 @@ import io.ebeaninternal.api.CQueryPlanKey;
|
||||
import io.ebeaninternal.api.HashQuery;
|
||||
import io.ebeaninternal.api.ManyWhereJoins;
|
||||
import io.ebeaninternal.api.NaturalKeyQueryData;
|
||||
import io.ebeaninternal.api.SpiEbeanServer;
|
||||
import io.ebeaninternal.api.SpiExpression;
|
||||
import io.ebeaninternal.api.SpiExpressionList;
|
||||
import io.ebeaninternal.api.SpiExpressionValidation;
|
||||
@@ -39,6 +39,7 @@ import io.ebeaninternal.api.SpiNamedParam;
|
||||
import io.ebeaninternal.api.SpiQuery;
|
||||
import io.ebeaninternal.api.SpiQuerySecondary;
|
||||
import io.ebeaninternal.server.autotune.ProfilingListener;
|
||||
import io.ebeaninternal.server.core.OrmQueryRequest;
|
||||
import io.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
import io.ebeaninternal.server.deploy.BeanPropertyAssocMany;
|
||||
import io.ebeaninternal.server.deploy.TableJoin;
|
||||
@@ -75,7 +76,7 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
|
||||
|
||||
private final BeanDescriptor<T> beanDescriptor;
|
||||
|
||||
private final EbeanServer server;
|
||||
private final SpiEbeanServer server;
|
||||
|
||||
private final ExpressionFactory expressionFactory;
|
||||
|
||||
@@ -254,6 +255,8 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
|
||||
|
||||
private String nativeSql;
|
||||
|
||||
private boolean orderById;
|
||||
|
||||
/**
|
||||
* Identity the query for profiling purposes (expected to be unique for a bean type).
|
||||
*/
|
||||
@@ -261,10 +264,11 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
|
||||
|
||||
private ProfileLocation profileLocation;
|
||||
|
||||
public DefaultOrmQuery(BeanDescriptor<T> desc, EbeanServer server, ExpressionFactory expressionFactory) {
|
||||
public DefaultOrmQuery(BeanDescriptor<T> desc, SpiEbeanServer server, ExpressionFactory expressionFactory) {
|
||||
this.beanDescriptor = desc;
|
||||
this.beanType = desc.getBeanType();
|
||||
this.server = server;
|
||||
this.orderById = server.getServerConfig().isDefaultOrderById();
|
||||
this.expressionFactory = expressionFactory;
|
||||
this.detail = new OrmQueryDetail();
|
||||
}
|
||||
@@ -279,6 +283,7 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean isFindAll() {
|
||||
return whereExpressions == null && nativeSql == null && rawSql == null;
|
||||
}
|
||||
@@ -304,6 +309,7 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public short getProfileId() {
|
||||
return profileId;
|
||||
}
|
||||
@@ -717,8 +723,7 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public DefaultOrmQuery<T> copy(EbeanServer server) {
|
||||
|
||||
public DefaultOrmQuery<T> copy(SpiEbeanServer server) {
|
||||
DefaultOrmQuery<T> copy = new DefaultOrmQuery<>(beanDescriptor, server, expressionFactory);
|
||||
copy.m2mIncludeJoin = m2mIncludeJoin;
|
||||
copy.profilingListener = profilingListener;
|
||||
@@ -742,6 +747,7 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
|
||||
if (orderBy != null) {
|
||||
copy.orderBy = orderBy.copy();
|
||||
}
|
||||
copy.orderById = orderById;
|
||||
if (bindParams != null) {
|
||||
copy.bindParams = bindParams.copy();
|
||||
}
|
||||
@@ -1037,6 +1043,7 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
|
||||
public CQueryPlanKey prepare(BeanQueryRequest<?> request) {
|
||||
|
||||
prepareExpressions(request);
|
||||
prepareForPaging((OrmQueryRequest) request);
|
||||
queryPlanKey = createQueryPlanKey();
|
||||
return queryPlanKey;
|
||||
}
|
||||
@@ -1054,6 +1061,24 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* deemed to be a be a paging query - check that the order by contains the id
|
||||
* property to ensure unique row ordering for predicable paging but only in
|
||||
* case, this is not a distinct query
|
||||
*/
|
||||
private void prepareForPaging(OrmQueryRequest<T> request) {
|
||||
|
||||
// add the rawSql statement - if any
|
||||
if (orderByIsEmpty()) {
|
||||
SpiRawSql rawSql = getRawSql();
|
||||
if (rawSql != null && rawSql.getSql() != null) {
|
||||
order(rawSql.getSql().getOrderBy());
|
||||
}
|
||||
}
|
||||
if (checkPagingOrderBy()) {
|
||||
request.getBeanDescriptor().appendOrderById(this);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Calculate a hash based on the bind values used in the query.
|
||||
* <p>
|
||||
@@ -1388,10 +1413,11 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
|
||||
|
||||
@Override
|
||||
public boolean checkPagingOrderBy() {
|
||||
return (maxRows > 1 || firstRow > 0) && !distinct && orderByIsEmpty();
|
||||
return (maxRows > 1 || firstRow > 0) && !distinct && (orderByIsEmpty() || isOrderById());
|
||||
}
|
||||
|
||||
private boolean orderByIsEmpty() {
|
||||
@Override
|
||||
public boolean orderByIsEmpty() {
|
||||
return orderBy == null || orderBy.isEmpty();
|
||||
}
|
||||
|
||||
@@ -1776,4 +1802,14 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
|
||||
public ProfileLocation getProfileLocation() {
|
||||
return profileLocation;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Query<T> orderById(boolean orderById) {
|
||||
this.orderById = orderById;
|
||||
return this;
|
||||
}
|
||||
|
||||
public boolean isOrderById() {
|
||||
return orderById;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user