diff --git a/src/main/java/com/avaje/ebeaninternal/api/LoadBeanRequest.java b/src/main/java/com/avaje/ebeaninternal/api/LoadBeanRequest.java index ce56db5a9..24c0a5c59 100644 --- a/src/main/java/com/avaje/ebeaninternal/api/LoadBeanRequest.java +++ b/src/main/java/com/avaje/ebeaninternal/api/LoadBeanRequest.java @@ -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 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 getIdList(int batchSize) { + + ArrayList idList = new ArrayList(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(); + } + } } diff --git a/src/main/java/com/avaje/ebeaninternal/api/LoadManyRequest.java b/src/main/java/com/avaje/ebeaninternal/api/LoadManyRequest.java index 16ef3f906..9df25dccc 100644 --- a/src/main/java/com/avaje/ebeaninternal/api/LoadManyRequest.java +++ b/src/main/java/com/avaje/ebeaninternal/api/LoadManyRequest.java @@ -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(); } diff --git a/src/main/java/com/avaje/ebeaninternal/api/LoadRequest.java b/src/main/java/com/avaje/ebeaninternal/api/LoadRequest.java index 92ec982d3..89c2c7fdf 100644 --- a/src/main/java/com/avaje/ebeaninternal/api/LoadRequest.java +++ b/src/main/java/com/avaje/ebeaninternal/api/LoadRequest.java @@ -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. 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 3ef642c39..05c81e907 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/core/DefaultBeanLoader.java +++ b/src/main/java/com/avaje/ebeaninternal/server/core/DefaultBeanLoader.java @@ -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 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 idList = new ArrayList(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 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) {