diff --git a/src/main/java/com/avaje/ebean/bean/EntityBeanIntercept.java b/src/main/java/com/avaje/ebean/bean/EntityBeanIntercept.java index 4f6f9362a..bf1abcfdf 100644 --- a/src/main/java/com/avaje/ebean/bean/EntityBeanIntercept.java +++ b/src/main/java/com/avaje/ebean/bean/EntityBeanIntercept.java @@ -343,28 +343,10 @@ public final class EntityBeanIntercept implements Serializable { } /** - * Check if the lazy load succeeded. If not then mark this bean as having - * failed lazy loading due to the underlying row being deleted. - *

- * We mark the bean this way rather than immediately fail as we might be batch - * 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. - *

- * @param lazyLoadPropertyIndex the property that is expected to be loaded + * Set lazy load failure flag. */ - 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) { + public void setLazyLoadFailure(Object ownerId) { + this.lazyLoadFailure = true; this.ownerId = ownerId; } diff --git a/src/main/java/com/avaje/ebeaninternal/api/LoadBeanRequest.java b/src/main/java/com/avaje/ebeaninternal/api/LoadBeanRequest.java index caeb2ccb3..a3054fcea 100644 --- a/src/main/java/com/avaje/ebeaninternal/api/LoadBeanRequest.java +++ b/src/main/java/com/avaje/ebeaninternal/api/LoadBeanRequest.java @@ -8,7 +8,9 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.ArrayList; +import java.util.HashSet; import java.util.List; +import java.util.Set; /** * Request for loading ManyToOne and OneToOne relationships. @@ -96,7 +98,7 @@ public class LoadBeanRequest extends LoadRequest { */ public List getIdList(int batchSize) { - ArrayList idList = new ArrayList(batchSize); + List idList = new ArrayList(batchSize); BeanDescriptor desc = loadBuffer.getBeanDescriptor(); for (int i = 0; i < batch.size(); i++) { @@ -124,7 +126,7 @@ public class LoadBeanRequest extends LoadRequest { /** * Configure the query for lazy loading execution. */ - public void configureQuery(SpiQuery query) { + public void configureQuery(SpiQuery query, List idList) { query.setMode(SpiQuery.Mode.LAZYLOAD_BEAN); query.setPersistenceContext(loadBuffer.getPersistenceContext()); @@ -138,6 +140,12 @@ public class LoadBeanRequest extends LoadRequest { } loadBuffer.configureQuery(query, getLazyLoadProperty()); + + if (idList.size() == 1) { + query.where().idEq(idList.get(0)); + } else { + query.where().idIn(idList); + } } /** @@ -145,25 +153,27 @@ public class LoadBeanRequest extends LoadRequest { */ public void postLoad(List list) { - if (isLoadCache()) { - BeanDescriptor desc = loadBuffer.getBeanDescriptor(); - for (int i = 0; i < list.size(); i++) { - desc.cacheBeanPutData((EntityBean) list.get(i)); + Set loadedIds = new HashSet(); + + BeanDescriptor desc = loadBuffer.getBeanDescriptor(); + // collect Ids and maybe load bean cache + for (int i = 0; i < list.size(); i++) { + EntityBean loadedBean = (EntityBean) list.get(i); + loadedIds.add(desc.getId(loadedBean)); + if (isLoadCache()) { + desc.cacheBeanPutData(loadedBean); } } 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"); + Object id = desc.getId(ebi.getOwner()); + if (!loadedIds.contains(id)) { + logger.info("Lazy loading unsuccessful for type:" + desc.getName() + " id:" + id + " - expecting when bean has been deleted"); + ebi.setLazyLoadFailure(id); } } } diff --git a/src/main/java/com/avaje/ebeaninternal/server/core/DefaultBeanLoader.java b/src/main/java/com/avaje/ebeaninternal/server/core/DefaultBeanLoader.java index 3d9e82118..705e18867 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/core/DefaultBeanLoader.java +++ b/src/main/java/com/avaje/ebeaninternal/server/core/DefaultBeanLoader.java @@ -206,16 +206,9 @@ public class DefaultBeanLoader { } SpiQuery query = (SpiQuery) server.createQuery(loadRequest.getBeanType()); - loadRequest.configureQuery(query); - - if (idList.size() == 1) { - query.where().idEq(idList.get(0)); - } else { - query.where().idIn(idList); - } + loadRequest.configureQuery(query, idList); List list = executeQuery(loadRequest, query); - loadRequest.postLoad(list); // log the query (for testing secondary queries) diff --git a/src/test/java/com/avaje/tests/inheritance/TestInheritanceBatchLazyLoad.java b/src/test/java/com/avaje/tests/inheritance/TestInheritanceBatchLazyLoad.java new file mode 100644 index 000000000..3f36fb198 --- /dev/null +++ b/src/test/java/com/avaje/tests/inheritance/TestInheritanceBatchLazyLoad.java @@ -0,0 +1,41 @@ +package com.avaje.tests.inheritance; + +import com.avaje.ebean.Ebean; +import com.avaje.tests.model.basic.Car; +import com.avaje.tests.model.basic.Truck; +import com.avaje.tests.model.basic.Vehicle; +import org.junit.Test; + +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; + + +public class TestInheritanceBatchLazyLoad { + + @Test + public void lazyLoadProperty_when_propertyNotOnAllInheritanceTypes() { + + Car c = new Car(); + c.setLicenseNumber("VZVZ1"); + c.setDriver("CarDriver"); + Ebean.save(c); + + Truck t = new Truck(); + t.setLicenseNumber("VZVZ2"); + t.setCapacity(20D); + Ebean.save(t); + + + List list = Ebean.find(Vehicle.class) + .select("licenseNumber") + .where().startsWith("licenseNumber","VZVZ") + .order().asc("licenseNumber") + .findList(); + + assertThat(list).hasSize(2); + + Car car = (Car)list.get(0); + car.getNotes(); + } +} diff --git a/src/test/java/com/avaje/tests/model/basic/Car.java b/src/test/java/com/avaje/tests/model/basic/Car.java index 9dc74e43d..052ab09a7 100644 --- a/src/test/java/com/avaje/tests/model/basic/Car.java +++ b/src/test/java/com/avaje/tests/model/basic/Car.java @@ -19,12 +19,14 @@ public class Car extends Vehicle { private String driver; @ManyToOne - TruckRef carRef; + private TruckRef carRef; @OneToMany(mappedBy = "car") @OrderBy("fuse.locationCode") private Set accessories = new HashSet(); + private String notes; + public String getDriver() { return driver; } @@ -33,6 +35,14 @@ public class Car extends Vehicle { this.driver = driver; } + public String getNotes() { + return notes; + } + + public void setNotes(String notes) { + this.notes = notes; + } + public TruckRef getCarRef() { return carRef; }