This is an automated email from the ASF dual-hosted git repository.

aadamchik pushed a commit to branch CAY-2660
in repository https://gitbox.apache.org/repos/asf/cayenne.git

commit 3e00d0e7ff9c16726e9d861439cdf3c136a7870c
Author: Andrus Adamchik <[email protected]>
AuthorDate: Fri Jun 5 13:55:12 2020 +0300

    CAY-2660 BigDecimals that differ only in scale are treated as different 
values causing unneeded updates
---
 .../main/java/org/apache/cayenne/util/Util.java    | 12 ++-
 .../DataContextEJBQLNumericalFunctionalIT.java     | 12 +--
 .../org/apache/cayenne/access/NumericTypesIT.java  | 99 +++++++++++++++++-----
 .../numeric_types/auto/_BigDecimalEntity.java      | 47 +++++++---
 .../org/apache/cayenne/unit/di/CommitStats.java    | 50 +++++++++++
 .../src/test/resources/cayenne-numeric-types.xml   |  2 +
 .../src/test/resources/numeric-types.map.xml       |  8 +-
 7 files changed, 186 insertions(+), 44 deletions(-)

diff --git a/cayenne-server/src/main/java/org/apache/cayenne/util/Util.java 
b/cayenne-server/src/main/java/org/apache/cayenne/util/Util.java
index c86c279..ed334b7 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/util/Util.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/util/Util.java
@@ -53,6 +53,7 @@ import java.io.ObjectOutputStream;
 import java.io.Serializable;
 import java.lang.reflect.Member;
 import java.lang.reflect.Modifier;
+import java.math.BigDecimal;
 import java.net.URI;
 import java.net.URISyntaxException;
 import java.net.URL;
@@ -208,8 +209,15 @@ public class Util {
                        builder.append(o1, o2);
                        return builder.isEquals();
                } else { // It is NOT an array, so use regular equals()
-                       return o1.equals(o2);
-               }
+            boolean eq = o1.equals(o2);
+
+            // special case - BigDecimals that differ only in trailing zeros
+            if (!eq && o1 instanceof BigDecimal && o2 instanceof BigDecimal) {
+                return ((BigDecimal) o1).compareTo((BigDecimal) o2) == 0;
+            }
+
+            return eq;
+        }
        }
 
        /**
diff --git 
a/cayenne-server/src/test/java/org/apache/cayenne/access/DataContextEJBQLNumericalFunctionalIT.java
 
b/cayenne-server/src/test/java/org/apache/cayenne/access/DataContextEJBQLNumericalFunctionalIT.java
index 10db03c..76287b8 100644
--- 
a/cayenne-server/src/test/java/org/apache/cayenne/access/DataContextEJBQLNumericalFunctionalIT.java
+++ 
b/cayenne-server/src/test/java/org/apache/cayenne/access/DataContextEJBQLNumericalFunctionalIT.java
@@ -64,15 +64,15 @@ public class DataContextEJBQLNumericalFunctionalIT extends 
ServerCase {
     public void testABS() {
 
         BigDecimalEntity o1 = context.newObject(BigDecimalEntity.class);
-        o1.setBigDecimalField(new BigDecimal("4.1"));
+        o1.setBigDecimalNumeric(new BigDecimal("4.1"));
 
         BigDecimalEntity o2 = context.newObject(BigDecimalEntity.class);
-        o2.setBigDecimalField(new BigDecimal("-5.1"));
+        o2.setBigDecimalNumeric(new BigDecimal("-5.1"));
 
         context.commitChanges();
 
         EJBQLQuery query = new EJBQLQuery(
-                "SELECT d FROM BigDecimalEntity d WHERE ABS(d.bigDecimalField) 
< 5.0");
+                "SELECT d FROM BigDecimalEntity d WHERE 
ABS(d.bigDecimalNumeric) < 5.0");
         List<?> objects = context.performQuery(query);
         assertEquals(1, objects.size());
         assertTrue(objects.contains(o1));
@@ -82,15 +82,15 @@ public class DataContextEJBQLNumericalFunctionalIT extends 
ServerCase {
     public void testSQRT() {
 
         BigDecimalEntity o1 = context.newObject(BigDecimalEntity.class);
-        o1.setBigDecimalField(new BigDecimal("9"));
+        o1.setBigDecimalNumeric(new BigDecimal("9"));
 
         BigDecimalEntity o2 = context.newObject(BigDecimalEntity.class);
-        o2.setBigDecimalField(new BigDecimal("16"));
+        o2.setBigDecimalNumeric(new BigDecimal("16"));
 
         context.commitChanges();
 
         EJBQLQuery query = new EJBQLQuery(
-                "SELECT d FROM BigDecimalEntity d WHERE 
SQRT(d.bigDecimalField) > 3.1");
+                "SELECT d FROM BigDecimalEntity d WHERE 
SQRT(d.bigDecimalNumeric) > 3.1");
         List<?> objects = context.performQuery(query);
         assertEquals(1, objects.size());
         assertTrue(objects.contains(o2));
diff --git 
a/cayenne-server/src/test/java/org/apache/cayenne/access/NumericTypesIT.java 
b/cayenne-server/src/test/java/org/apache/cayenne/access/NumericTypesIT.java
index 7189dfb..3d4398d 100644
--- a/cayenne-server/src/test/java/org/apache/cayenne/access/NumericTypesIT.java
+++ b/cayenne-server/src/test/java/org/apache/cayenne/access/NumericTypesIT.java
@@ -41,18 +41,21 @@ import 
org.apache.cayenne.testdo.numeric_types.DecimalPKTestEntity;
 import org.apache.cayenne.testdo.numeric_types.LongEntity;
 import org.apache.cayenne.testdo.numeric_types.SmallintTestEntity;
 import org.apache.cayenne.testdo.numeric_types.TinyintTestEntity;
+import org.apache.cayenne.unit.di.CommitStats;
+import org.apache.cayenne.unit.di.DataChannelInterceptor;
+import org.apache.cayenne.unit.di.DataChannelSyncStats;
 import org.apache.cayenne.unit.di.server.CayenneProjects;
 import org.apache.cayenne.unit.di.server.ServerCase;
 import org.apache.cayenne.unit.di.server.UseServerRuntime;
+import org.junit.After;
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
 
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNotSame;
-import static org.junit.Assert.assertSame;
+import static org.junit.Assert.*;
 
 /**
+ *
  */
 @UseServerRuntime(CayenneProjects.NUMERIC_TYPES_PROJECT)
 public class NumericTypesIT extends ServerCase {
@@ -69,11 +72,15 @@ public class NumericTypesIT extends ServerCase {
     @Inject
     protected DBHelper dbHelper;
 
+    private final CommitStats commitStats = new CommitStats(() -> 
runtime.getDataDomain());
+
     protected TableHelper tSmallintTest;
     protected TableHelper tTinyintTest;
 
     @Before
-    public void setUp() throws Exception {
+    public void before() {
+        commitStats.before();
+
         tSmallintTest = new TableHelper(dbHelper, "SMALLINT_TEST");
         tSmallintTest.setColumns("ID", "SMALLINT_COL");
 
@@ -81,6 +88,11 @@ public class NumericTypesIT extends ServerCase {
         tTinyintTest.setColumns("ID", "TINYINT_COL");
     }
 
+    @After
+    public void after() {
+        commitStats.after();
+    }
+
     protected void createShortDataSet() throws Exception {
         tSmallintTest.insert(1, 9999);
         tSmallintTest.insert(2, 3333);
@@ -128,21 +140,70 @@ public class NumericTypesIT extends ServerCase {
     }
 
     @Test
-    public void testBigDecimal() throws Exception {
-
-        BigDecimalEntity test = context.newObject(BigDecimalEntity.class);
-
-        BigDecimal i = new BigDecimal("1234567890.44");
-        test.setBigDecimalField(i);
-        context.commitChanges();
-
-        BigDecimalEntity testRead = ObjectSelect.query(BigDecimalEntity.class)
-                .selectFirst(context);
-        assertNotNull(testRead.getBigDecimalField());
-        assertEquals(i, testRead.getBigDecimalField());
+    public void testBigDecimal_Decimal() {
+
+        // this matches the column scale exactly
+        String v1 = "7890.123456";
+        // this has lower scale than the column
+        String v2 = "7890.1";
+        String v2_padded = "7890.100000";
+
+        BigDecimalEntity o = context.newObject(BigDecimalEntity.class);
+        o.setBigDecimalDecimal(new BigDecimal(v1));
+        o.getObjectContext().commitChanges();
+        assertEquals(1, commitStats.getCommitCount());
+        BigDecimalEntity o1 = 
ObjectSelect.query(BigDecimalEntity.class).selectFirst(runtime.newContext());
+        assertEquals(v1, o1.getBigDecimalDecimal().toString());
+
+        o.setBigDecimalDecimal(new BigDecimal(v2));
+        o.getObjectContext().commitChanges();
+        BigDecimalEntity o2 = 
ObjectSelect.query(BigDecimalEntity.class).selectFirst(runtime.newContext());
+        assertEquals(v2_padded, o2.getBigDecimalDecimal().toString());
+        assertEquals(2, commitStats.getCommitCount());
+
+        o2.setBigDecimalDecimal(new BigDecimal(v2));
+        o2.getObjectContext().commitChanges();
+        assertEquals("Commit was not expected. The difference is purely in 
value padding", 2, commitStats.getCommitCount());
+        BigDecimalEntity o3 = 
ObjectSelect.query(BigDecimalEntity.class).selectFirst(runtime.newContext());
+        assertEquals(v2_padded, o3.getBigDecimalDecimal().toString());
+
+        o3.setBigDecimalDecimal(null);
+        o3.getObjectContext().commitChanges();
+        assertEquals(3, commitStats.getCommitCount());
+        BigDecimalEntity o4 = 
ObjectSelect.query(BigDecimalEntity.class).selectFirst(runtime.newContext());
+        assertNull(o4.getBigDecimalDecimal());
+    }
 
-        test.setBigDecimalField(null);
-        context.commitChanges();
+    @Test
+    public void testBigDecimal_Numeric() {
+
+        String v1 = "1234567890.44";
+        String v2 = "1234567890.4";
+        String v2_padded = "1234567890.40";
+
+        BigDecimalEntity o = context.newObject(BigDecimalEntity.class);
+        o.setBigDecimalNumeric(new BigDecimal(v1));
+        o.getObjectContext().commitChanges();
+        assertEquals(1, commitStats.getCommitCount());
+        BigDecimalEntity o1 = 
ObjectSelect.query(BigDecimalEntity.class).selectFirst(runtime.newContext());
+        assertEquals(v1, o1.getBigDecimalNumeric().toString());
+
+        o1.setBigDecimalNumeric(new BigDecimal(v2));
+        o1.getObjectContext().commitChanges();
+        assertEquals(2, commitStats.getCommitCount());
+        BigDecimalEntity o2 = 
ObjectSelect.query(BigDecimalEntity.class).selectFirst(runtime.newContext());
+        assertEquals(v2_padded, o2.getBigDecimalNumeric().toString());
+
+        o2.setBigDecimalNumeric(new BigDecimal(v2));
+        assertEquals("Commit was not expected. The difference is purely in 
value padding", 2, commitStats.getCommitCount());
+        BigDecimalEntity o3 = 
ObjectSelect.query(BigDecimalEntity.class).selectFirst(runtime.newContext());
+        assertEquals(v2_padded, o3.getBigDecimalNumeric().toString());
+
+        o3.setBigDecimalNumeric(null);
+        o3.getObjectContext().commitChanges();
+        assertEquals(3, commitStats.getCommitCount());
+        BigDecimalEntity o4 = 
ObjectSelect.query(BigDecimalEntity.class).selectFirst(runtime.newContext());
+        assertNull(o4.getBigDecimalNumeric());
     }
 
     @Test
diff --git 
a/cayenne-server/src/test/java/org/apache/cayenne/testdo/numeric_types/auto/_BigDecimalEntity.java
 
b/cayenne-server/src/test/java/org/apache/cayenne/testdo/numeric_types/auto/_BigDecimalEntity.java
index b0473f0..20b6cce 100644
--- 
a/cayenne-server/src/test/java/org/apache/cayenne/testdo/numeric_types/auto/_BigDecimalEntity.java
+++ 
b/cayenne-server/src/test/java/org/apache/cayenne/testdo/numeric_types/auto/_BigDecimalEntity.java
@@ -21,19 +21,31 @@ public abstract class _BigDecimalEntity extends 
BaseDataObject {
 
     public static final String ID_PK_COLUMN = "ID";
 
-    public static final NumericProperty<BigDecimal> BIG_DECIMAL_FIELD = 
PropertyFactory.createNumeric("bigDecimalField", BigDecimal.class);
+    public static final NumericProperty<BigDecimal> BIG_DECIMAL_DECIMAL = 
PropertyFactory.createNumeric("bigDecimalDecimal", BigDecimal.class);
+    public static final NumericProperty<BigDecimal> BIG_DECIMAL_NUMERIC = 
PropertyFactory.createNumeric("bigDecimalNumeric", BigDecimal.class);
 
-    protected BigDecimal bigDecimalField;
+    protected BigDecimal bigDecimalDecimal;
+    protected BigDecimal bigDecimalNumeric;
 
 
-    public void setBigDecimalField(BigDecimal bigDecimalField) {
-        beforePropertyWrite("bigDecimalField", this.bigDecimalField, 
bigDecimalField);
-        this.bigDecimalField = bigDecimalField;
+    public void setBigDecimalDecimal(BigDecimal bigDecimalDecimal) {
+        beforePropertyWrite("bigDecimalDecimal", this.bigDecimalDecimal, 
bigDecimalDecimal);
+        this.bigDecimalDecimal = bigDecimalDecimal;
     }
 
-    public BigDecimal getBigDecimalField() {
-        beforePropertyRead("bigDecimalField");
-        return this.bigDecimalField;
+    public BigDecimal getBigDecimalDecimal() {
+        beforePropertyRead("bigDecimalDecimal");
+        return this.bigDecimalDecimal;
+    }
+
+    public void setBigDecimalNumeric(BigDecimal bigDecimalNumeric) {
+        beforePropertyWrite("bigDecimalNumeric", this.bigDecimalNumeric, 
bigDecimalNumeric);
+        this.bigDecimalNumeric = bigDecimalNumeric;
+    }
+
+    public BigDecimal getBigDecimalNumeric() {
+        beforePropertyRead("bigDecimalNumeric");
+        return this.bigDecimalNumeric;
     }
 
     @Override
@@ -43,8 +55,10 @@ public abstract class _BigDecimalEntity extends 
BaseDataObject {
         }
 
         switch(propName) {
-            case "bigDecimalField":
-                return this.bigDecimalField;
+            case "bigDecimalDecimal":
+                return this.bigDecimalDecimal;
+            case "bigDecimalNumeric":
+                return this.bigDecimalNumeric;
             default:
                 return super.readPropertyDirectly(propName);
         }
@@ -57,8 +71,11 @@ public abstract class _BigDecimalEntity extends 
BaseDataObject {
         }
 
         switch (propName) {
-            case "bigDecimalField":
-                this.bigDecimalField = (BigDecimal)val;
+            case "bigDecimalDecimal":
+                this.bigDecimalDecimal = (BigDecimal)val;
+                break;
+            case "bigDecimalNumeric":
+                this.bigDecimalNumeric = (BigDecimal)val;
                 break;
             default:
                 super.writePropertyDirectly(propName, val);
@@ -76,13 +93,15 @@ public abstract class _BigDecimalEntity extends 
BaseDataObject {
     @Override
     protected void writeState(ObjectOutputStream out) throws IOException {
         super.writeState(out);
-        out.writeObject(this.bigDecimalField);
+        out.writeObject(this.bigDecimalDecimal);
+        out.writeObject(this.bigDecimalNumeric);
     }
 
     @Override
     protected void readState(ObjectInputStream in) throws IOException, 
ClassNotFoundException {
         super.readState(in);
-        this.bigDecimalField = (BigDecimal)in.readObject();
+        this.bigDecimalDecimal = (BigDecimal)in.readObject();
+        this.bigDecimalNumeric = (BigDecimal)in.readObject();
     }
 
 }
diff --git 
a/cayenne-server/src/test/java/org/apache/cayenne/unit/di/CommitStats.java 
b/cayenne-server/src/test/java/org/apache/cayenne/unit/di/CommitStats.java
new file mode 100644
index 0000000..4679463
--- /dev/null
+++ b/cayenne-server/src/test/java/org/apache/cayenne/unit/di/CommitStats.java
@@ -0,0 +1,50 @@
+package org.apache.cayenne.unit.di;
+
+import org.apache.cayenne.DataChannel;
+import org.apache.cayenne.DataChannelSyncFilter;
+import org.apache.cayenne.DataChannelSyncFilterChain;
+import org.apache.cayenne.ObjectContext;
+import org.apache.cayenne.access.DataDomain;
+import org.apache.cayenne.graph.GraphDiff;
+import org.junit.rules.ExternalResource;
+
+import java.util.function.Supplier;
+
+public class CommitStats implements DataChannelSyncFilter {
+
+    private int commitCount;
+    private Supplier<DataDomain> dataDomain;
+
+    public CommitStats(Supplier<DataDomain> dataDomain) {
+        this.dataDomain = dataDomain;
+    }
+
+    public void before() {
+        dataDomain.get().addSyncFilter(this);
+        commitCount = 0;
+    }
+
+    public void after() {
+        dataDomain.get().removeSyncFilter(this);
+    }
+
+    @Override
+    public GraphDiff onSync(
+            ObjectContext originatingContext,
+            GraphDiff changes,
+            int syncType,
+            DataChannelSyncFilterChain filterChain) {
+
+        switch (syncType) {
+            case DataChannel.FLUSH_CASCADE_SYNC:
+                commitCount++;
+                break;
+        }
+
+        return filterChain.onSync(originatingContext, changes, syncType);
+    }
+
+    public int getCommitCount() {
+        return commitCount;
+    }
+}
diff --git a/cayenne-server/src/test/resources/cayenne-numeric-types.xml 
b/cayenne-server/src/test/resources/cayenne-numeric-types.xml
index cb44482..d9c33bf 100644
--- a/cayenne-server/src/test/resources/cayenne-numeric-types.xml
+++ b/cayenne-server/src/test/resources/cayenne-numeric-types.xml
@@ -1,5 +1,7 @@
 <?xml version="1.0" encoding="utf-8"?>
 <domain xmlns="http://cayenne.apache.org/schema/10/domain";
+        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance";
+        xsi:schemaLocation="http://cayenne.apache.org/schema/10/domain 
https://cayenne.apache.org/schema/10/domain.xsd";
         project-version="10">
        <map name="numeric-types"/>
 </domain>
diff --git a/cayenne-server/src/test/resources/numeric-types.map.xml 
b/cayenne-server/src/test/resources/numeric-types.map.xml
index 65d4000..d53a680 100644
--- a/cayenne-server/src/test/resources/numeric-types.map.xml
+++ b/cayenne-server/src/test/resources/numeric-types.map.xml
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="utf-8"?>
 <data-map xmlns="http://cayenne.apache.org/schema/10/modelMap";
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance";
-        xsi:schemaLocation="http://cayenne.apache.org/schema/10/modelMap 
http://cayenne.apache.org/schema/10/modelMap.xsd";
+        xsi:schemaLocation="http://cayenne.apache.org/schema/10/modelMap 
https://cayenne.apache.org/schema/10/modelMap.xsd";
         project-version="10">
        <property name="defaultPackage" 
value="org.apache.cayenne.testdo.numeric_types"/>
        <property name="defaultSuperclass" 
value="org.apache.cayenne.CayenneDataObject"/>
@@ -9,7 +9,8 @@
        <property name="defaultClientPackage" value="test.client"/>
        <property name="defaultClientSuperclass" 
value="org.apache.cayenne.PersistentObject"/>
        <db-entity name="BIGDECIMAL_ENTITY">
-               <db-attribute name="BIGDECIMAL_FIELD" type="NUMERIC" 
length="12" scale="2"/>
+               <db-attribute name="BIG_DECIMAL_DECIMAL" type="DECIMAL" 
length="12" scale="6"/>
+               <db-attribute name="BIG_DECIMAL_NUMERIC" type="NUMERIC" 
length="12" scale="2"/>
                <db-attribute name="ID" type="INTEGER" isPrimaryKey="true" 
isMandatory="true"/>
        </db-entity>
        <db-entity name="BIGINTEGER_ENTITY">
@@ -45,7 +46,8 @@
                <db-attribute name="TINYINT_COL" type="TINYINT"/>
        </db-entity>
        <obj-entity name="BigDecimalEntity" 
className="org.apache.cayenne.testdo.numeric_types.BigDecimalEntity" 
dbEntityName="BIGDECIMAL_ENTITY">
-               <obj-attribute name="bigDecimalField" 
type="java.math.BigDecimal" db-attribute-path="BIGDECIMAL_FIELD"/>
+               <obj-attribute name="bigDecimalDecimal" 
type="java.math.BigDecimal" db-attribute-path="BIG_DECIMAL_DECIMAL"/>
+               <obj-attribute name="bigDecimalNumeric" 
type="java.math.BigDecimal" db-attribute-path="BIG_DECIMAL_NUMERIC"/>
        </obj-entity>
        <obj-entity name="BigIntegerEntity" 
className="org.apache.cayenne.testdo.numeric_types.BigIntegerEntity" 
dbEntityName="BIGINTEGER_ENTITY">
                <obj-attribute name="bigIntegerField" 
type="java.math.BigInteger" db-attribute-path="BIG_INTEGER_FIELD"/>

Reply via email to