Merge pull request #3050 from ebean-orm/feature/LoadBeanRequest

Fix SqlTreeLoadBean to only register new beans into load context
This commit is contained in:
Rob Bygrave
2023-05-09 23:23:31 +12:00
committed by GitHub
3 changed files with 37 additions and 75 deletions
@@ -6,7 +6,11 @@ import io.ebeaninternal.api.SpiQuery.Mode;
import io.ebeaninternal.server.core.OrmQueryRequest;
import io.ebeaninternal.server.deploy.BeanDescriptor;
import java.util.*;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import static java.lang.System.Logger.Level.DEBUG;
@@ -21,6 +25,9 @@ public final class LoadBeanRequest extends LoadRequest {
private final boolean loadCache;
private final boolean alreadyLoaded;
private String triggerEbi;
private String batchBefore;
private List<Object> queryIds;
/**
* Construct for lazy load request.
*/
@@ -56,17 +63,19 @@ public final class LoadBeanRequest extends LoadRequest {
}
/**
* Return the batch of beans to actually load.
* Return true if the batch is empty.
*/
public Set<EntityBeanIntercept> batch() {
public boolean checkEmpty() {
loadBuffer.loadingStarted();
return batch;
batchBefore = String.valueOf(batch);
queryIds = ids();
return batch.isEmpty();
}
/**
* Return the list of Id values for the beans in the lazy load buffer.
*/
public List<Object> ids() {
private List<Object> ids() {
final List<Object> idList = new ArrayList<>(batch.size());
final BeanDescriptor<?> desc = loadBuffer.descriptor();
for (EntityBeanIntercept ebi : batch) {
@@ -78,7 +87,7 @@ public final class LoadBeanRequest extends LoadRequest {
/**
* Configure the query for lazy loading execution.
*/
public void configureQuery(SpiQuery<?> query, List<Object> idList) {
public void configureQuery(SpiQuery<?> query) {
query.setMode(Mode.LAZYLOAD_BEAN);
query.setPersistenceContext(loadBuffer.persistenceContext());
query.setLoadDescription(mode(), description());
@@ -94,10 +103,10 @@ public final class LoadBeanRequest extends LoadRequest {
if (loadCache) {
query.setBeanCacheMode(CacheMode.PUT);
}
if (idList.size() == 1) {
query.where().idEq(idList.get(0));
if (queryIds.size() == 1) {
query.where().idEq(queryIds.get(0));
} else {
query.where().idIn(idList);
query.where().idIn(queryIds);
}
}
@@ -108,7 +117,7 @@ public final class LoadBeanRequest extends LoadRequest {
/**
* Load the beans into the L2 cache if that is requested and check for load failures due to deletes.
*/
public Result postLoad(List<?> list, List<Object> queryIds) {
public void postLoad(List<?> list) {
loadBuffer.loadingStopped();
Set<Object> loadedIds = new HashSet<>();
BeanDescriptor<?> desc = loadBuffer.descriptor();
@@ -137,42 +146,16 @@ public final class LoadBeanRequest extends LoadRequest {
}
}
if (!missed.isEmpty()) {
CoreLog.markedAsDeleted.log(DEBUG, "Loaded bean batch triggered by ebi:{0} property:{1}", triggerEbi, lazyLoadProperty);
if (CoreLog.markedAsDeleted.isLoggable(DEBUG)) {
CoreLog.markedAsDeleted.log(DEBUG, "Loaded bean batch triggered by ebi:{0} property:{1}", triggerEbi, lazyLoadProperty);
CoreLog.markedAsDeleted.log(DEBUG, "Loaded bean batch BEFORE {0}", batchBefore);
CoreLog.markedAsDeleted.log(DEBUG, "Loaded bean batch AFTER {0}", batch);
String msg = MessageFormat.format("Bean added to batch during load for {0} missedIds:{1} queryIds:{2} missed:{3}",
beanType(), missedIds, queryIds, missed);
CoreLog.markedAsDeleted.log(DEBUG, msg, new RuntimeException("LoadBeanRequest - Bean added to batch during load"));
}
}
}
return new Result(loadedIds, missedIds, missed);
}
return EMPTY_RESULT;
}
static final Result EMPTY_RESULT = new Result(Collections.emptySet(),Collections.emptySet(), Collections.emptyList());
public static class Result {
private final Set<Object> loadedIds;
private final Set<Object> missedIds;
private final List<EntityBeanIntercept> missed;
Result(Set<Object> loadedIds, Set<Object> missedIds, List<EntityBeanIntercept> missed) {
this.loadedIds = loadedIds;
this.missedIds = missedIds;
this.missed = missed;
}
public boolean hasMisses() {
return !missedIds.isEmpty();
}
public Set<Object> missedIds() {
return missedIds;
}
public Set<Object> loadedIds() {
return loadedIds;
}
public List<EntityBeanIntercept> missed() {
return missed;
}
}
}
@@ -14,12 +14,9 @@ import io.ebeaninternal.server.deploy.BeanPropertyAssocMany;
import io.ebeaninternal.server.transaction.DefaultPersistenceContext;
import javax.persistence.EntityNotFoundException;
import java.text.MessageFormat;
import java.util.List;
import java.util.Set;
import static java.lang.System.Logger.Level.DEBUG;
import static java.lang.System.Logger.Level.ERROR;
/**
* Helper to handle lazy loading and refreshing of beans.
@@ -126,30 +123,12 @@ final class DefaultBeanLoader {
* Load a batch of beans for +query or +lazy loading.
*/
void loadBean(LoadBeanRequest loadRequest) {
final Set<EntityBeanIntercept> batch = loadRequest.batch();
if (batch.isEmpty()) {
if (loadRequest.checkEmpty()) {
throw new RuntimeException("Nothing in batch?");
}
final String batchBefore = String.valueOf(batch);
final List<Object> ids = loadRequest.ids();
if (ids.isEmpty()) {
// this should never happen given the batch is not empty
CoreLog.internal.log(ERROR, "No Ids when loading Batch buffer");
return;
}
final SpiQuery<?> query = server.createQuery(loadRequest.beanType());
loadRequest.configureQuery(query, ids);
final List<?> list = executeQuery(loadRequest, query);
final LoadBeanRequest.Result result = loadRequest.postLoad(list, ids);
if (result.hasMisses() && CoreLog.markedAsDeleted.isLoggable(DEBUG)) {
CoreLog.markedAsDeleted.log(DEBUG, "Loaded bean batch BEFORE {0}", batchBefore);
CoreLog.markedAsDeleted.log(DEBUG, "Loaded bean batch AFTER {0}", batch);
String msg = MessageFormat.format("Bean added to batch during load for {0} missedIds:{1} ids:{2} loadedIds:{3} sql:{4} missed:{5}",
loadRequest.beanType(), result.missedIds(), ids, result.loadedIds(), query.getGeneratedSql(), result.missed());
CoreLog.markedAsDeleted.log(DEBUG, msg, new RuntimeException("LoadBeanRequest - Bean added to batch during load"));
}
loadRequest.configureQuery(query);
loadRequest.postLoad(executeQuery(loadRequest, query));
}
/**
@@ -147,6 +147,7 @@ class SqlTreeLoadBean implements SqlTreeLoad {
EntityBean contextBean;
SqlBeanLoad sqlBeanLoad;
boolean lazyLoadMany;
boolean usingContextBean;
private Load(DbReadContext ctx, EntityBean parentBean) {
this.ctx = ctx;
@@ -189,6 +190,7 @@ class SqlTreeLoadBean implements SqlTreeLoad {
contextBean = localBean;
} else {
// bean already exists in persistenceContext
usingContextBean = true;
if (ctx.isLoadContextBean()) {
// if explicitly set loadContextBean to true, then reload
localBean = contextBean;
@@ -278,13 +280,11 @@ class SqlTreeLoadBean implements SqlTreeLoad {
if (!partialObject) {
ebi.setFullyLoadedBean(true);
}
} else if (partialObject) {
if (readId) {
// register for lazy loading
ctx.register(null, ebi);
}
} else {
} else if (!partialObject) {
ebi.setFullyLoadedBean(true);
} else if (readId && !usingContextBean) {
// register for lazy loading if bean is new
ctx.register(null, ebi);
}
if (ctx.isAutoTuneProfiling() && !disableLazyLoad) {
@@ -370,7 +370,7 @@ class SqlTreeLoadBean implements SqlTreeLoad {
* context we need to check if it is already contained in the collection.
*/
final boolean isContextBean() {
return localBean == null || queryMode == Mode.LAZYLOAD_BEAN;
return usingContextBean;
}
}