mirror of
https://github.com/ebean-orm/ebean.git
synced 2024-04-21 10:51:47 +00:00
#534 - REFACTOR: Internals for lazy loading specifically improving errors/logging in cases of lazy load errors due to deletes - Initial refactor
This commit is contained in:
@@ -1,9 +1,12 @@
|
||||
package com.avaje.ebeaninternal.api;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import com.avaje.ebean.bean.EntityBean;
|
||||
import com.avaje.ebean.bean.EntityBeanIntercept;
|
||||
import com.avaje.ebeaninternal.server.core.OrmQueryRequest;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Request for loading ManyToOne and OneToOne relationships.
|
||||
@@ -12,7 +15,7 @@ public class LoadBeanRequest extends LoadRequest {
|
||||
|
||||
private final List<EntityBeanIntercept> batch;
|
||||
|
||||
private final LoadBeanBuffer LoadBuffer;
|
||||
private final LoadBeanBuffer loadBuffer;
|
||||
|
||||
private final String lazyLoadProperty;
|
||||
|
||||
@@ -32,20 +35,26 @@ public class LoadBeanRequest extends LoadRequest {
|
||||
this(LoadBuffer, parentRequest, false, null, false);
|
||||
}
|
||||
|
||||
private LoadBeanRequest(LoadBeanBuffer LoadBuffer, OrmQueryRequest<?> parentRequest, boolean lazy, String lazyLoadProperty, boolean loadCache) {
|
||||
private LoadBeanRequest(LoadBeanBuffer loadBuffer, OrmQueryRequest<?> parentRequest, boolean lazy, String lazyLoadProperty, boolean loadCache) {
|
||||
super(parentRequest, lazy);
|
||||
this.LoadBuffer = LoadBuffer;
|
||||
this.batch = LoadBuffer.getBatch();
|
||||
this.loadBuffer = loadBuffer;
|
||||
this.batch = loadBuffer.getBatch();
|
||||
this.lazyLoadProperty = lazyLoadProperty;
|
||||
this.loadCache = loadCache;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getBeanType() {
|
||||
return loadBuffer.getBeanDescriptor().getBeanType();
|
||||
}
|
||||
|
||||
public boolean isLoadCache() {
|
||||
return loadCache;
|
||||
}
|
||||
|
||||
|
||||
public String getDescription() {
|
||||
return "path:" + LoadBuffer.getFullPath() + " batch:" + batch.size();
|
||||
return "path:" + loadBuffer.getFullPath() + " batch:" + batch.size();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -59,7 +68,7 @@ public class LoadBeanRequest extends LoadRequest {
|
||||
* Return the load context.
|
||||
*/
|
||||
public LoadBeanBuffer getLoadContext() {
|
||||
return LoadBuffer;
|
||||
return loadBuffer;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -72,4 +81,72 @@ public class LoadBeanRequest extends LoadRequest {
|
||||
public int getBatchSize() {
|
||||
return getLoadContext().getBatchSize();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the list of Id values for the beans in the lazy load buffer.
|
||||
*/
|
||||
public List<Object> getIdList(int batchSize) {
|
||||
|
||||
ArrayList<Object> idList = new ArrayList<Object>(batchSize);
|
||||
|
||||
BeanDescriptor<?> desc = loadBuffer.getBeanDescriptor();
|
||||
for (int i = 0; i < batch.size(); i++) {
|
||||
EntityBeanIntercept ebi = batch.get(i);
|
||||
EntityBean bean = ebi.getOwner();
|
||||
idList.add(desc.getId(bean));
|
||||
}
|
||||
|
||||
if (!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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure the query for lazy loading execution.
|
||||
*/
|
||||
public void configureQuery(SpiQuery<?> query) {
|
||||
|
||||
query.setMode(SpiQuery.Mode.LAZYLOAD_BEAN);
|
||||
query.setPersistenceContext(loadBuffer.getPersistenceContext());
|
||||
|
||||
String mode = isLazy() ? "+lazy" : "+query";
|
||||
query.setLoadDescription(mode, getDescription());
|
||||
|
||||
if (isLazy()) {
|
||||
// cascade the batch size (if set) for further lazy loading
|
||||
query.setLazyLoadBatchSize(getBatchSize());
|
||||
}
|
||||
|
||||
loadBuffer.configureQuery(query, getLazyLoadProperty());
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the beans into the L2 cache if that is requested and check for load failures due to deletes.
|
||||
*/
|
||||
public void processLoadedBeans(List<?> list) {
|
||||
|
||||
if (isLoadCache()) {
|
||||
BeanDescriptor<?> desc = loadBuffer.getBeanDescriptor();
|
||||
for (int i = 0; i < list.size(); i++) {
|
||||
desc.cacheBeanPutData((EntityBean) list.get(i));
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < batch.size(); i++) {
|
||||
// Check if the underlying row in DB was deleted. Mark this bean as 'failed' if
|
||||
// necessary but allow processing to continue until it is accessed by client code
|
||||
batch.get(i).checkLazyLoadFailure();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,6 +40,11 @@ public class LoadManyRequest extends LoadRequest {
|
||||
this.loadCache = loadCache;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getBeanType() {
|
||||
return loadContext.getBeanDescriptor().getBeanType();
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return "path:" + loadContext.getFullPath() + " size:" + batch.size();
|
||||
}
|
||||
|
||||
@@ -21,6 +21,11 @@ public abstract class LoadRequest {
|
||||
this.lazy = lazy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the associated bean type for this load request.
|
||||
*/
|
||||
public abstract Class<?> getBeanType();
|
||||
|
||||
/**
|
||||
* Log the just executed secondary query with the 'root' query if 'logSecondaryQuery' is set to
|
||||
* true. This is for testing purposes to confirm the secondary query executes etc.
|
||||
|
||||
@@ -6,10 +6,10 @@ import com.avaje.ebean.bean.BeanCollection;
|
||||
import com.avaje.ebean.bean.EntityBean;
|
||||
import com.avaje.ebean.bean.EntityBeanIntercept;
|
||||
import com.avaje.ebean.bean.PersistenceContext;
|
||||
import com.avaje.ebeaninternal.api.LoadBeanBuffer;
|
||||
import com.avaje.ebeaninternal.api.LoadBeanRequest;
|
||||
import com.avaje.ebeaninternal.api.LoadManyBuffer;
|
||||
import com.avaje.ebeaninternal.api.LoadManyRequest;
|
||||
import com.avaje.ebeaninternal.api.LoadRequest;
|
||||
import com.avaje.ebeaninternal.api.SpiQuery;
|
||||
import com.avaje.ebeaninternal.api.SpiQuery.Mode;
|
||||
import com.avaje.ebeaninternal.api.SpiTransaction;
|
||||
@@ -144,17 +144,7 @@ public class DefaultBeanLoader {
|
||||
query.select(many.getTargetIdProperty());
|
||||
}
|
||||
|
||||
if (onIterateUseExtraTxn && loadRequest.isParentFindIterate()) {
|
||||
// MySql - we need a different transaction to execute the secondary query
|
||||
SpiTransaction extraTxn = server.createQueryTransaction();
|
||||
try {
|
||||
server.findList(query, extraTxn);
|
||||
} finally {
|
||||
extraTxn.end();
|
||||
}
|
||||
} else {
|
||||
server.findList(query, loadRequest.getTransaction());
|
||||
}
|
||||
executeLazyLoadQuery(loadRequest, query);
|
||||
|
||||
// check for BeanCollection's that where never processed
|
||||
// in the +query or +lazy load due to no rows (predicates)
|
||||
@@ -274,96 +264,50 @@ public class DefaultBeanLoader {
|
||||
public void loadBean(LoadBeanRequest loadRequest) {
|
||||
|
||||
List<EntityBeanIntercept> batch = loadRequest.getBatch();
|
||||
|
||||
if (batch.isEmpty()) {
|
||||
throw new RuntimeException("Nothing in batch?");
|
||||
}
|
||||
|
||||
int batchSize = getBatchSize(batch.size());
|
||||
|
||||
LoadBeanBuffer ctx = loadRequest.getLoadContext();
|
||||
BeanDescriptor<?> desc = ctx.getBeanDescriptor();
|
||||
|
||||
Class<?> beanType = desc.getBeanType();
|
||||
|
||||
EntityBeanIntercept[] ebis = batch.toArray(new EntityBeanIntercept[batch.size()]);
|
||||
ArrayList<Object> idList = new ArrayList<Object>(batchSize);
|
||||
|
||||
for (int i = 0; i < batch.size(); i++) {
|
||||
EntityBeanIntercept ebi = batch.get(i);
|
||||
EntityBean bean = ebi.getOwner();
|
||||
Object id = desc.getId(bean);
|
||||
idList.add(id);
|
||||
}
|
||||
|
||||
List<Object> idList = loadRequest.getIdList(batchSize);
|
||||
if (idList.isEmpty()) {
|
||||
// everything was loaded from cache
|
||||
return;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
SpiQuery<?> query = (SpiQuery<?>) server.createQuery(loadRequest.getBeanType());
|
||||
loadRequest.configureQuery(query);
|
||||
|
||||
PersistenceContext persistenceContext = ctx.getPersistenceContext();
|
||||
|
||||
SpiQuery<?> query = (SpiQuery<?>) server.createQuery(beanType);
|
||||
|
||||
query.setMode(Mode.LAZYLOAD_BEAN);
|
||||
query.setPersistenceContext(persistenceContext);
|
||||
|
||||
String mode = loadRequest.isLazy() ? "+lazy" : "+query";
|
||||
query.setLoadDescription(mode, loadRequest.getDescription());
|
||||
|
||||
if (loadRequest.isLazy()) {
|
||||
// cascade the batch size (if set) for further lazy loading
|
||||
query.setLazyLoadBatchSize(loadRequest.getBatchSize());
|
||||
}
|
||||
|
||||
ctx.configureQuery(query, loadRequest.getLazyLoadProperty());
|
||||
|
||||
// make sure the query doesn't use the cache
|
||||
// query.setUseCache(false);
|
||||
if (idList.size() == 1) {
|
||||
query.where().idEq(idList.get(0));
|
||||
} else {
|
||||
query.where().idIn(idList);
|
||||
}
|
||||
|
||||
List<?> list;
|
||||
List<?> list = executeLazyLoadQuery(loadRequest, query);
|
||||
|
||||
loadRequest.processLoadedBeans(list);
|
||||
|
||||
// log the query (for testing secondary queries)
|
||||
loadRequest.logSecondaryQuery(query);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the lazy load query taking into account MySql transaction oddness.
|
||||
*/
|
||||
private List<?> executeLazyLoadQuery(LoadRequest loadRequest, SpiQuery<?> query) {
|
||||
if (onIterateUseExtraTxn && loadRequest.isParentFindIterate()) {
|
||||
// MySql - we need a different transaction to execute the secondary query
|
||||
SpiTransaction extraTxn = server.createQueryTransaction();
|
||||
try {
|
||||
list = server.findList(query, extraTxn);
|
||||
return server.findList(query, extraTxn);
|
||||
} finally {
|
||||
extraTxn.end();
|
||||
}
|
||||
} else {
|
||||
list = server.findList(query, loadRequest.getTransaction());
|
||||
return server.findList(query, loadRequest.getTransaction());
|
||||
}
|
||||
|
||||
if (loadRequest.isLoadCache()) {
|
||||
for (int i = 0; i < list.size(); i++) {
|
||||
desc.cacheBeanPutData((EntityBean) list.get(i));
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < ebis.length; i++) {
|
||||
// Check if the underlying row in DB was deleted. Mark this bean as 'failed' if
|
||||
// necessary but allow processing to continue until it is accessed by client code
|
||||
ebis[i].checkLazyLoadFailure();
|
||||
}
|
||||
|
||||
// log the query (for testing secondary queries)
|
||||
loadRequest.logSecondaryQuery(query);
|
||||
}
|
||||
|
||||
public void refresh(EntityBean bean) {
|
||||
|
||||
Reference in New Issue
Block a user