mirror of
https://github.com/ebean-orm/ebean.git
synced 2024-04-21 10:51:47 +00:00
#535 - Bean lazy loading can execute an extra query when one of the lazy loaded beans has been deleted
This commit is contained in:
@@ -1,5 +1,10 @@
|
||||
package com.avaje.ebean.bean;
|
||||
|
||||
import com.avaje.ebean.Ebean;
|
||||
import com.avaje.ebean.ValuePair;
|
||||
|
||||
import javax.persistence.EntityNotFoundException;
|
||||
import javax.persistence.PersistenceException;
|
||||
import java.beans.PropertyChangeEvent;
|
||||
import java.beans.PropertyChangeListener;
|
||||
import java.beans.PropertyChangeSupport;
|
||||
@@ -11,12 +16,6 @@ import java.util.LinkedHashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.persistence.EntityNotFoundException;
|
||||
import javax.persistence.PersistenceException;
|
||||
|
||||
import com.avaje.ebean.Ebean;
|
||||
import com.avaje.ebean.ValuePair;
|
||||
|
||||
/**
|
||||
* This is the object added to every entity bean using byte code enhancement.
|
||||
* <p>
|
||||
@@ -92,6 +91,8 @@ public final class EntityBeanIntercept implements Serializable {
|
||||
|
||||
private int lazyLoadProperty = -1;
|
||||
|
||||
private Object ownerId;
|
||||
|
||||
/**
|
||||
* Create a intercept with a given entity.
|
||||
* <p>
|
||||
@@ -349,11 +350,22 @@ public final class EntityBeanIntercept implements Serializable {
|
||||
* lazy loading and this bean might not be used by the client code at all.
|
||||
* Instead we will fail as soon as the client code tries to use this bean.
|
||||
* </p>
|
||||
* @param lazyLoadPropertyIndex the property that is expected to be loaded
|
||||
*/
|
||||
public void checkLazyLoadFailure() {
|
||||
if (lazyLoadProperty != -1) {
|
||||
this.lazyLoadFailure = true;
|
||||
public boolean isLazyLoadFailure(int lazyLoadPropertyIndex) {
|
||||
if (lazyLoadProperty != -1 || !isLoadedProperty(lazyLoadPropertyIndex)) {
|
||||
lazyLoadFailure = true;
|
||||
return true;
|
||||
}
|
||||
lazyLoadFailure = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the Id of the owner bean.
|
||||
*/
|
||||
public void setOwnerId(Object ownerId) {
|
||||
this.ownerId = ownerId;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -748,7 +760,7 @@ public final class EntityBeanIntercept implements Serializable {
|
||||
|
||||
if (lazyLoadFailure) {
|
||||
// failed when batch lazy loaded by another bean in the batch
|
||||
throw new EntityNotFoundException("Bean has been deleted - lazy loading failed");
|
||||
throw new EntityNotFoundException("Lazy loading failed on type:" + owner.getClass().getName() + " id:" + ownerId + " - Bean has been deleted");
|
||||
}
|
||||
|
||||
if (lazyLoadProperty == -1) {
|
||||
@@ -763,7 +775,7 @@ public final class EntityBeanIntercept implements Serializable {
|
||||
|
||||
if (lazyLoadFailure) {
|
||||
// failed when lazy loading this bean
|
||||
throw new EntityNotFoundException("Bean has been deleted - lazy loading failed");
|
||||
throw new EntityNotFoundException("Lazy loading failed on type:" + owner.getClass().getName() + " id:" + ownerId + " - Bean has been deleted.");
|
||||
}
|
||||
|
||||
// bean should be loaded and intercepting now. setLoaded() has
|
||||
|
||||
@@ -4,6 +4,8 @@ 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 org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
@@ -13,10 +15,14 @@ import java.util.List;
|
||||
*/
|
||||
public class LoadBeanRequest extends LoadRequest {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(LoadBeanRequest.class);
|
||||
|
||||
private final List<EntityBeanIntercept> batch;
|
||||
|
||||
private final LoadBeanBuffer loadBuffer;
|
||||
|
||||
private final int lazyLoadPropertyIndex;
|
||||
|
||||
private final String lazyLoadProperty;
|
||||
|
||||
private final boolean loadCache;
|
||||
@@ -24,21 +30,24 @@ public class LoadBeanRequest extends LoadRequest {
|
||||
/**
|
||||
* Construct for lazy load request.
|
||||
*/
|
||||
public LoadBeanRequest(LoadBeanBuffer LoadBuffer, String lazyLoadProperty, boolean loadCache) {
|
||||
this(LoadBuffer, null, true, lazyLoadProperty, loadCache);
|
||||
public LoadBeanRequest(LoadBeanBuffer LoadBuffer, int lazyLoadPropertyIndex, String lazyLoadProperty, boolean loadCache) {
|
||||
this(LoadBuffer, null, true, lazyLoadPropertyIndex, lazyLoadProperty, loadCache);
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct for secondary query.
|
||||
*/
|
||||
public LoadBeanRequest(LoadBeanBuffer LoadBuffer, OrmQueryRequest<?> parentRequest) {
|
||||
this(LoadBuffer, parentRequest, false, null, false);
|
||||
this(LoadBuffer, parentRequest, false, -1, null, false);
|
||||
}
|
||||
|
||||
private LoadBeanRequest(LoadBeanBuffer loadBuffer, OrmQueryRequest<?> parentRequest, boolean lazy, String lazyLoadProperty, boolean loadCache) {
|
||||
private LoadBeanRequest(LoadBeanBuffer loadBuffer, OrmQueryRequest<?> parentRequest, boolean lazy,
|
||||
int lazyLoadPropertyIndex, String lazyLoadProperty, boolean loadCache) {
|
||||
|
||||
super(parentRequest, lazy);
|
||||
this.loadBuffer = loadBuffer;
|
||||
this.batch = loadBuffer.getBatch();
|
||||
this.lazyLoadPropertyIndex = lazyLoadPropertyIndex;
|
||||
this.lazyLoadProperty = lazyLoadProperty;
|
||||
this.loadCache = loadCache;
|
||||
}
|
||||
@@ -143,10 +152,20 @@ public class LoadBeanRequest extends LoadRequest {
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
if (lazyLoadPropertyIndex > -1) {
|
||||
// this is a lazy loading query so check for lazy loading failure (due to deleted rows)
|
||||
for (int i = 0; i < batch.size(); i++) {
|
||||
// check if the underlying row in DB was deleted. Mark the bean as 'failed' if
|
||||
// necessary but allow processing to continue until it is accessed by client code
|
||||
EntityBeanIntercept ebi = batch.get(i);
|
||||
// all beans in the batch should have this property loaded now
|
||||
if (ebi.isLazyLoadFailure(lazyLoadPropertyIndex)) {
|
||||
BeanDescriptor<?> desc = loadBuffer.getBeanDescriptor();
|
||||
Object beanId = desc.getId(ebi.getOwner());
|
||||
ebi.setOwnerId(beanId);
|
||||
logger.info("Lazy loading unsuccessful for type:" + desc.getName() + " id:" + beanId + " - expecting when bean has been deleted");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -183,7 +183,7 @@ public class LoadManyRequest extends LoadRequest {
|
||||
if (logger.isDebugEnabled()) {
|
||||
EntityBean ownerBean = bc.getOwnerBean();
|
||||
Object parentId = desc.getId(ownerBean);
|
||||
logger.debug("BeanCollection after lazy load was empty. type[" + ownerBean.getClass() + "] id[" + parentId + "] owner[" + ownerBean + "]");
|
||||
logger.debug("BeanCollection after lazy load was empty. type:" + ownerBean.getClass().getName() + " id:" + parentId + " owner:" + ownerBean);
|
||||
}
|
||||
} else if (isLoadCache()) {
|
||||
Object parentId = desc.getId(bc.getOwnerBean());
|
||||
|
||||
@@ -85,7 +85,7 @@ public class DefaultBeanLoader {
|
||||
|
||||
SpiQuery<?> query = loadRequest.createQuery(server, batchSize);
|
||||
|
||||
executeLazyLoadQuery(loadRequest, query);
|
||||
executeQuery(loadRequest, query);
|
||||
|
||||
loadRequest.postLoad();
|
||||
|
||||
@@ -214,7 +214,7 @@ public class DefaultBeanLoader {
|
||||
query.where().idIn(idList);
|
||||
}
|
||||
|
||||
List<?> list = executeLazyLoadQuery(loadRequest, query);
|
||||
List<?> list = executeQuery(loadRequest, query);
|
||||
|
||||
loadRequest.postLoad(list);
|
||||
|
||||
@@ -225,7 +225,7 @@ public class DefaultBeanLoader {
|
||||
/**
|
||||
* Execute the lazy load query taking into account MySql transaction oddness.
|
||||
*/
|
||||
private List<?> executeLazyLoadQuery(LoadRequest loadRequest, SpiQuery<?> query) {
|
||||
private List<?> executeQuery(LoadRequest loadRequest, SpiQuery<?> query) {
|
||||
if (onIterateUseExtraTxn && loadRequest.isParentFindIterate()) {
|
||||
// MySql - we need a different transaction to execute the secondary query
|
||||
SpiTransaction extraTxn = server.createQueryTransaction();
|
||||
|
||||
@@ -190,7 +190,7 @@ public class DLoadBeanContext extends DLoadBaseContext implements LoadBeanContex
|
||||
}
|
||||
}
|
||||
|
||||
LoadBeanRequest req = new LoadBeanRequest(this, ebi.getLazyLoadProperty(), context.hitCache);
|
||||
LoadBeanRequest req = new LoadBeanRequest(this, ebi.getLazyLoadPropertyIndex(), ebi.getLazyLoadProperty(), context.hitCache);
|
||||
context.desc.getEbeanServer().loadBean(req);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user