From 2991cb9ad1bbfea855d32511a3f7a26ea4f3c851 Mon Sep 17 00:00:00 2001 From: Robin Bygrave Date: Mon, 11 Jul 2016 15:09:59 +1200 Subject: [PATCH] #768 - Query.setDisableLazyLoading(true) ... not disabling lazy loading on @ManyToOne reference bean --- src/main/java/com/avaje/ebean/BeanState.java | 5 + .../server/core/DefaultBeanState.java | 99 ++++++++++--------- .../server/core/DefaultServer.java | 2 +- .../server/core/PersistRequestBean.java | 2 +- .../server/deploy/AssocOneHelp.java | 8 +- .../server/deploy/AssocOneHelpRefInherit.java | 8 +- .../server/deploy/BeanDescriptor.java | 14 ++- .../deploy/BeanDescriptorCacheHelp.java | 2 +- .../server/deploy/BeanPropertyAssocMany.java | 2 +- .../server/deploy/BeanPropertyAssocOne.java | 2 +- .../server/deploy/DbReadContext.java | 5 + .../server/persist/DefaultPersister.java | 2 +- .../ebeaninternal/server/query/CQuery.java | 8 ++ .../server/query/CQueryFetchIds.java | 9 +- .../server/deploy/BeanDescriptorTest.java | 13 ++- .../batchload/TestQueryDisableLazyLoad.java | 48 +++++++++ 16 files changed, 160 insertions(+), 69 deletions(-) diff --git a/src/main/java/com/avaje/ebean/BeanState.java b/src/main/java/com/avaje/ebean/BeanState.java index aaa830995..68cd12ab5 100644 --- a/src/main/java/com/avaje/ebean/BeanState.java +++ b/src/main/java/com/avaje/ebean/BeanState.java @@ -38,6 +38,11 @@ public interface BeanState { */ void setDisableLazyLoad(boolean disableLazyLoading); + /** + * Return true if the bean has lazy loading disabled. + */ + boolean isDisableLazyLoad(); + /** * Set the loaded state of the property given it's name. * diff --git a/src/main/java/com/avaje/ebeaninternal/server/core/DefaultBeanState.java b/src/main/java/com/avaje/ebeaninternal/server/core/DefaultBeanState.java index a713b5100..d31135827 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/core/DefaultBeanState.java +++ b/src/main/java/com/avaje/ebeaninternal/server/core/DefaultBeanState.java @@ -14,14 +14,14 @@ import java.util.Set; */ public class DefaultBeanState implements BeanState { - private final EntityBean entityBean; - - private final EntityBeanIntercept intercept; - - public DefaultBeanState(EntityBean entityBean){ - this.entityBean = entityBean; - this.intercept = entityBean._ebean_getIntercept(); - } + private final EntityBean entityBean; + + private final EntityBeanIntercept intercept; + + public DefaultBeanState(EntityBean entityBean) { + this.entityBean = entityBean; + this.intercept = entityBean._ebean_getIntercept(); + } public void setPropertyLoaded(String propertyName, boolean loaded) { intercept.setPropertyLoaded(propertyName, loaded); @@ -31,52 +31,57 @@ public class DefaultBeanState implements BeanState { return intercept.isReference(); } - public boolean isNew() { - return intercept.isNew(); - } - - public boolean isNewOrDirty() { - return intercept.isNewOrDirty(); - } - - public boolean isDirty() { - return intercept.isDirty(); - } - - public Set getLoadedProps() { - return intercept.getLoadedPropertyNames(); - } - - public Set getChangedProps() { - return intercept.getDirtyPropertyNames(); + public boolean isNew() { + return intercept.isNew(); } - - public Map getDirtyValues() { + + public boolean isNewOrDirty() { + return intercept.isNewOrDirty(); + } + + public boolean isDirty() { + return intercept.isDirty(); + } + + public Set getLoadedProps() { + return intercept.getLoadedPropertyNames(); + } + + public Set getChangedProps() { + return intercept.getDirtyPropertyNames(); + } + + public Map getDirtyValues() { return intercept.getDirtyValues(); } - - public boolean isReadOnly() { - return intercept.isReadOnly(); - } - - public void setReadOnly(boolean readOnly){ - intercept.setReadOnly(readOnly); - } - - public void addPropertyChangeListener(PropertyChangeListener listener) { - entityBean.addPropertyChangeListener(listener); - } - - public void removePropertyChangeListener(PropertyChangeListener listener) { - entityBean.removePropertyChangeListener(listener); - } - public void setLoaded() { - intercept.setLoaded(); - } + public boolean isReadOnly() { + return intercept.isReadOnly(); + } + + public void setReadOnly(boolean readOnly) { + intercept.setReadOnly(readOnly); + } + + public void addPropertyChangeListener(PropertyChangeListener listener) { + entityBean.addPropertyChangeListener(listener); + } + + public void removePropertyChangeListener(PropertyChangeListener listener) { + entityBean.removePropertyChangeListener(listener); + } + + public void setLoaded() { + intercept.setLoaded(); + } @Override public void setDisableLazyLoad(boolean disableLazyLoading) { intercept.setDisableLazyLoad(disableLazyLoading); } + + @Override + public boolean isDisableLazyLoad() { + return intercept.isDisableLazyLoad(); + } } diff --git a/src/main/java/com/avaje/ebeaninternal/server/core/DefaultServer.java b/src/main/java/com/avaje/ebeaninternal/server/core/DefaultServer.java index 1f97a70cb..30d2304fd 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/core/DefaultServer.java +++ b/src/main/java/com/avaje/ebeaninternal/server/core/DefaultServer.java @@ -557,7 +557,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer { InheritInfo inheritInfo = desc.getInheritInfo(); if (inheritInfo == null) { - return (T)desc.contextRef(pc, null, id); + return (T)desc.contextRef(pc, null, false, id); } BeanProperty idProp = desc.getIdProperty(); diff --git a/src/main/java/com/avaje/ebeaninternal/server/core/PersistRequestBean.java b/src/main/java/com/avaje/ebeaninternal/server/core/PersistRequestBean.java index dbe649870..afd86bfa6 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/core/PersistRequestBean.java +++ b/src/main/java/com/avaje/ebeaninternal/server/core/PersistRequestBean.java @@ -548,7 +548,7 @@ public final class PersistRequestBean extends PersistRequest implements BeanP * Create and return a new reference bean matching this beans Id value. */ public T createReference() { - return beanDescriptor.createReference(Boolean.FALSE, getBeanId(), null); + return beanDescriptor.createReference(Boolean.FALSE, false, getBeanId(), null); } /** diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/AssocOneHelp.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/AssocOneHelp.java index 1faabb50f..93dd8210c 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/AssocOneHelp.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/AssocOneHelp.java @@ -46,9 +46,11 @@ abstract class AssocOneHelp { return existing; } - Object ref = target.contextRef(pc, ctx.isReadOnly(), id); - EntityBeanIntercept ebi = ((EntityBean) ref)._ebean_getIntercept(); - ctx.register(property.name, ebi); + boolean disableLazyLoading = ctx.isDisableLazyLoading(); + Object ref = target.contextRef(pc, ctx.isReadOnly(), disableLazyLoading, id); + if (!disableLazyLoading) { + ctx.register(property.name, ((EntityBean) ref)._ebean_getIntercept()); + } return ref; } diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/AssocOneHelpRefInherit.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/AssocOneHelpRefInherit.java index 6c2634f65..ffb37a85d 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/AssocOneHelpRefInherit.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/AssocOneHelpRefInherit.java @@ -53,9 +53,11 @@ class AssocOneHelpRefInherit extends AssocOneHelp { } // for inheritance hierarchy create the correct type for this row... - Object ref = desc.contextRef(pc, ctx.isReadOnly(), id); - EntityBeanIntercept ebi = ((EntityBean) ref)._ebean_getIntercept(); - ctx.register(property.name, ebi); + boolean disableLazyLoading = ctx.isDisableLazyLoading(); + Object ref = desc.contextRef(pc, ctx.isReadOnly(), disableLazyLoading, id); + if (disableLazyLoading) { + ctx.register(property.name, ((EntityBean) ref)._ebean_getIntercept()); + } return ref; } diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanDescriptor.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanDescriptor.java index 506411cea..7a7790ccc 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanDescriptor.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanDescriptor.java @@ -1569,9 +1569,9 @@ public class BeanDescriptor implements MetaBeanInfo, BeanType { * Create a reference bean based on the id. */ @SuppressWarnings("unchecked") - public T createReference(Boolean readOnly, Object id, PersistenceContext pc) { + public T createReference(Boolean readOnly, boolean disableLazyLoad, Object id, PersistenceContext pc) { - if (cacheSharableBeans && !Boolean.FALSE.equals(readOnly)) { + if (cacheSharableBeans && !disableLazyLoad && !Boolean.FALSE.equals(readOnly)) { CachedBeanData d = cacheHelp.beanCacheGetData(id); if (d != null) { Object shareableBean = d.getSharableBean(); @@ -1588,7 +1588,11 @@ public class BeanDescriptor implements MetaBeanInfo, BeanType { id = convertSetId(id, eb); EntityBeanIntercept ebi = eb._ebean_getIntercept(); - ebi.setBeanLoader(ebeanServer); + if (disableLazyLoad) { + ebi.setDisableLazyLoad(true); + } else { + ebi.setBeanLoader(ebeanServer); + } ebi.setReference(idPropertyIndex); if (Boolean.TRUE == readOnly) { ebi.setReadOnly(true); @@ -1760,8 +1764,8 @@ public class BeanDescriptor implements MetaBeanInfo, BeanType { /** * Create a reference bean and put it in the persistence context (and return it). */ - public Object contextRef(PersistenceContext pc, Boolean readOnly, Object id) { - return createReference(readOnly, id, pc); + public Object contextRef(PersistenceContext pc, Boolean readOnly, boolean disableLazyLoad, Object id) { + return createReference(readOnly, disableLazyLoad, id, pc); } /** diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanDescriptorCacheHelp.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanDescriptorCacheHelp.java index 064b42d5f..3b9e066ed 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanDescriptorCacheHelp.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanDescriptorCacheHelp.java @@ -266,7 +266,7 @@ final class BeanDescriptorCacheHelp { bc.checkEmptyLazyLoad(); for (int i = 0; i < idList.size(); i++) { Object id = idList.get(i); - Object refBean = targetDescriptor.createReference(readOnly, id, persistenceContext); + Object refBean = targetDescriptor.createReference(readOnly, false, id, persistenceContext); many.add(bc, (EntityBean) refBean); } return true; diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanPropertyAssocMany.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanPropertyAssocMany.java index 6be0dd11f..1f008a0d2 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanPropertyAssocMany.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanPropertyAssocMany.java @@ -1028,7 +1028,7 @@ public class BeanPropertyAssocMany extends BeanPropertyAssoc { if (isManyToMany()) { if (liveBean == null) { // add new relationship (Map not allowed here) - liveVal.addBean(targetDescriptor.createReference(Boolean.FALSE, id, null)); + liveVal.addBean(targetDescriptor.createReference(Boolean.FALSE, false, id, null)); } } else { diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanPropertyAssocOne.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanPropertyAssocOne.java index dd503959c..ee70e29c9 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanPropertyAssocOne.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanPropertyAssocOne.java @@ -422,7 +422,7 @@ public class BeanPropertyAssocOne extends BeanPropertyAssoc { // cacheData is the id value, maybe already in persistence context Object assocBean = targetDescriptor.contextGet(context, cacheData); if (assocBean == null) { - assocBean = targetDescriptor.createReference(Boolean.FALSE, cacheData, context); + assocBean = targetDescriptor.createReference(Boolean.FALSE, false, cacheData, context); } setValue(bean, assocBean); } diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/DbReadContext.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/DbReadContext.java index 0581f3288..93634032b 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/DbReadContext.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/DbReadContext.java @@ -84,4 +84,9 @@ public interface DbReadContext { * Return true if the underlying query is a 'asDraft' query. */ boolean isDraftQuery(); + + /** + * Return true if this request disables lazy loading. + */ + boolean isDisableLazyLoading(); } diff --git a/src/main/java/com/avaje/ebeaninternal/server/persist/DefaultPersister.java b/src/main/java/com/avaje/ebeaninternal/server/persist/DefaultPersister.java index f1822ad6d..7883d169e 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/persist/DefaultPersister.java +++ b/src/main/java/com/avaje/ebeaninternal/server/persist/DefaultPersister.java @@ -1294,7 +1294,7 @@ public final class DefaultPersister implements Persister { // convert into a list of reference objects and perform delete by object List refList = new ArrayList(childIds.size()); for (Object id : childIds) { - refList.add(targetDesc.createReference(null, id, null)); + refList.add(targetDesc.createReference(null, false, id, null)); } deleteList(refList, t, softDelete); diff --git a/src/main/java/com/avaje/ebeaninternal/server/query/CQuery.java b/src/main/java/com/avaje/ebeaninternal/server/query/CQuery.java index f2b768c3a..ca37d7976 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/query/CQuery.java +++ b/src/main/java/com/avaje/ebeaninternal/server/query/CQuery.java @@ -107,6 +107,8 @@ public class CQuery implements DbReadContext, CancelableQuery { private final SpiQuery query; + private final boolean disableLazyLoading; + private Map currentPathMap; private String currentPrefix; @@ -197,6 +199,7 @@ public class CQuery implements DbReadContext, CancelableQuery { this.lazyLoadManyProperty = query.getLazyLoadMany(); this.readOnly = request.isReadOnly(); + this.disableLazyLoading = query.isDisableLazyLoading(); this.objectGraphNode = query.getParentNode(); this.profilingListener = query.getProfilingListener(); @@ -239,6 +242,11 @@ public class CQuery implements DbReadContext, CancelableQuery { return query.isAsDraft(); } + @Override + public boolean isDisableLazyLoading() { + return disableLazyLoading; + } + public Boolean isReadOnly() { return readOnly; } diff --git a/src/main/java/com/avaje/ebeaninternal/server/query/CQueryFetchIds.java b/src/main/java/com/avaje/ebeaninternal/server/query/CQueryFetchIds.java index 31da74715..7000e6558 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/query/CQueryFetchIds.java +++ b/src/main/java/com/avaje/ebeaninternal/server/query/CQueryFetchIds.java @@ -197,7 +197,7 @@ public class CQueryFetchIds { dataReader = null; } } catch (SQLException e) { - logger.error(null, e); + logger.error("Error closing DataReader", e); } try { if (pstmt != null) { @@ -205,7 +205,7 @@ public class CQueryFetchIds { pstmt = null; } } catch (SQLException e) { - logger.error(null, e); + logger.error("Error closing PreparedStatement", e); } } @@ -228,6 +228,11 @@ public class CQueryFetchIds { return Boolean.FALSE; } + @Override + public boolean isDisableLazyLoading() { + return false; + } + public boolean isRawSql() { return false; } diff --git a/src/test/java/com/avaje/ebeaninternal/server/deploy/BeanDescriptorTest.java b/src/test/java/com/avaje/ebeaninternal/server/deploy/BeanDescriptorTest.java index eda9a52d4..b01379ed2 100644 --- a/src/test/java/com/avaje/ebeaninternal/server/deploy/BeanDescriptorTest.java +++ b/src/test/java/com/avaje/ebeaninternal/server/deploy/BeanDescriptorTest.java @@ -18,7 +18,7 @@ public class BeanDescriptorTest extends BaseTestCase { @Test public void createReference() { - Customer bean = customerDesc.createReference(null, 42, null); + Customer bean = customerDesc.createReference(null, false, 42, null); assertThat(bean.getId()).isEqualTo(42); assertThat(server().getBeanState(bean).isReadOnly()).isFalse(); } @@ -26,17 +26,24 @@ public class BeanDescriptorTest extends BaseTestCase { @Test public void createReference_whenReadOnly() { - Customer bean = customerDesc.createReference(Boolean.TRUE, 42, null); + Customer bean = customerDesc.createReference(Boolean.TRUE, false, 42, null); assertThat(server().getBeanState(bean).isReadOnly()).isTrue(); } @Test public void createReference_whenNotReadOnly() { - Customer bean = customerDesc.createReference(Boolean.FALSE, 42, null); + Customer bean = customerDesc.createReference(Boolean.FALSE, false, 42, null); assertThat(server().getBeanState(bean).isReadOnly()).isFalse(); } + @Test + public void createReference_when_disabledLazyLoad() { + + Customer bean = customerDesc.createReference(Boolean.FALSE, true, 42, null); + assertThat(server().getBeanState(bean).isDisableLazyLoad()).isTrue(); + } + @Test public void allProperties() { diff --git a/src/test/java/com/avaje/tests/batchload/TestQueryDisableLazyLoad.java b/src/test/java/com/avaje/tests/batchload/TestQueryDisableLazyLoad.java index 54b18bf49..934d1f71c 100644 --- a/src/test/java/com/avaje/tests/batchload/TestQueryDisableLazyLoad.java +++ b/src/test/java/com/avaje/tests/batchload/TestQueryDisableLazyLoad.java @@ -11,6 +11,7 @@ import java.util.List; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; public class TestQueryDisableLazyLoad { @@ -39,4 +40,51 @@ public class TestQueryDisableLazyLoad { assertThat(loggedSql.get(0)).contains("select t0.id c0, t0.status c1, t0.order_date c2,"); assertThat(loggedSql.get(0)).contains(" from o_order t0 "); } + + @Test + public void onAssocOne() { + + ResetBasicData.reset(); + + LoggedSqlCollector.start(); + + List l0 = Ebean.find(Order.class) + .setDisableLazyLoading(true) + .order().asc("id") + .findList(); + + assertThat(l0).isNotEmpty(); + + Order order = l0.get(0); + + // normally invokes lazy loading + assertNull(order.getCustomer().getStatus()); + + List loggedSql = LoggedSqlCollector.stop(); + assertThat(loggedSql).hasSize(1); + } + + @Test + public void onAssocOne_when_partial() { + + ResetBasicData.reset(); + + LoggedSqlCollector.start(); + + List l0 = Ebean.find(Order.class) + .setDisableLazyLoading(true) + .fetch("customer","smallnote") + .order().asc("id") + .findList(); + + assertThat(l0).isNotEmpty(); + + Order order = l0.get(0); + + // normally invokes lazy loading + assertNull(order.getCustomer().getStatus()); + + List loggedSql = LoggedSqlCollector.stop(); + assertThat(loggedSql).hasSize(1); + } }