mirror of
https://github.com/ebean-orm/ebean.git
synced 2026-10-27 00:02:42 +00:00
#1795 - Refactor bind padding for Id IN expressions (based on internal multiValueSupport and simple id)
This commit is contained in:
@@ -24,6 +24,11 @@ public interface BeanQueryRequest<T> {
|
||||
*/
|
||||
Query<T> getQuery();
|
||||
|
||||
/**
|
||||
* Return true if an Id IN expression should have the bind parameters padded.
|
||||
*/
|
||||
boolean isPadInExpression();
|
||||
|
||||
/**
|
||||
* Return true if multi-value binding using Array or Table Values is supported.
|
||||
*/
|
||||
|
||||
@@ -4,8 +4,6 @@ import io.ebean.bean.EntityBean;
|
||||
import io.ebean.bean.EntityBeanIntercept;
|
||||
import io.ebeaninternal.server.core.OrmQueryRequest;
|
||||
import io.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
@@ -17,8 +15,6 @@ import java.util.Set;
|
||||
*/
|
||||
public class LoadBeanRequest extends LoadRequest {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(LoadBeanRequest.class);
|
||||
|
||||
private final List<EntityBeanIntercept> batch;
|
||||
|
||||
private final LoadBeanBuffer loadBuffer;
|
||||
@@ -56,7 +52,7 @@ public class LoadBeanRequest extends LoadRequest {
|
||||
return loadBuffer.getBeanDescriptor().getBeanType();
|
||||
}
|
||||
|
||||
public boolean isLoadCache() {
|
||||
private boolean isLoadCache() {
|
||||
return loadCache;
|
||||
}
|
||||
|
||||
@@ -75,17 +71,10 @@ public class LoadBeanRequest extends LoadRequest {
|
||||
/**
|
||||
* Return the load context.
|
||||
*/
|
||||
public LoadBeanBuffer getLoadContext() {
|
||||
private LoadBeanBuffer getLoadContext() {
|
||||
return loadBuffer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the property that invoked the lazy loading.
|
||||
*/
|
||||
public String getLazyLoadProperty() {
|
||||
return lazyLoadProperty;
|
||||
}
|
||||
|
||||
public int getBatchSize() {
|
||||
return getLoadContext().getBatchSize();
|
||||
}
|
||||
@@ -93,30 +82,14 @@ public class LoadBeanRequest extends LoadRequest {
|
||||
/**
|
||||
* Return the list of Id values for the beans in the lazy load buffer.
|
||||
*/
|
||||
public List<Object> getIdList(int batchSize) {
|
||||
public List<Object> getIdList() {
|
||||
|
||||
List<Object> idList = new ArrayList<>(batchSize);
|
||||
List<Object> idList = new ArrayList<>();
|
||||
|
||||
BeanDescriptor<?> desc = loadBuffer.getBeanDescriptor();
|
||||
for (EntityBeanIntercept ebi : batch) {
|
||||
EntityBean bean = ebi.getOwner();
|
||||
idList.add(desc.getId(bean));
|
||||
idList.add(desc.getId(ebi.getOwner()));
|
||||
}
|
||||
|
||||
|
||||
if (!desc.isMultiValueIdSupported() && !idList.isEmpty()) {
|
||||
int extraIds = batchSize - batch.size();
|
||||
if (extraIds > 0) {
|
||||
// for performance make up the Id's to the batch size
|
||||
// so we get the same query (for Ebean and the db)
|
||||
Object firstId = idList.get(0);
|
||||
for (int i = 0; i < extraIds; i++) {
|
||||
// just add the first Id again
|
||||
idList.add(firstId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return idList;
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ package io.ebeaninternal.api;
|
||||
import io.ebean.bean.BeanCollection;
|
||||
import io.ebean.bean.EntityBean;
|
||||
import io.ebean.util.StringHelper;
|
||||
import io.ebeaninternal.server.core.BindPadding;
|
||||
import io.ebeaninternal.server.core.OrmQueryRequest;
|
||||
import io.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
import io.ebeaninternal.server.deploy.BeanPropertyAssocMany;
|
||||
@@ -65,13 +66,6 @@ public class LoadManyRequest extends LoadRequest {
|
||||
return batch;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the load context.
|
||||
*/
|
||||
public LoadManyBuffer getLoadContext() {
|
||||
return loadContext;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if lazy loading should only load the id values.
|
||||
* <p>
|
||||
@@ -80,14 +74,14 @@ public class LoadManyRequest extends LoadRequest {
|
||||
* used.
|
||||
* </p>
|
||||
*/
|
||||
public boolean isOnlyIds() {
|
||||
private boolean isOnlyIds() {
|
||||
return onlyIds;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if we should load the Collection ids into the cache.
|
||||
*/
|
||||
public boolean isLoadCache() {
|
||||
private boolean isLoadCache() {
|
||||
return loadCache;
|
||||
}
|
||||
|
||||
@@ -98,22 +92,16 @@ public class LoadManyRequest extends LoadRequest {
|
||||
return loadContext.getBatchSize();
|
||||
}
|
||||
|
||||
private List<Object> getParentIdList(int batchSize) {
|
||||
private List<Object> getParentIdList() {
|
||||
|
||||
ArrayList<Object> idList = new ArrayList<>(batchSize);
|
||||
List<Object> idList = new ArrayList<>();
|
||||
|
||||
BeanPropertyAssocMany<?> many = getMany();
|
||||
for (BeanCollection<?> bc : batch) {
|
||||
idList.add(many.getParentId(bc.getOwnerBean()));
|
||||
}
|
||||
if (!many.getTargetDescriptor().isMultiValueIdSupported()) {
|
||||
int extraIds = batchSize - batch.size();
|
||||
if (extraIds > 0) {
|
||||
Object firstId = idList.get(0);
|
||||
for (int i = 0; i < extraIds; i++) {
|
||||
idList.add(firstId);
|
||||
}
|
||||
}
|
||||
if (many.getTargetDescriptor().isPadInExpression()) {
|
||||
BindPadding.padIds(idList);
|
||||
}
|
||||
|
||||
return idList;
|
||||
@@ -123,7 +111,7 @@ public class LoadManyRequest extends LoadRequest {
|
||||
return loadContext.getBeanProperty();
|
||||
}
|
||||
|
||||
public SpiQuery<?> createQuery(SpiEbeanServer server, int batchSize) {
|
||||
public SpiQuery<?> createQuery(SpiEbeanServer server) {
|
||||
|
||||
BeanPropertyAssocMany<?> many = getMany();
|
||||
|
||||
@@ -142,10 +130,7 @@ public class LoadManyRequest extends LoadRequest {
|
||||
}
|
||||
|
||||
query.setLazyLoadForParents(many);
|
||||
|
||||
List<Object> idList = getParentIdList(batchSize);
|
||||
many.addWhereParentIdIn(query, idList, loadContext.isUseDocStore());
|
||||
|
||||
many.addWhereParentIdIn(query, getParentIdList(), loadContext.isUseDocStore());
|
||||
query.setPersistenceContext(loadContext.getPersistenceContext());
|
||||
|
||||
String mode = isLazy() ? "+lazy" : "+query";
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
package io.ebeaninternal.server.core;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Supports padding bind parameters for IN expressions.
|
||||
* <p>
|
||||
* We do this in order to get better hit ratio on DB query plans.
|
||||
* </p>
|
||||
*/
|
||||
public final class BindPadding {
|
||||
|
||||
/**
|
||||
* Pad out the Ids into common bucket sizes.
|
||||
*
|
||||
* @param idCollection The collection of Ids values being bound.
|
||||
*/
|
||||
public static void padIds(List<Object> idCollection) {
|
||||
|
||||
int extraIds = padding(idCollection.size());
|
||||
if (extraIds > 0) {
|
||||
// for performance make up the Id's to the batch size
|
||||
// so we get the same query (for Ebean and the db)
|
||||
Object firstId = idCollection.get(0);
|
||||
for (int i = 0; i < extraIds; i++) {
|
||||
// just add the first Id again
|
||||
idCollection.add(firstId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Extra padding on binding id's in order to get better hit ratio on DB prepared statements / query plans.
|
||||
*/
|
||||
static int padding(int size) {
|
||||
if (size == 1) {
|
||||
return 0;
|
||||
}
|
||||
if (size <= 5) {
|
||||
return 5 - size;
|
||||
}
|
||||
if (size <= 10) {
|
||||
return 10 - size;
|
||||
}
|
||||
if (size <= 20) {
|
||||
return 20 - size;
|
||||
}
|
||||
if (size <= 40) {
|
||||
return 40 - size;
|
||||
}
|
||||
if (size <= 50) {
|
||||
return 50 - size;
|
||||
}
|
||||
return size <= 100 ? 100 - size : 0;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -38,54 +38,14 @@ class DefaultBeanLoader {
|
||||
this.onIterateUseExtraTxn = server.getDatabasePlatform().useExtraTransactionOnIterateSecondaryQueries();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a batch size that might be less than the requestedBatchSize.
|
||||
* <p>
|
||||
* This means we can have large and variable requestedBatchSizes.
|
||||
* </p>
|
||||
* <p>
|
||||
* We want to restrict the number of different batch sizes as we want to
|
||||
* re-use the query plan cache and get DB statement re-use.
|
||||
* </p>
|
||||
*/
|
||||
private int getBatchSize(int batchSize) {
|
||||
|
||||
if (batchSize == 1) {
|
||||
// there is only one bean/collection to load
|
||||
return 1;
|
||||
}
|
||||
if (batchSize <= 5) {
|
||||
// anything less than 5 becomes 5
|
||||
return 5;
|
||||
}
|
||||
if (batchSize <= 10) {
|
||||
return 10;
|
||||
}
|
||||
if (batchSize <= 20) {
|
||||
return 20;
|
||||
}
|
||||
if (batchSize <= 50) {
|
||||
return 50;
|
||||
}
|
||||
if (batchSize <= 100) {
|
||||
return 100;
|
||||
}
|
||||
return batchSize;
|
||||
}
|
||||
|
||||
void refreshMany(EntityBean parentBean, String propertyName) {
|
||||
refreshMany(parentBean, propertyName, null);
|
||||
}
|
||||
|
||||
void loadMany(LoadManyRequest loadRequest) {
|
||||
|
||||
List<BeanCollection<?>> batch = loadRequest.getBatch();
|
||||
|
||||
int batchSize = getBatchSize(batch.size());
|
||||
|
||||
SpiQuery<?> query = loadRequest.createQuery(server, batchSize);
|
||||
SpiQuery<?> query = loadRequest.createQuery(server);
|
||||
executeQuery(loadRequest, query);
|
||||
|
||||
loadRequest.postLoad();
|
||||
}
|
||||
|
||||
@@ -97,7 +57,7 @@ class DefaultBeanLoader {
|
||||
loadManyInternal(parentBean, propertyName, null, false, onlyIds);
|
||||
}
|
||||
|
||||
void refreshMany(EntityBean parentBean, String propertyName, Transaction t) {
|
||||
private void refreshMany(EntityBean parentBean, String propertyName, Transaction t) {
|
||||
loadManyInternal(parentBean, propertyName, t, true, false);
|
||||
}
|
||||
|
||||
@@ -147,8 +107,7 @@ class DefaultBeanLoader {
|
||||
query.setLoadDescription("+lazy", null);
|
||||
}
|
||||
|
||||
String idProperty = parentDesc.getIdBinder().getIdProperty();
|
||||
query.select(idProperty);
|
||||
query.select(parentDesc.getIdBinder().getIdProperty());
|
||||
|
||||
if (onlyIds) {
|
||||
query.fetch(many.getName(), many.getTargetIdProperty());
|
||||
@@ -192,9 +151,7 @@ class DefaultBeanLoader {
|
||||
throw new RuntimeException("Nothing in batch?");
|
||||
}
|
||||
|
||||
int batchSize = getBatchSize(batch.size());
|
||||
|
||||
List<Object> idList = loadRequest.getIdList(batchSize);
|
||||
List<Object> idList = loadRequest.getIdList();
|
||||
if (idList.isEmpty()) {
|
||||
// everything was loaded from cache
|
||||
return;
|
||||
@@ -315,6 +272,5 @@ class DefaultBeanLoader {
|
||||
}
|
||||
|
||||
desc.resetManyProperties(dbBean);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -115,6 +115,11 @@ public final class OrmQueryRequest<T> extends BeanRequest implements SpiOrmQuery
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPadInExpression() {
|
||||
return beanDescriptor.isPadInExpression();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isMultiValueIdSupported() {
|
||||
return beanDescriptor.isMultiValueIdSupported();
|
||||
|
||||
@@ -1835,6 +1835,13 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
|
||||
return multiValueSupported && isSimpleId();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if Id IN expression should have bind parameters padded.
|
||||
*/
|
||||
public boolean isPadInExpression() {
|
||||
return !multiValueSupported && isSimpleId();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the sql for binding an id. This is the columns with table alias that
|
||||
* make up the id.
|
||||
|
||||
@@ -5,6 +5,7 @@ import io.ebeaninternal.api.ManyWhereJoins;
|
||||
import io.ebeaninternal.api.SpiExpression;
|
||||
import io.ebeaninternal.api.SpiExpressionRequest;
|
||||
import io.ebeaninternal.api.SpiExpressionValidation;
|
||||
import io.ebeaninternal.server.core.BindPadding;
|
||||
import io.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
import io.ebeaninternal.server.deploy.id.IdBinder;
|
||||
|
||||
@@ -46,6 +47,10 @@ public class IdInExpression extends NonPrepareExpression {
|
||||
@Override
|
||||
public void prepareExpression(BeanQueryRequest<?> request) {
|
||||
multiValueIdSupported = request.isMultiValueIdSupported();
|
||||
if (!multiValueIdSupported && !idCollection.isEmpty() && request.isPadInExpression()) {
|
||||
// pad out the ids for better hit ratio on DB query plans
|
||||
BindPadding.padIds(idCollection);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package io.ebeaninternal.server.loadcontext;
|
||||
|
||||
import io.ebean.CacheMode;
|
||||
import io.ebean.bean.BeanLoader;
|
||||
import io.ebean.bean.EntityBeanIntercept;
|
||||
import io.ebean.bean.PersistenceContext;
|
||||
@@ -20,6 +21,8 @@ import java.util.Set;
|
||||
*/
|
||||
class DLoadBeanContext extends DLoadBaseContext implements LoadBeanContext {
|
||||
|
||||
private final boolean cache;
|
||||
|
||||
private List<LoadBuffer> bufferList;
|
||||
|
||||
private LoadBuffer currentBuffer;
|
||||
@@ -29,6 +32,7 @@ class DLoadBeanContext extends DLoadBaseContext implements LoadBeanContext {
|
||||
// bufferList only required when using query joins (queryFetch)
|
||||
this.bufferList = (!queryFetch) ? null : new ArrayList<>();
|
||||
this.currentBuffer = createBuffer(firstBatchSize);
|
||||
this.cache = (queryProps == null) ? false : queryProps.isCache();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -43,6 +47,9 @@ class DLoadBeanContext extends DLoadBaseContext implements LoadBeanContext {
|
||||
|
||||
private void configureQuery(SpiQuery<?> query, String lazyLoadProperty) {
|
||||
|
||||
if (cache) {
|
||||
query.setBeanCacheMode(CacheMode.ON);
|
||||
}
|
||||
setLabel(query);
|
||||
parent.propagateQueryState(query, desc.isDocStoreMapped());
|
||||
query.setParentNode(objectGraphNode);
|
||||
|
||||
Reference in New Issue
Block a user