This is an automated email from the ASF dual-hosted git repository.
ntimofeev pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/cayenne.git
The following commit(s) were added to refs/heads/master by this push:
new e24c57ede CAY-2900 Meaningful generated PKs could lead to the
ClassCastException
e24c57ede is described below
commit e24c57ededa97a24b5d123e78c3e565d7ad61f38
Author: Nikita Timofeev <[email protected]>
AuthorDate: Fri Oct 10 15:00:45 2025 +0400
CAY-2900 Meaningful generated PKs could lead to the ClassCastException
---
RELEASE-NOTES.txt | 1 +
.../apache/cayenne/access/jdbc/BatchAction.java | 26 +++--
.../java/org/apache/cayenne/query/BatchQuery.java | 1 -
.../DataContextEntityWithMeaningfulPKIT.java | 18 ++++
.../meaningful_pk/MeaningfulPkBigintGenerated.java | 27 ++++++
.../auto/_MeaningfulPkBigintGenerated.java | 108 +++++++++++++++++++++
cayenne/src/test/resources/meaningful-pk.map.xml | 8 ++
7 files changed, 181 insertions(+), 8 deletions(-)
diff --git a/RELEASE-NOTES.txt b/RELEASE-NOTES.txt
index 1c6a8a33a..3b667923d 100644
--- a/RELEASE-NOTES.txt
+++ b/RELEASE-NOTES.txt
@@ -30,6 +30,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: 5.0-M1
diff --git
a/cayenne/src/main/java/org/apache/cayenne/access/jdbc/BatchAction.java
b/cayenne/src/main/java/org/apache/cayenne/access/jdbc/BatchAction.java
index 131aea3c4..92e8917af 100644
--- a/cayenne/src/main/java/org/apache/cayenne/access/jdbc/BatchAction.java
+++ b/cayenne/src/main/java/org/apache/cayenne/access/jdbc/BatchAction.java
@@ -28,9 +28,10 @@ import org.apache.cayenne.access.jdbc.reader.RowReader;
import org.apache.cayenne.access.translator.DbAttributeBinding;
import org.apache.cayenne.access.translator.batch.BatchTranslator;
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,11 +262,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();
@@ -280,7 +280,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(key.getJavaClass());
+
columns[0].setJavaClass(typeForGeneratedPK(key));
builder.setColumns(columns);
} else {
builder.setResultSet(keysRS);
@@ -299,4 +299,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 key.getJavaClass();
+ }
}
diff --git a/cayenne/src/main/java/org/apache/cayenne/query/BatchQuery.java
b/cayenne/src/main/java/org/apache/cayenne/query/BatchQuery.java
index 4bce2b8f2..874311e92 100644
--- a/cayenne/src/main/java/org/apache/cayenne/query/BatchQuery.java
+++ b/cayenne/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/src/test/java/org/apache/cayenne/access/DataContextEntityWithMeaningfulPKIT.java
b/cayenne/src/test/java/org/apache/cayenne/access/DataContextEntityWithMeaningfulPKIT.java
index a57aa89a3..0c379f73d 100644
---
a/cayenne/src/test/java/org/apache/cayenne/access/DataContextEntityWithMeaningfulPKIT.java
+++
b/cayenne/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.runtime.CayenneProjects;
@@ -327,4 +328,21 @@ public class DataContextEntityWithMeaningfulPKIT extends
RuntimeCase {
assertTrue(pk.getPk().compareTo(BigInteger.valueOf(120)) > 0);
}
}
+
+ @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/src/test/java/org/apache/cayenne/testdo/meaningful_pk/MeaningfulPkBigintGenerated.java
b/cayenne/src/test/java/org/apache/cayenne/testdo/meaningful_pk/MeaningfulPkBigintGenerated.java
new file mode 100644
index 000000000..b3c0bb93c
--- /dev/null
+++
b/cayenne/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/src/test/java/org/apache/cayenne/testdo/meaningful_pk/auto/_MeaningfulPkBigintGenerated.java
b/cayenne/src/test/java/org/apache/cayenne/testdo/meaningful_pk/auto/_MeaningfulPkBigintGenerated.java
new file mode 100644
index 000000000..3217ebee0
--- /dev/null
+++
b/cayenne/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/src/test/resources/meaningful-pk.map.xml
b/cayenne/src/test/resources/meaningful-pk.map.xml
index 4e7415e13..819bfb7c3 100644
--- a/cayenne/src/test/resources/meaningful-pk.map.xml
+++ b/cayenne/src/test/resources/meaningful-pk.map.xml
@@ -10,6 +10,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"/>
@@ -46,6 +50,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"
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"/>