From dba245c5176d50a456f3771e60c91303b5ca8878 Mon Sep 17 00:00:00 2001 From: Robin Bygrave Date: Tue, 22 Sep 2015 13:21:19 +1200 Subject: [PATCH] #421 - ENH: Add support for unsetting the loaded state of a bean property (via BeanState) --- src/main/java/com/avaje/ebean/BeanState.java | 27 +++++++ src/main/java/com/avaje/ebean/Model.java | 27 ++++++- .../avaje/ebean/bean/EntityBeanIntercept.java | 13 +++- .../server/core/DefaultBeanState.java | 18 +++-- .../ebean/TestUnsetLoadedProperties.java | 71 +++++++++++++------ 5 files changed, 124 insertions(+), 32 deletions(-) diff --git a/src/main/java/com/avaje/ebean/BeanState.java b/src/main/java/com/avaje/ebean/BeanState.java index 51e54d940..aaa830995 100644 --- a/src/main/java/com/avaje/ebean/BeanState.java +++ b/src/main/java/com/avaje/ebean/BeanState.java @@ -38,6 +38,33 @@ public interface BeanState { */ void setDisableLazyLoad(boolean disableLazyLoading); + /** + * Set the loaded state of the property given it's name. + * + *

+ * Typically this would be used to set the loaded state of a property + * to false to ensure that the specific property is excluded from a + * stateless update. + *

+ * + *
{@code
+   *
+   *   // populate a bean via say JSON
+   *   User user = ...;
+   *
+   *   // set loaded state on the email property to false so that
+   *   // the email property is not included in a stateless update
+   *   Ebean.getBeanState(user).setPropertyLoaded("email", false);
+   *
+   *   user.update();
+   *
+   * }
+ * + * + * This will throw an IllegalArgumentException if the property is unknown. + */ + void setPropertyLoaded(String propertyName, boolean loaded); + /** * For partially populated beans returns the properties that are loaded on the * bean. diff --git a/src/main/java/com/avaje/ebean/Model.java b/src/main/java/com/avaje/ebean/Model.java index 79366e8c6..49c9f0d63 100644 --- a/src/main/java/com/avaje/ebean/Model.java +++ b/src/main/java/com/avaje/ebean/Model.java @@ -1,5 +1,6 @@ package com.avaje.ebean; +import com.avaje.ebean.bean.EntityBean; import com.avaje.ebean.text.PathProperties; import com.avaje.ebean.util.ClassUtil; import org.jetbrains.annotations.Nullable; @@ -193,7 +194,31 @@ public abstract class Model { public void markAsDirty() { db().markAsDirty(this); } - + + /** + * Mark the property as unset or 'not loaded'. + *

+ * This would be used to specify a property that we did not wish to include in a stateless update. + *

+ *
{@code
+   *
+   *   // populate an entity bean from JSON or whatever
+   *   User user = ...;
+   *
+   *   // mark the email property as 'unset' so that it is not
+   *   // included in a 'stateless update'
+   *   user.markPropertyUnset("email");
+   *
+   *   user.update();
+   *
+   * }
+ * + * @param propertyName the name of the property on the bean to be marked as 'unset' + */ + public void markPropertyUnset(String propertyName) { + ((EntityBean)this)._ebean_getIntercept().setPropertyLoaded(propertyName, false); + } + /** * Insert or update this entity depending on its state. * diff --git a/src/main/java/com/avaje/ebean/bean/EntityBeanIntercept.java b/src/main/java/com/avaje/ebean/bean/EntityBeanIntercept.java index 42d67e7c8..e1e2845a1 100644 --- a/src/main/java/com/avaje/ebean/bean/EntityBeanIntercept.java +++ b/src/main/java/com/avaje/ebean/bean/EntityBeanIntercept.java @@ -449,7 +449,18 @@ public final class EntityBeanIntercept implements Serializable { public int getPropertyLength() { return owner._ebean_getPropertyNames().length; } - + + /** + * Set the loaded state of the property given it's name. + */ + public void setPropertyLoaded(String propertyName, boolean loaded) { + int position = findProperty(propertyName); + if (position == -1) { + throw new IllegalArgumentException("Property "+propertyName+" not found"); + } + loadedProps[position] = loaded; + } + /** * Set the property to be treated as unloaded. Used for properties initialised in default * constructor. 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 daa516599..a713b5100 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/core/DefaultBeanState.java +++ b/src/main/java/com/avaje/ebeaninternal/server/core/DefaultBeanState.java @@ -1,14 +1,14 @@ package com.avaje.ebeaninternal.server.core; -import java.beans.PropertyChangeListener; -import java.util.Map; -import java.util.Set; - import com.avaje.ebean.BeanState; import com.avaje.ebean.ValuePair; import com.avaje.ebean.bean.EntityBean; import com.avaje.ebean.bean.EntityBeanIntercept; +import java.beans.PropertyChangeListener; +import java.util.Map; +import java.util.Set; + /** * Default implementation of BeanState. */ @@ -23,9 +23,13 @@ public class DefaultBeanState implements BeanState { this.intercept = entityBean._ebean_getIntercept(); } - public boolean isReference() { - return intercept.isReference(); - } + public void setPropertyLoaded(String propertyName, boolean loaded) { + intercept.setPropertyLoaded(propertyName, loaded); + } + + public boolean isReference() { + return intercept.isReference(); + } public boolean isNew() { return intercept.isNew(); diff --git a/src/test/java/com/avaje/ebean/TestUnsetLoadedProperties.java b/src/test/java/com/avaje/ebean/TestUnsetLoadedProperties.java index 894b300b4..fa6937e69 100644 --- a/src/test/java/com/avaje/ebean/TestUnsetLoadedProperties.java +++ b/src/test/java/com/avaje/ebean/TestUnsetLoadedProperties.java @@ -1,7 +1,6 @@ package com.avaje.ebean; import com.avaje.ebean.bean.EntityBean; -import com.avaje.ebean.bean.EntityBeanIntercept; import com.avaje.tests.model.converstation.User; import org.avaje.ebeantest.LoggedSqlCollector; import org.junit.Test; @@ -9,7 +8,6 @@ import org.junit.Test; import java.util.List; import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.Assert.assertTrue; public class TestUnsetLoadedProperties extends BaseTestCase { @@ -30,22 +28,10 @@ public class TestUnsetLoadedProperties extends BaseTestCase { user.setEmail("change@junk.com"); - // confirm it's loaded state - EntityBean eb = (EntityBean)user; - EntityBeanIntercept ebi = eb._ebean_getIntercept(); - - int namePosition = ebi.findProperty("name"); - assertTrue(ebi.isLoadedProperty(namePosition)); - - int emailPosition = ebi.findProperty("email"); - assertTrue(ebi.isLoadedProperty(emailPosition)); - BeanState beanState = Ebean.getBeanState(user); assertThat(beanState.getLoadedProps()).containsExactly("id", "name", "email"); - // unset the loaded state for email - //ebi.setPropertyUnloaded(emailPosition); - unloadProperty(user, "email"); + user.markPropertyUnset("email"); assertThat(beanState.getLoadedProps()).containsExactly("id", "name"); @@ -58,16 +44,55 @@ public class TestUnsetLoadedProperties extends BaseTestCase { } - private void unloadProperty(Object entityBean, String propertyName) { + @Test + public void testUnloadVia_EntityBeanIntercept_setPropertyLoaded() { - EntityBean eb = (EntityBean)entityBean; - EntityBeanIntercept ebi = eb._ebean_getIntercept(); - int position = ebi.findProperty(propertyName); - if (position == -1) { - throw new RuntimeException("Property "+propertyName+" not found on bean "+entityBean.getClass()); - } - ebi.setPropertyUnloaded(position); + // our bean to perform stateless update + User user = new User(); + user.setId(42L); + user.setName("name mod"); + user.setEmail("change@junk.com"); + + BeanState beanState = Ebean.getBeanState(user); + assertThat(beanState.getLoadedProps()).containsExactly("id", "name", "email"); + + // unset the loaded state for email + ((EntityBean)user)._ebean_getIntercept().setPropertyLoaded("email", false); + + assertThat(beanState.getLoadedProps()).containsExactly("id", "name"); } + @Test + public void testUnloadVia_Model_markPropertyUnset() { + + // our bean to perform stateless update + User user = new User(); + user.setId(42L); + user.setName("name mod"); + user.setEmail("change@junk.com"); + + BeanState beanState = Ebean.getBeanState(user); + assertThat(beanState.getLoadedProps()).containsExactly("id", "name", "email"); + + + user.markPropertyUnset("email"); + assertThat(beanState.getLoadedProps()).containsExactly("id", "name"); + } + + @Test + public void testUnloadVia_BeanState_setPropertyLoaded() { + + // our bean to perform stateless update + User user = new User(); + user.setId(42L); + user.setName("name mod"); + user.setEmail("change@junk.com"); + + BeanState beanState = Ebean.getBeanState(user); + assertThat(beanState.getLoadedProps()).containsExactly("id", "name", "email"); + + Ebean.getBeanState(user).setPropertyLoaded("email", false); + assertThat(beanState.getLoadedProps()).containsExactly("id", "name"); + } }