diff --git a/src/main/java/com/avaje/ebean/BeanState.java b/src/main/java/com/avaje/ebean/BeanState.java index 3bd17068e..df6290ab6 100644 --- a/src/main/java/com/avaje/ebean/BeanState.java +++ b/src/main/java/com/avaje/ebean/BeanState.java @@ -1,6 +1,7 @@ package com.avaje.ebean; import java.beans.PropertyChangeListener; +import java.util.Map; import java.util.Set; /** @@ -46,6 +47,11 @@ public interface BeanState { */ public Set getChangedProps(); + /** + * Return a map of the updated properties and their new and old values. + */ + public Map getDirtyValues(); + /** * Return true if the bean is readOnly. *

diff --git a/src/main/java/com/avaje/ebean/ValuePair.java b/src/main/java/com/avaje/ebean/ValuePair.java index e1a89caad..fe11b7799 100644 --- a/src/main/java/com/avaje/ebean/ValuePair.java +++ b/src/main/java/com/avaje/ebean/ValuePair.java @@ -5,30 +5,46 @@ package com.avaje.ebean; */ public class ValuePair { - final Object value1; + private final Object newValue; - final Object value2; + private final Object oldValue; - public ValuePair(Object value1, Object value2) { - this.value1 = value1; - this.value2 = value2; + public ValuePair(Object newValue, Object oldValue) { + this.newValue = newValue; + this.oldValue = oldValue; } /** - * Return the first value. + * Return the new value. */ + public Object getNewValue() { + return newValue; + } + + /** + * Return the old value. + */ + public Object getOldValue() { + return oldValue; + } + + /** + * Return the new value. + */ + @Deprecated public Object getValue1() { - return value1; + return newValue; } /** - * Return the second value. + * Return the old value. */ + @Deprecated public Object getValue2() { - return value2; + return oldValue; } public String toString() { - return value1 + "," + value2; + return newValue + "," + oldValue; } } diff --git a/src/main/java/com/avaje/ebean/bean/EntityBeanIntercept.java b/src/main/java/com/avaje/ebean/bean/EntityBeanIntercept.java index 0b249f404..7f5b0361e 100644 --- a/src/main/java/com/avaje/ebean/bean/EntityBeanIntercept.java +++ b/src/main/java/com/avaje/ebean/bean/EntityBeanIntercept.java @@ -6,13 +6,16 @@ import java.beans.PropertyChangeSupport; import java.io.Serializable; import java.math.BigDecimal; import java.net.URL; +import java.util.LinkedHashMap; 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. @@ -82,6 +85,13 @@ public final class EntityBeanIntercept implements Serializable { * Set of changed properties. */ private boolean[] changedProps; + + /** + * Flags indicating if a property is a dirty embedded bean. Used to distingush + * between an embedded bean being completely overwritten and one of its + * embedded properties being made dirty. + */ + private boolean[] embeddedDirty; private Object[] origValues; @@ -227,9 +237,12 @@ public final class EntityBeanIntercept implements Serializable { return dirty; } + /** + * Called by an embedded bean onto its owner. + */ public void setEmbeddedDirty(int embeddedProperty) { this.dirty = true; - setChangedProperty(embeddedProperty); + setEmbeddedPropertyDirty(embeddedProperty); } public void setDirty(boolean dirty) { @@ -382,6 +395,30 @@ public final class EntityBeanIntercept implements Serializable { } } + /** + * Return the original value that was changed via an update. + */ + public Object getOrigValue(int propertyIndex) { + if (origValues == null) { + return null; + } + return origValues[propertyIndex]; + } + + /** + * Finds the index position of a given property. Returns -1 if the property + * can not be found. + */ + public int findProperty(String propertyName) { + String[] names = owner._ebean_getPropertyNames(); + for (int i = 0; i < names.length; i++) { + if (names[i].equals(propertyName)) { + return i; + } + } + return -1; + } + public String getProperty(int propertyIndex) { if (propertyIndex == -1) { return null; @@ -405,6 +442,15 @@ public final class EntityBeanIntercept implements Serializable { return (changedProps != null && changedProps[propertyIndex]); } + /** + * Return true if the property was changed or if it is embedded and one of its + * embedded properties is dirty. + */ + public boolean isDirtyProperty(int propertyIndex) { + return (changedProps != null && changedProps[propertyIndex] + || embeddedDirty != null && embeddedDirty[propertyIndex]); + } + /** * Explicitly mark a property as having been changed. */ @@ -419,6 +465,16 @@ public final class EntityBeanIntercept implements Serializable { } changedProps[propertyIndex] = true; } + + /** + * Set that an embedded bean has had one of its properties changed. + */ + private void setEmbeddedPropertyDirty(int propertyIndex) { + if (embeddedDirty == null) { + embeddedDirty = new boolean[owner._ebean_getPropertyNames().length]; + } + embeddedDirty[propertyIndex] = true; + } private void setOriginalValue(int propertyIndex, Object value) { if (origValues == null) { @@ -457,29 +513,88 @@ public final class EntityBeanIntercept implements Serializable { } return props; } - - public Set getChangedPropertyNames() { + + /** + * Return the set of dirty properties. + */ + public Set getDirtyPropertyNames() { Set props = new LinkedHashSet(); - if (changedProps != null) { - for (int i=0; i props, String prefix) { + int len = getPropertyLength(); + for (int i = 0; i < len; i++) { + if (changedProps != null && changedProps[i]) { + // the property has been changed on this bean + String propName = (prefix == null ? getProperty(i) : prefix + getProperty(i)); + props.add(propName); + } else if (embeddedDirty != null && embeddedDirty[i]) { + // an embedded property has been changed - recurse + EntityBean embeddedBean = (EntityBean)owner._ebean_getField(i); + embeddedBean._ebean_getIntercept().addDirtyPropertyNames(props, getProperty(i)+"."); } } - return h; + } + + /** + * Return a map of dirty properties with their new and old values. + */ + public Map getDirtyValues() { + Map dirtyValues = new LinkedHashMap(); + addDirtyPropertyValues(dirtyValues, null); + return dirtyValues; + } + + /** + * Recursively add dirty properties. + */ + public void addDirtyPropertyValues(Map dirtyValues, String prefix) { + int len = getPropertyLength(); + for (int i = 0; i < len; i++) { + if (changedProps != null && changedProps[i]) { + // the property has been changed on this bean + String propName = (prefix == null ? getProperty(i) : prefix + getProperty(i)); + Object newVal = owner._ebean_getField(i); + Object oldVal = getOrigValue(i); + + dirtyValues.put(propName, new ValuePair(newVal, oldVal)); + + } else if (embeddedDirty != null && embeddedDirty[i]) { + // an embedded property has been changed - recurse + EntityBean embeddedBean = (EntityBean)owner._ebean_getField(i); + embeddedBean._ebean_getIntercept().addDirtyPropertyValues(dirtyValues, getProperty(i)+"."); + } + } + } + + /** + * Return a dirty property hash taking into account embedded beans. + */ + public int getDirtyPropertyHash() { + return addDirtyPropertyHash(37); + } + + /** + * Add and return a dirty property hash recursing into embedded beans. + */ + public int addDirtyPropertyHash(int hash) { + int len = getPropertyLength(); + for (int i = 0; i < len; i++) { + if (changedProps != null && changedProps[i]) { + // the property has been changed on this bean + hash = hash * 31 + (i+1); + } else if (embeddedDirty != null && embeddedDirty[i]) { + // an embedded property has been changed - recurse + EntityBean embeddedBean = (EntityBean)owner._ebean_getField(i); + hash = hash * 31 + embeddedBean._ebean_getIntercept().addDirtyPropertyHash(hash); + } + } + return hash; } /** @@ -693,7 +808,7 @@ public final class EntityBeanIntercept implements Serializable { if (state == STATE_NEW) { setLoadedProperty(propertyIndex); } else if (!areEqual(oldValue, newValue)) { - setChangedPropertyValue(propertyIndex, intercept, newValue); + setChangedPropertyValue(propertyIndex, intercept, oldValue); } else { return null; } diff --git a/src/main/java/com/avaje/ebean/event/BeanPersistListener.java b/src/main/java/com/avaje/ebean/event/BeanPersistListener.java index f38aee387..1f9d6e32a 100644 --- a/src/main/java/com/avaje/ebean/event/BeanPersistListener.java +++ b/src/main/java/com/avaje/ebean/event/BeanPersistListener.java @@ -2,6 +2,8 @@ package com.avaje.ebean.event; import java.util.Set; +import com.avaje.ebean.config.ServerConfig; + /** * Listens for committed bean events. *

@@ -31,8 +33,9 @@ import java.util.Set; *

*

* A BeanPersistListener is either found automatically via class path search or - * can be added programmatically via ServerConfiguration.addEntity(). + * can be added programmatically via {@link ServerConfig#add(BeanPersistListener)}. *

+ * @see ServerConfig#add(BeanPersistListener) */ public interface BeanPersistListener { @@ -52,9 +55,9 @@ public interface BeanPersistListener { * @param bean * The bean that was updated. * @param updatedProperties - * the properties on the bean that where updated + * The properties that were modified by this update. */ - public boolean updated(T bean);//, Set updatedProperties); + public boolean updated(T bean, Set updatedProperties); /** * Notified that a bean has been deleted locally. Return true if you want the diff --git a/src/main/java/com/avaje/ebean/event/BeanPersistRequest.java b/src/main/java/com/avaje/ebean/event/BeanPersistRequest.java index 88053c703..388b03325 100644 --- a/src/main/java/com/avaje/ebean/event/BeanPersistRequest.java +++ b/src/main/java/com/avaje/ebean/event/BeanPersistRequest.java @@ -1,7 +1,11 @@ package com.avaje.ebean.event; +import java.util.Map; +import java.util.Set; + import com.avaje.ebean.EbeanServer; import com.avaje.ebean.Transaction; +import com.avaje.ebean.ValuePair; /** * Holds the information available for a bean persist (insert, update or @@ -22,29 +26,25 @@ public interface BeanPersistRequest { */ public Transaction getTransaction(); -// /** -// * For an update or delete of a partially populated bean this is the set of -// * loaded properties and otherwise returns null. -// */ -// public Set getLoadedProperties(); -// -// /** -// * For an update this is the set of properties that where updated. -// */ -// public Set getUpdatedProperties(); + /** + * For an update or delete of a partially populated bean this is the set of + * loaded properties and otherwise returns null. + */ + public Set getLoadedProperties(); + + /** + * For an update this is the set of properties that where updated. + */ + public Set getUpdatedProperties(); /** * Returns the bean being inserted updated or deleted. */ public T getBean(); -// /** -// * Returns a bean containing the original values prior to the bean being -// * modified. -// *

-// * This is for updates only. -// *

-// */ -// public T getOldValues(); + /** + * Returns a map of the properties that have changed and their new and old values. + */ + public Map getUpdatedValues(); } 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 cfea34803..6aeb1fa3c 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/core/DefaultBeanState.java +++ b/src/main/java/com/avaje/ebeaninternal/server/core/DefaultBeanState.java @@ -1,9 +1,11 @@ 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; @@ -12,9 +14,9 @@ import com.avaje.ebean.bean.EntityBeanIntercept; */ public class DefaultBeanState implements BeanState { - final EntityBean entityBean; + private final EntityBean entityBean; - final EntityBeanIntercept intercept; + private final EntityBeanIntercept intercept; public DefaultBeanState(EntityBean entityBean){ this.entityBean = entityBean; @@ -42,7 +44,11 @@ public class DefaultBeanState implements BeanState { } public Set getChangedProps() { - return intercept.getChangedPropertyNames(); + return intercept.getDirtyPropertyNames(); + } + + public Map getDirtyValues() { + return intercept.getDirtyValues(); } public boolean isReadOnly() { @@ -69,5 +75,4 @@ public class DefaultBeanState implements BeanState { intercept.setReference(); } - } diff --git a/src/main/java/com/avaje/ebeaninternal/server/core/DiffHelp.java b/src/main/java/com/avaje/ebeaninternal/server/core/DiffHelp.java index b76465e24..632121642 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/core/DiffHelp.java +++ b/src/main/java/com/avaje/ebeaninternal/server/core/DiffHelp.java @@ -5,7 +5,9 @@ import java.util.Map; import com.avaje.ebean.ValuePair; import com.avaje.ebean.bean.EntityBean; +import com.avaje.ebean.bean.EntityBeanIntercept; import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; +import com.avaje.ebeaninternal.server.deploy.BeanProperty; import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocOne; /** @@ -30,17 +32,16 @@ public class DiffHelp { */ public Map diff(Object a, Object b, BeanDescriptor desc) { - boolean oldValues = false; + Map map = new LinkedHashMap(); + if (b == null) { // get the old values from a if (a instanceof EntityBean) { - EntityBean eb = (EntityBean) a; - b = null;//FIXME eb._ebean_getIntercept().getOldValues(); - oldValues = true; + return ((EntityBean) a)._ebean_getIntercept().getDirtyValues(); } + return map; } - Map map = new LinkedHashMap(); // if (b == null) { // return map; @@ -70,8 +71,7 @@ public class DiffHelp { * determined to be different as is added to the map. *

*/ - private void diffEmbedded(Object a, Object b, BeanDescriptor desc, Map map, - boolean oldValues) { + private void diffEmbedded(Object a, Object b, BeanDescriptor desc, Map map, boolean oldValues) { // BeanPropertyAssocOne[] emb = desc.propertiesEmbedded(); // 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 6dfb2eeb6..12d54710f 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/core/PersistRequestBean.java +++ b/src/main/java/com/avaje/ebeaninternal/server/core/PersistRequestBean.java @@ -2,9 +2,12 @@ package com.avaje.ebeaninternal.server.core; import java.sql.SQLException; import java.util.List; +import java.util.Map; +import java.util.Set; import javax.persistence.OptimisticLockException; +import com.avaje.ebean.ValuePair; import com.avaje.ebean.annotation.ConcurrencyMode; import com.avaje.ebean.bean.EntityBean; import com.avaje.ebean.bean.EntityBeanIntercept; @@ -79,6 +82,8 @@ public class PersistRequestBean extends PersistRequest implements BeanPersist private boolean statelessUpdate; private boolean deleteMissingChildren; private boolean updateNullProperties; + + private final Set dirtyPropertyNames; public PersistRequestBean(SpiEbeanServer server, T bean, Object parentBean, BeanManager mgr, SpiTransaction t, PersistExecute persistExecute) { @@ -89,6 +94,8 @@ public class PersistRequestBean extends PersistRequest implements BeanPersist this.beanManager = mgr; this.beanDescriptor = mgr.getBeanDescriptor(); this.beanPersistListener = beanDescriptor.getPersistListener(); + this.dirtyPropertyNames = (beanPersistListener == null) ? null : intercept.getDirtyPropertyNames(); + this.bean = bean; this.parentBean = parentBean; @@ -101,9 +108,23 @@ public class PersistRequestBean extends PersistRequest implements BeanPersist // this is ok to not use isNewOrDirty() as used for updates only this.isDirty = intercept.isDirty(); } - - public void setNotNullAsLoaded() { + @Override + public Set getLoadedProperties() { + return intercept.getLoadedPropertyNames(); + } + + @Override + public Set getUpdatedProperties() { + return intercept.getDirtyPropertyNames(); + } + + @Override + public Map getUpdatedValues() { + return intercept.getDirtyValues(); + } + + public void setNotNullAsLoaded() { BeanProperty[] props = beanDescriptor.propertiesNonMany(); for (int i=0; i< props.length; i++) { BeanProperty prop = props[i]; @@ -163,7 +184,7 @@ public class PersistRequestBean extends PersistRequest implements BeanPersist return beanPersistListener.inserted(bean); case UPDATE: - return beanPersistListener.updated(bean);//, getUpdatedProperties()); + return beanPersistListener.updated(bean, dirtyPropertyNames); case DELETE: return beanPersistListener.deleted(bean); @@ -558,7 +579,7 @@ public class PersistRequestBean extends PersistRequest implements BeanPersist * update. */ public boolean isAddToUpdate(BeanProperty prop) { - return intercept.isChangedProperty(prop.getPropertyIndex()); + return intercept.isDirtyProperty(prop.getPropertyIndex()); } public List getDerivedRelationships() { diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/ChainedBeanPersistListener.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/ChainedBeanPersistListener.java index e5d7a1f81..a0dc8a7fc 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/ChainedBeanPersistListener.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/ChainedBeanPersistListener.java @@ -2,6 +2,7 @@ package com.avaje.ebeaninternal.server.deploy; import java.util.ArrayList; import java.util.List; +import java.util.Set; import com.avaje.ebean.event.BeanPersistListener; @@ -11,6 +12,7 @@ import com.avaje.ebean.event.BeanPersistListener; public class ChainedBeanPersistListener implements BeanPersistListener { private final List> list; + private final BeanPersistListener[] chain; /** @@ -109,10 +111,10 @@ public class ChainedBeanPersistListener implements BeanPersistListener { } } - public boolean updated(T bean) { + public boolean updated(T bean, Set updatedProperties) { boolean notifyCluster = false; for (int i = 0; i < chain.length; i++) { - if (chain[i].updated(bean)) { + if (chain[i].updated(bean, updatedProperties)) { notifyCluster = true; } } diff --git a/src/main/java/com/avaje/ebeaninternal/server/persist/dml/GenerateDmlRequest.java b/src/main/java/com/avaje/ebeaninternal/server/persist/dml/GenerateDmlRequest.java index 1410690be..fe4b0a097 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/persist/dml/GenerateDmlRequest.java +++ b/src/main/java/com/avaje/ebeaninternal/server/persist/dml/GenerateDmlRequest.java @@ -59,7 +59,7 @@ public class GenerateDmlRequest { return true; } if (changesOnly) { - return ebi.isChangedProperty(prop.getPropertyIndex()); + return ebi.isDirtyProperty(prop.getPropertyIndex()); } else { return ebi.isLoadedProperty(prop.getPropertyIndex()); } diff --git a/src/main/java/com/avaje/ebeaninternal/server/persist/dml/UpdateMeta.java b/src/main/java/com/avaje/ebeaninternal/server/persist/dml/UpdateMeta.java index bb7bfce86..4a3a303c1 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/persist/dml/UpdateMeta.java +++ b/src/main/java/com/avaje/ebeaninternal/server/persist/dml/UpdateMeta.java @@ -11,7 +11,6 @@ import com.avaje.ebeaninternal.api.SpiUpdatePlan; import com.avaje.ebeaninternal.server.core.PersistRequestBean; import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; import com.avaje.ebeaninternal.server.deploy.BeanProperty; -import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocOne; import com.avaje.ebeaninternal.server.persist.dmlbind.Bindable; import com.avaje.ebeaninternal.server.persist.dmlbind.BindableId; import com.avaje.ebeaninternal.server.persist.dmlbind.BindableList; @@ -115,20 +114,10 @@ public final class UpdateMeta { // we can use a cached UpdatePlan for the changed properties EntityBeanIntercept ebi = persistRequest.getEntityBeanIntercept(); - int hash = ebi.getChangedPropertiesHash(); + int hash = ebi.getDirtyPropertyHash(); BeanDescriptor beanDescriptor = persistRequest.getBeanDescriptor(); - BeanPropertyAssocOne[] propertiesEmbedded = beanDescriptor.propertiesEmbedded(); - for (int i=0; i< propertiesEmbedded.length; i++) { - EntityBean embeddedBean = (EntityBean)propertiesEmbedded[i].getValue(persistRequest.getEntityBean()); - if (embeddedBean == null) { - hash = hash * 31; - } else { - hash = hash * 31 + embeddedBean._ebean_getIntercept().getChangedPropertiesHash(); - } - } - BeanProperty versionProperty = beanDescriptor.getVersionProperty(); if (versionProperty != null) { if (ebi.isLoadedProperty(versionProperty.getPropertyIndex())) { diff --git a/src/test/java/com/avaje/ebean/TestDirtyProperties.java b/src/test/java/com/avaje/ebean/TestDirtyProperties.java new file mode 100644 index 000000000..fc4646eb2 --- /dev/null +++ b/src/test/java/com/avaje/ebean/TestDirtyProperties.java @@ -0,0 +1,138 @@ +package com.avaje.ebean; + +import java.util.Map; +import java.util.Set; + +import junit.framework.Assert; + +import org.junit.Test; + +import com.avaje.ebean.bean.EntityBean; +import com.avaje.ebean.bean.EntityBeanIntercept; +import com.avaje.ebeaninternal.server.core.DefaultBeanState; +import com.avaje.tests.model.embedded.EMain; +import com.avaje.tests.model.embedded.Eembeddable; + +public class TestDirtyProperties extends BaseTestCase { + + @Test + public void testEmbeddedUpdateEmbeddedProperty() { + + EMain emain = new EMain(); + + EntityBean eb = (EntityBean)emain; + EntityBeanIntercept ebi = eb._ebean_getIntercept(); + + emain.setId(1); + emain.setName("foo"); + Eembeddable embeddable = setEmbeddedBean(emain, "bar"); + setEmbeddedLoaded(embeddable); + + // sets loaded state so follow setters are deemed as changes to the bean + ebi.setLoaded(); + + emain.setName("changedFoo"); + + DefaultBeanState beanState = new DefaultBeanState(eb); + + Set changedProps = beanState.getChangedProps(); + Assert.assertEquals(1, changedProps.size()); + Assert.assertTrue(changedProps.contains("name")); + + Map dirtyValues = beanState.getDirtyValues(); + Assert.assertEquals(1, dirtyValues.size()); + Assert.assertTrue(dirtyValues.keySet().contains("name")); + + ValuePair valuePair = dirtyValues.get("name"); + Assert.assertNotNull(valuePair); + Assert.assertEquals("changedFoo",valuePair.getNewValue()); + Assert.assertEquals("foo",valuePair.getOldValue()); + + Eembeddable embeddableRead = emain.getEmbeddable(); + embeddableRead.setDescription("embChanged"); + + Set changedProps2 = beanState.getChangedProps(); + Assert.assertEquals(2, changedProps2.size()); + Assert.assertTrue(changedProps2.contains("name")); + Assert.assertTrue(changedProps2.contains("embeddable.description")); + + Map dirtyValues2 = beanState.getDirtyValues(); + Assert.assertEquals(2, dirtyValues2.size()); + Assert.assertTrue(dirtyValues2.keySet().contains("name")); + Assert.assertTrue(dirtyValues2.keySet().contains("embeddable.description")); + + ValuePair valuePair2 = dirtyValues2.get("embeddable.description"); + Assert.assertEquals("embChanged",valuePair2.getNewValue()); + Assert.assertEquals("bar",valuePair2.getOldValue()); + } + + + + @Test + public void testEmbeddedUpdateSetNewBean() { + + EMain emain = new EMain(); + + EntityBean eb = (EntityBean)emain; + EntityBeanIntercept ebi = eb._ebean_getIntercept(); + + emain.setId(1); + emain.setName("foo"); + Eembeddable embeddable = setEmbeddedBean(emain, "bar"); + setEmbeddedLoaded(embeddable); + + // sets loaded state so follow setters are deemed as changes to the bean + ebi.setLoaded(); + + emain.setName("changedFoo"); + + Assert.assertSame(embeddable, emain.getEmbeddable()); + + Eembeddable embeddable2 = setEmbeddedBean(emain, "changeEmbeddedInstance"); + Assert.assertSame(embeddable2, emain.getEmbeddable()); + Assert.assertNotSame(embeddable, emain.getEmbeddable()); + + + DefaultBeanState beanState = new DefaultBeanState(eb); + + Set changedProps2 = beanState.getChangedProps(); + Assert.assertEquals(2, changedProps2.size()); + Assert.assertTrue(changedProps2.contains("name")); + + Assert.assertTrue("The whole bean instance has changed", changedProps2.contains("embeddable")); + + Map dirtyValues2 = beanState.getDirtyValues(); + Assert.assertEquals(2, dirtyValues2.size()); + Assert.assertTrue(dirtyValues2.keySet().contains("name")); + Assert.assertTrue(dirtyValues2.keySet().contains("embeddable")); + + + ValuePair valuePair2 = dirtyValues2.get("embeddable"); + Assert.assertSame(embeddable2, valuePair2.getNewValue()); + Assert.assertSame(embeddable, valuePair2.getOldValue()); + + } + + + private void setEmbeddedLoaded(Eembeddable embeddable) { + ((EntityBean)embeddable)._ebean_getIntercept().setLoaded(); + } + + + private Eembeddable setEmbeddedBean(EMain emain, String description) { + + Eembeddable embeddable = new Eembeddable(); + embeddable.setDescription(description); + + emain.setEmbeddable(embeddable); + + EntityBean owner = (EntityBean)emain; + EntityBeanIntercept ebi= owner._ebean_getIntercept(); + + // hooks the embeddable bean back to the owner + int embeddablePropertyIndex = ebi.findProperty("embeddable"); + Assert.assertTrue(embeddablePropertyIndex > -1); + ((EntityBean)embeddable)._ebean_getIntercept().setEmbeddedOwner(owner, embeddablePropertyIndex); + return embeddable; + } +} diff --git a/src/test/java/com/avaje/tests/model/basic/MyEBasicConfigStartup.java b/src/test/java/com/avaje/tests/model/basic/MyEBasicConfigStartup.java index 821b3c32f..385c412d5 100644 --- a/src/test/java/com/avaje/tests/model/basic/MyEBasicConfigStartup.java +++ b/src/test/java/com/avaje/tests/model/basic/MyEBasicConfigStartup.java @@ -3,8 +3,6 @@ package com.avaje.tests.model.basic; import java.util.HashSet; import java.util.Set; -import com.avaje.ebean.BeanState; -import com.avaje.ebean.Ebean; import com.avaje.ebean.config.ServerConfig; import com.avaje.ebean.event.BeanPersistListener; import com.avaje.ebean.event.BulkTableEvent; @@ -44,10 +42,8 @@ public class MyEBasicConfigStartup implements ServerConfigStartup { return false; } - public boolean updated(EBasic bean) { - BeanState beanState = Ebean.getBeanState(bean); - Set updatedProperties = beanState.getChangedProps(); - System.out.println("-- EBasic updated " + bean.getId()); + public boolean updated(EBasic bean, Set updatedProperties) { + System.out.println("-- EBasic updated " + bean.getId()+" updatedProperties: "+updatedProperties); return false; }