diff --git a/src/main/java/io/ebeaninternal/server/cache/CachedBeanDataFromBean.java b/src/main/java/io/ebeaninternal/server/cache/CachedBeanDataFromBean.java index c91fee62d..32ac2ce9a 100644 --- a/src/main/java/io/ebeaninternal/server/cache/CachedBeanDataFromBean.java +++ b/src/main/java/io/ebeaninternal/server/cache/CachedBeanDataFromBean.java @@ -11,11 +11,9 @@ import java.util.Map; public class CachedBeanDataFromBean { - public static CachedBeanData extract(BeanDescriptor desc, EntityBean bean) { EntityBeanIntercept ebi = bean._ebean_getIntercept(); - Map data = new LinkedHashMap<>(); BeanProperty idProperty = desc.getIdProperty(); @@ -25,11 +23,13 @@ public class CachedBeanDataFromBean { data.put(idProperty.getName(), idProperty.getCacheDataValue(bean)); } } - BeanProperty[] props = desc.propertiesNonMany(); // extract all the non-many properties - for (BeanProperty prop : props) { - if (ebi.isLoadedProperty(prop.getPropertyIndex())) { + final boolean dirty = ebi.isDirty(); + for (BeanProperty prop : desc.propertiesNonMany()) { + if (dirty && ebi.isDirtyProperty(prop.getPropertyIndex())) { + data.put(prop.getName(), prop.getCacheDataValueOrig(ebi)); + } else if (ebi.isLoadedProperty(prop.getPropertyIndex())) { data.put(prop.getName(), prop.getCacheDataValue(bean)); } } @@ -72,5 +72,4 @@ public class CachedBeanDataFromBean { return sharableBean; } - } diff --git a/src/main/java/io/ebeaninternal/server/deploy/BeanProperty.java b/src/main/java/io/ebeaninternal/server/deploy/BeanProperty.java index 11d11d67e..31b3bc810 100644 --- a/src/main/java/io/ebeaninternal/server/deploy/BeanProperty.java +++ b/src/main/java/io/ebeaninternal/server/deploy/BeanProperty.java @@ -3,6 +3,7 @@ package io.ebeaninternal.server.deploy; import com.fasterxml.jackson.core.JsonToken; import io.ebean.ValuePair; import io.ebean.bean.EntityBean; +import io.ebean.bean.EntityBeanIntercept; import io.ebean.bean.PersistenceContext; import io.ebean.config.EncryptKey; import io.ebean.config.dbplatform.DbEncryptFunction; @@ -10,7 +11,6 @@ import io.ebean.config.dbplatform.DbPlatformType; import io.ebean.plugin.Property; import io.ebean.text.StringParser; import io.ebean.util.SplitName; -import io.ebean.util.StringHelper; import io.ebeaninternal.api.SpiExpressionRequest; import io.ebeaninternal.api.SpiQuery; import io.ebeaninternal.api.json.SpiJsonReader; @@ -794,7 +794,17 @@ public class BeanProperty implements ElPropertyValue, Property, STreeProperty { *

*/ public Object getCacheDataValue(EntityBean bean) { - Object value = getValue(bean); + return cacheDataConvert(getValue(bean)); + } + + /** + * Return the bean cache value for this property using original values. + */ + public Object getCacheDataValueOrig(EntityBeanIntercept ebi) { + return cacheDataConvert(ebi.getOrigValue(propertyIndex)); + } + + private Object cacheDataConvert(Object value) { if (value == null || scalarType.isBinaryType()) { return value; } else { diff --git a/src/main/java/io/ebeaninternal/server/deploy/BeanPropertyAssocOne.java b/src/main/java/io/ebeaninternal/server/deploy/BeanPropertyAssocOne.java index d33dd95c2..59404d6b1 100644 --- a/src/main/java/io/ebeaninternal/server/deploy/BeanPropertyAssocOne.java +++ b/src/main/java/io/ebeaninternal/server/deploy/BeanPropertyAssocOne.java @@ -5,6 +5,7 @@ import io.ebean.SqlUpdate; import io.ebean.Transaction; import io.ebean.ValuePair; import io.ebean.bean.EntityBean; +import io.ebean.bean.EntityBeanIntercept; import io.ebean.bean.PersistenceContext; import io.ebean.util.SplitName; import io.ebeaninternal.api.SpiEbeanServer; @@ -426,9 +427,19 @@ public class BeanPropertyAssocOne extends BeanPropertyAssoc implements STr return getPropertyType(); } + /** + * Return the bean cache value for this property using original values. + */ + public Object getCacheDataValueOrig(EntityBeanIntercept ebi) { + return cacheDataConvert(ebi.getOrigValue(propertyIndex)); + } + @Override public Object getCacheDataValue(EntityBean bean) { - Object ap = getValue(bean); + return cacheDataConvert(getValue(bean)); + } + + private Object cacheDataConvert(Object ap) { if (ap == null) { return null; } diff --git a/src/test/java/io/ebeaninternal/server/cache/CacheBeanDataTest.java b/src/test/java/io/ebeaninternal/server/cache/CacheBeanDataTest.java index 2ba1b9747..957515f80 100644 --- a/src/test/java/io/ebeaninternal/server/cache/CacheBeanDataTest.java +++ b/src/test/java/io/ebeaninternal/server/cache/CacheBeanDataTest.java @@ -43,7 +43,7 @@ public class CacheBeanDataTest extends BaseTestCase { billingAddress.setLine1("92 Someplace Else"); c.setBillingAddress(billingAddress); - ((EntityBean) c)._ebean_getIntercept().setNewBeanForUpdate(); + ((EntityBean) c)._ebean_getIntercept().setLoaded(); CachedBeanData cacheData = CachedBeanDataFromBean.extract(desc, (EntityBean) c); diff --git a/src/test/java/io/ebeaninternal/server/cache/CachedBeanDataFromBeanTest.java b/src/test/java/io/ebeaninternal/server/cache/CachedBeanDataFromBeanTest.java index 1490bead6..323fcc2e0 100644 --- a/src/test/java/io/ebeaninternal/server/cache/CachedBeanDataFromBeanTest.java +++ b/src/test/java/io/ebeaninternal/server/cache/CachedBeanDataFromBeanTest.java @@ -5,21 +5,24 @@ import io.ebean.bean.EntityBean; import io.ebeaninternal.api.SpiEbeanServer; import io.ebeaninternal.server.deploy.BeanDescriptor; import io.ebeaninternal.server.transaction.DefaultPersistenceContext; +import org.junit.Test; import org.tests.model.basic.Address; import org.tests.model.basic.Car; +import org.tests.model.basic.Contact; import org.tests.model.basic.Customer; -import org.junit.Test; import java.sql.Date; +import java.util.Map; +import static org.assertj.core.api.Assertions.assertThat; import static org.junit.Assert.assertEquals; public class CachedBeanDataFromBeanTest extends BaseTestCase { - SpiEbeanServer server = spiEbeanServer(); + private final SpiEbeanServer server = spiEbeanServer(); @Test - public void extract() throws Exception { + public void extract() { BeanDescriptor desc = server.getBeanDescriptor(Customer.class); @@ -64,4 +67,61 @@ public class CachedBeanDataFromBeanTest extends BaseTestCase { assertEquals(newCar.getDriver(), car.getDriver()); assertEquals(newCar.getNotes(), car.getNotes()); } + + @SuppressWarnings("unchecked") + @Test + public void dirtyScalar_expect_originalValueUsed() { + + Contact contact = new Contact(); + contact.setId(42); + contact.setLastName("Bygrave"); + contact.setFirstName("Foo"); + contact.setEmail("rob@email.com"); + + EntityBean entityBean = (EntityBean)contact; + entityBean._ebean_getIntercept().setLoaded(); + + // mutate, dirty + contact.setLastName("Banana"); + + final BeanDescriptor desc = getBeanDescriptor(Contact.class); + CachedBeanData cacheData = CachedBeanDataFromBean.extract(desc, entityBean); + + final Map data = cacheData.getData(); + assertThat(data.get("id")).isEqualTo("42"); + assertThat(data.get("lastName")).isEqualTo("Bygrave"); // ORIGINAL VALUE + assertThat(data.get("firstName")).isEqualTo(contact.getFirstName()); + assertThat(data.get("email")).isEqualTo(contact.getEmail()); + } + + @Test + public void dirtyManyToOne_expect_originalValueUsed() { + + Customer customer = new Customer(); + customer.setId(99); + + Contact contact = new Contact(); + contact.setFirstName("Foo"); + contact.setLastName("Bygrave"); + contact.setEmail("rob@email.com"); + contact.setCustomer(customer); + + EntityBean entityBean = (EntityBean)contact; + entityBean._ebean_getIntercept().setLoaded(); + + // mutate, dirty + Customer customer2 = new Customer(); + customer2.setId(108); + contact.setCustomer(customer2); + contact.setLastName("Banana"); + + final BeanDescriptor desc = getBeanDescriptor(Contact.class); + CachedBeanData cacheData = CachedBeanDataFromBean.extract(desc, entityBean); + + final Map data = cacheData.getData(); + assertThat(data.get("lastName")).isEqualTo("Bygrave"); // Original value + assertThat(data.get("customer")).isEqualTo("99"); // Original value + assertThat(data.get("firstName")).isEqualTo(contact.getFirstName()); + assertThat(data.get("email")).isEqualTo(contact.getEmail()); + } }