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

ntimofeev pushed a commit to branch STABLE-4.2
in repository https://gitbox.apache.org/repos/asf/cayenne.git


The following commit(s) were added to refs/heads/STABLE-4.2 by this push:
     new b052080a4 CAY-2900 Meaningful generated PKs could lead to the 
ClassCastException
b052080a4 is described below

commit b052080a401cdc9a7403b89997616de5bededc0c
Author: Nikita Timofeev <[email protected]>
AuthorDate: Thu Oct 9 18:37:27 2025 +0400

    CAY-2900 Meaningful generated PKs could lead to the ClassCastException
---
 RELEASE-NOTES.txt                                  |   1 +
 .../apache/cayenne/access/jdbc/BatchAction.java    |  28 ++++--
 .../java/org/apache/cayenne/query/BatchQuery.java  |   1 -
 .../DataContextEntityWithMeaningfulPKIT.java       |  17 ++++
 .../meaningful_pk/MeaningfulPkBigintGenerated.java |  27 ++++++
 .../auto/_MeaningfulPkBigintGenerated.java         | 108 +++++++++++++++++++++
 .../src/test/resources/meaningful-pk.map.xml       |   8 ++
 7 files changed, 180 insertions(+), 10 deletions(-)

diff --git a/RELEASE-NOTES.txt b/RELEASE-NOTES.txt
index 2a6fd16b0..4e4c3b968 100644
--- a/RELEASE-NOTES.txt
+++ b/RELEASE-NOTES.txt
@@ -23,6 +23,7 @@ CAY-2883 License and notice templates are not processed by 
the Gradle build
 CAY-2885 Modeler: DbImport fails to load DB schema view
 CAY-2896 Inserting two identical objects into two datamaps stores both objects 
in the last used datamap
 CAY-2898 Crypto: NPE in a ColumnQuery
+CAY-2900 Meaningful generated PKs could lead to the ClassCastException
 
 ----------------------------------
 Release: 4.2.2
diff --git 
a/cayenne-server/src/main/java/org/apache/cayenne/access/jdbc/BatchAction.java 
b/cayenne-server/src/main/java/org/apache/cayenne/access/jdbc/BatchAction.java
index 2788e4110..64c294f80 100644
--- 
a/cayenne-server/src/main/java/org/apache/cayenne/access/jdbc/BatchAction.java
+++ 
b/cayenne-server/src/main/java/org/apache/cayenne/access/jdbc/BatchAction.java
@@ -31,6 +31,8 @@ import org.apache.cayenne.dba.DbAdapter;
 import org.apache.cayenne.dba.TypesMapping;
 import org.apache.cayenne.log.JdbcEventLogger;
 import org.apache.cayenne.map.DbAttribute;
+import org.apache.cayenne.map.ObjAttribute;
+import org.apache.cayenne.map.ObjEntity;
 import org.apache.cayenne.query.BatchQuery;
 import org.apache.cayenne.query.BatchQueryRow;
 import org.apache.cayenne.query.InsertBatchQuery;
@@ -261,14 +263,10 @@ public class BatchAction extends BaseSQLAction {
 
                ResultSet keysRS = statement.getGeneratedKeys();
 
-               // TODO: andrus, 7/4/2007 - (1) get the type of meaningful PK's 
from
-               // their
-               // ObjAttributes; (2) use a different form of Statement.execute 
-
-               // "execute(String,String[])" to be able to map generated 
column names
-               // (this way
-               // we can support multiple columns.. although need to check how 
well
-               // this works
-               // with most common drivers)
+               // TODO: andrus, 7/4/2007 - use a different form of 
Statement.execute -
+               //  "execute(String,String[])" to be able to map generated 
column names
+               // (this way we can support multiple columns..
+               // although need to check how well this works with most common 
drivers)
 
                RowDescriptorBuilder builder = new RowDescriptorBuilder();
 
@@ -283,7 +281,7 @@ public class BatchAction extends BaseSQLAction {
                                // use column name from result set, but type 
and Java class from DB attribute
                                columns[0] = new 
ColumnDescriptor(keysRS.getMetaData(), 1);
                                columns[0].setJdbcType(key.getType());
-                               
columns[0].setJavaClass(TypesMapping.getJavaBySqlType(key));
+                               
columns[0].setJavaClass(typeForGeneratedPK(key));
                                builder.setColumns(columns);
                        } else {
                                builder.setResultSet(keysRS);
@@ -302,4 +300,16 @@ public class BatchAction extends BaseSQLAction {
                }
                observer.nextGeneratedRows(query, iterator, objectIds);
        }
+
+       private String typeForGeneratedPK(DbAttribute key) {
+               String entityName = 
getQuery().getRows().get(0).getObjectId().getEntityName();
+               ObjEntity objEntity = 
dataNode.getEntityResolver().getObjEntity(entityName);
+               if(objEntity != null) {
+                       ObjAttribute attributeForDbAttribute = 
objEntity.getAttributeForDbAttribute(key);
+                       if(attributeForDbAttribute != null) {
+                               return attributeForDbAttribute.getType();
+                       }
+               }
+               return TypesMapping.getJavaBySqlType(key);
+       }
 }
diff --git 
a/cayenne-server/src/main/java/org/apache/cayenne/query/BatchQuery.java 
b/cayenne-server/src/main/java/org/apache/cayenne/query/BatchQuery.java
index 4bce2b8f2..874311e92 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/query/BatchQuery.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/query/BatchQuery.java
@@ -19,7 +19,6 @@
 
 package org.apache.cayenne.query;
 
-import org.apache.cayenne.map.DataMap;
 import org.apache.cayenne.map.DbAttribute;
 import org.apache.cayenne.map.DbEntity;
 import org.apache.cayenne.map.EntityResolver;
diff --git 
a/cayenne-server/src/test/java/org/apache/cayenne/access/DataContextEntityWithMeaningfulPKIT.java
 
b/cayenne-server/src/test/java/org/apache/cayenne/access/DataContextEntityWithMeaningfulPKIT.java
index 16198a384..65d78a149 100644
--- 
a/cayenne-server/src/test/java/org/apache/cayenne/access/DataContextEntityWithMeaningfulPKIT.java
+++ 
b/cayenne-server/src/test/java/org/apache/cayenne/access/DataContextEntityWithMeaningfulPKIT.java
@@ -32,6 +32,7 @@ import 
org.apache.cayenne.testdo.meaningful_pk.MeaningfulPKDep;
 import org.apache.cayenne.testdo.meaningful_pk.MeaningfulPKTest1;
 import org.apache.cayenne.testdo.meaningful_pk.MeaningfulPk;
 import org.apache.cayenne.testdo.meaningful_pk.MeaningfulPkBigint;
+import org.apache.cayenne.testdo.meaningful_pk.MeaningfulPkBigintGenerated;
 import org.apache.cayenne.testdo.meaningful_pk.MeaningfulPkDep2;
 import org.apache.cayenne.testdo.meaningful_pk.MeaningfulPkTest2;
 import org.apache.cayenne.unit.di.server.CayenneProjects;
@@ -330,4 +331,20 @@ public class DataContextEntityWithMeaningfulPKIT extends 
ServerCase {
         }
     }
 
+    @Test
+    public void testGeneratedBigIntegerPK() {
+        MeaningfulPkBigintGenerated pkObj1 = 
context.newObject(MeaningfulPkBigintGenerated.class);
+        MeaningfulPkBigintGenerated pkObj2 = 
context.newObject(MeaningfulPkBigintGenerated.class);
+        MeaningfulPkBigintGenerated pkObj3 = 
context.newObject(MeaningfulPkBigintGenerated.class);
+
+        context.commitChanges();
+
+        assertNotNull(pkObj1.getPk());
+        assertNotNull(pkObj2.getPk());
+        assertNotNull(pkObj3.getPk());
+        assertTrue(pkObj1.getPk().compareTo(BigInteger.ZERO) > 0);
+        assertTrue(pkObj2.getPk().compareTo(BigInteger.ZERO) > 0);
+        assertTrue(pkObj3.getPk().compareTo(BigInteger.ZERO) > 0);
+    }
+
 }
diff --git 
a/cayenne-server/src/test/java/org/apache/cayenne/testdo/meaningful_pk/MeaningfulPkBigintGenerated.java
 
b/cayenne-server/src/test/java/org/apache/cayenne/testdo/meaningful_pk/MeaningfulPkBigintGenerated.java
new file mode 100644
index 000000000..b3c0bb93c
--- /dev/null
+++ 
b/cayenne-server/src/test/java/org/apache/cayenne/testdo/meaningful_pk/MeaningfulPkBigintGenerated.java
@@ -0,0 +1,27 @@
+/*****************************************************************
+ *   Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    https://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ ****************************************************************/
+package org.apache.cayenne.testdo.meaningful_pk;
+
+import 
org.apache.cayenne.testdo.meaningful_pk.auto._MeaningfulPkBigintGenerated;
+
+public class MeaningfulPkBigintGenerated extends _MeaningfulPkBigintGenerated {
+
+    private static final long serialVersionUID = 1L;
+
+}
diff --git 
a/cayenne-server/src/test/java/org/apache/cayenne/testdo/meaningful_pk/auto/_MeaningfulPkBigintGenerated.java
 
b/cayenne-server/src/test/java/org/apache/cayenne/testdo/meaningful_pk/auto/_MeaningfulPkBigintGenerated.java
new file mode 100644
index 000000000..3217ebee0
--- /dev/null
+++ 
b/cayenne-server/src/test/java/org/apache/cayenne/testdo/meaningful_pk/auto/_MeaningfulPkBigintGenerated.java
@@ -0,0 +1,108 @@
+package org.apache.cayenne.testdo.meaningful_pk.auto;
+
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.math.BigInteger;
+
+import org.apache.cayenne.BaseDataObject;
+import org.apache.cayenne.exp.property.NumericProperty;
+import org.apache.cayenne.exp.property.PropertyFactory;
+import org.apache.cayenne.exp.property.StringProperty;
+
+/**
+ * Class _MeaningfulPkBigintGenerated was generated by Cayenne.
+ * It is probably a good idea to avoid changing this class manually,
+ * since it may be overwritten next time code is regenerated.
+ * If you need to make any customizations, please use subclass.
+ */
+public abstract class _MeaningfulPkBigintGenerated extends BaseDataObject {
+
+    private static final long serialVersionUID = 1L;
+
+    public static final String PK_PK_COLUMN = "PK";
+
+    public static final NumericProperty<BigInteger> PK = 
PropertyFactory.createNumeric("pk", BigInteger.class);
+    public static final StringProperty<String> TEST_ATTR = 
PropertyFactory.createString("testAttr", String.class);
+
+    protected BigInteger pk;
+    protected String testAttr;
+
+
+    public void setPk(BigInteger pk) {
+        beforePropertyWrite("pk", this.pk, pk);
+        this.pk = pk;
+    }
+
+    public BigInteger getPk() {
+        beforePropertyRead("pk");
+        return this.pk;
+    }
+
+    public void setTestAttr(String testAttr) {
+        beforePropertyWrite("testAttr", this.testAttr, testAttr);
+        this.testAttr = testAttr;
+    }
+
+    public String getTestAttr() {
+        beforePropertyRead("testAttr");
+        return this.testAttr;
+    }
+
+    @Override
+    public Object readPropertyDirectly(String propName) {
+        if(propName == null) {
+            throw new IllegalArgumentException();
+        }
+
+        switch(propName) {
+            case "pk":
+                return this.pk;
+            case "testAttr":
+                return this.testAttr;
+            default:
+                return super.readPropertyDirectly(propName);
+        }
+    }
+
+    @Override
+    public void writePropertyDirectly(String propName, Object val) {
+        if(propName == null) {
+            throw new IllegalArgumentException();
+        }
+
+        switch (propName) {
+            case "pk":
+                this.pk = (BigInteger)val;
+                break;
+            case "testAttr":
+                this.testAttr = (String)val;
+                break;
+            default:
+                super.writePropertyDirectly(propName, val);
+        }
+    }
+
+    private void writeObject(ObjectOutputStream out) throws IOException {
+        writeSerialized(out);
+    }
+
+    private void readObject(ObjectInputStream in) throws IOException, 
ClassNotFoundException {
+        readSerialized(in);
+    }
+
+    @Override
+    protected void writeState(ObjectOutputStream out) throws IOException {
+        super.writeState(out);
+        out.writeObject(this.pk);
+        out.writeObject(this.testAttr);
+    }
+
+    @Override
+    protected void readState(ObjectInputStream in) throws IOException, 
ClassNotFoundException {
+        super.readState(in);
+        this.pk = (BigInteger)in.readObject();
+        this.testAttr = (String)in.readObject();
+    }
+
+}
diff --git a/cayenne-server/src/test/resources/meaningful-pk.map.xml 
b/cayenne-server/src/test/resources/meaningful-pk.map.xml
index 4202bed9f..51c238ea3 100644
--- a/cayenne-server/src/test/resources/meaningful-pk.map.xml
+++ b/cayenne-server/src/test/resources/meaningful-pk.map.xml
@@ -12,6 +12,10 @@
        <db-entity name="MEANINGFUL_PK_BIGINT">
                <db-attribute name="PK" type="INTEGER" isPrimaryKey="true" 
isMandatory="true"/>
        </db-entity>
+       <db-entity name="MEANINGFUL_PK_BIGINT_GENERATED">
+               <db-attribute name="PK" type="INTEGER" isPrimaryKey="true" 
isGenerated="true" isMandatory="true"/>
+               <db-attribute name="TEST_ATTR" type="VARCHAR" length="255"/>
+       </db-entity>
        <db-entity name="MEANINGFUL_PK_DEP">
                <db-attribute name="DESCR" type="VARCHAR" length="50"/>
                <db-attribute name="MASTER_PK" type="INTEGER"/>
@@ -48,6 +52,10 @@
        <obj-entity name="MeaningfulPkBigint" 
className="org.apache.cayenne.testdo.meaningful_pk.MeaningfulPkBigint" 
dbEntityName="MEANINGFUL_PK_BIGINT">
                <obj-attribute name="pk" type="java.math.BigInteger" 
db-attribute-path="PK"/>
        </obj-entity>
+       <obj-entity name="MeaningfulPkBigintGenerated" 
className="org.apache.cayenne.testdo.meaningful_pk.MeaningfulPkBigintGenerated" 
clientClassName="org.apache.cayenne.testdo.meaningful_pk.MeaningfulPkBigintGenerated"
 dbEntityName="MEANINGFUL_PK_BIGINT_GENERATED">
+               <obj-attribute name="pk" type="java.math.BigInteger" 
db-attribute-path="PK"/>
+               <obj-attribute name="testAttr" type="java.lang.String" 
db-attribute-path="TEST_ATTR"/>
+       </obj-entity>
        <obj-entity name="MeaningfulPkDep2" 
className="org.apache.cayenne.testdo.meaningful_pk.MeaningfulPkDep2" 
clientClassName="org.apache.cayenne.testdo.meaningful_pk.MeaningfulPkDep2" 
dbEntityName="MEANINGFUL_PK_DEP2">
                <obj-attribute name="descr" type="java.lang.String" 
db-attribute-path="DESCR"/>
                <obj-attribute name="pk" type="java.lang.String" 
db-attribute-path="PK"/>

Reply via email to