This is an automated email from the ASF dual-hosted git repository. asf-gitbox-commits pushed a commit to branch past-M2 in repository https://gitbox.apache.org/repos/asf/cayenne.git
commit 074cd9eaf1732191d05f75a2b525eb74947fcb66 Author: Andrus Adamchik <[email protected]> AuthorDate: Sun Jun 14 13:47:28 2026 -0400 CAY-2964 ClassCastException for non-generated meaningful PKs --- RELEASE-NOTES.txt | 1 + .../access/flush/PermanentObjectIdVisitor.java | 37 +++++++++++----------- .../org/apache/cayenne/dba/JdbcPkGenerator.java | 26 ++++++++------- .../java/org/apache/cayenne/dba/PkGenerator.java | 18 ++++++++--- .../dba/sqlserver/SQLServerPkGenerator.java | 4 +-- .../apache/cayenne/access/DataContextExtrasIT.java | 2 +- 6 files changed, 52 insertions(+), 36 deletions(-) diff --git a/RELEASE-NOTES.txt b/RELEASE-NOTES.txt index cc06801ef..e381602f4 100644 --- a/RELEASE-NOTES.txt +++ b/RELEASE-NOTES.txt @@ -24,6 +24,7 @@ CAY-2958 Empty join is saved CAY-2959 Modeler: DbRelationship dialog "Cancel" doesn't cancel CAY-2960 Undoing renamed relationship change throws CAY-2961 PostgreSQL "text" column is reverse-engineered as CLOB +CAY-2964 ClassCastException for non-generated meaningful PKs ---------------------------------- Release: 5.0-M2 diff --git a/cayenne/src/main/java/org/apache/cayenne/access/flush/PermanentObjectIdVisitor.java b/cayenne/src/main/java/org/apache/cayenne/access/flush/PermanentObjectIdVisitor.java index d1412a6d9..a412db1c2 100644 --- a/cayenne/src/main/java/org/apache/cayenne/access/flush/PermanentObjectIdVisitor.java +++ b/cayenne/src/main/java/org/apache/cayenne/access/flush/PermanentObjectIdVisitor.java @@ -19,8 +19,6 @@ package org.apache.cayenne.access.flush; -import java.util.Map; - import org.apache.cayenne.CayenneRuntimeException; import org.apache.cayenne.ObjectId; import org.apache.cayenne.access.DataDomain; @@ -37,6 +35,8 @@ import org.apache.cayenne.map.ObjAttribute; import org.apache.cayenne.map.ObjEntity; import org.apache.cayenne.reflect.ClassDescriptor; +import java.util.Map; + /** * Visitor that fills replacement map of {@link ObjectId}s of inserted objects. * @@ -65,9 +65,9 @@ class PermanentObjectIdVisitor implements DbRowOpVisitor<Void> { return null; } - if((lastObjEntity == null && lastDbEntity == null) || !id.getEntityName().equals(lastEntityName)) { + if ((lastObjEntity == null && lastDbEntity == null) || !id.getEntityName().equals(lastEntityName)) { lastEntityName = id.getEntityName(); - if(lastEntityName.startsWith(ASTDbPath.DB_PREFIX)) { + if (lastEntityName.startsWith(ASTDbPath.DB_PREFIX)) { lastDbEntity = resolver.getDbEntity(lastEntityName.substring(ASTDbPath.DB_PREFIX.length())); lastObjEntity = null; lastDescriptor = null; @@ -87,7 +87,7 @@ class PermanentObjectIdVisitor implements DbRowOpVisitor<Void> { @Override public Void visitDelete(DeleteDbRowOp dbRow) { // flattened ids will be temporary for delete operation, replace it - if(dbRow.getChangeId().isTemporary() && dbRow.getChangeId().isReplacementIdAttached()) { + if (dbRow.getChangeId().isTemporary() && dbRow.getChangeId().isReplacementIdAttached()) { dbRow.setChangeId(dbRow.getChangeId().createReplacementId()); } return null; @@ -110,18 +110,17 @@ class PermanentObjectIdVisitor implements DbRowOpVisitor<Void> { continue; } + ObjAttribute objAttr = lastObjEntity != null ? lastObjEntity.getAttributeForDbAttribute(dbAttr) : null; + // handle meaningful PK - if(lastObjEntity != null) { - ObjAttribute objAttr = lastObjEntity.getAttributeForDbAttribute(dbAttr); - if (objAttr != null) { - Object value = lastDescriptor.getProperty(objAttr.getName()).readPropertyDirectly(dbRow.getObject()); - if (value != null) { - // primitive 0 has to be treated as NULL, or otherwise we can't generate PK for POJO's - Class<?> javaClass = objAttr.getJavaClass(); - if (!javaClass.isPrimitive() || !(value instanceof Number number) || number.intValue() != 0) { - idMap.put(dbAttrName, value); - continue; - } + if (objAttr != null) { + Object value = lastDescriptor.getProperty(objAttr.getName()).readPropertyDirectly(dbRow.getObject()); + if (value != null) { + // primitive 0 has to be treated as NULL, or otherwise we can't generate PK for POJO's + Class<?> javaClass = objAttr.getJavaClass(); + if (!javaClass.isPrimitive() || !(value instanceof Number number) || number.intValue() != 0) { + idMap.put(dbAttrName, value); + continue; } } } @@ -140,11 +139,13 @@ class PermanentObjectIdVisitor implements DbRowOpVisitor<Void> { // finally, use database generation mechanism try { - Object pkValue = pkGenerator.generatePk(lastNode, dbAttr); + Class<?> javaType = objAttr != null ? objAttr.getJavaClass() : null; + + Object pkValue = pkGenerator.generatePk(lastNode, dbAttr, javaType); idMap.put(dbAttrName, pkValue); autoPkDone = true; } catch (Exception ex) { - throw new CayenneRuntimeException("Error generating PK: %s", ex, ex.getMessage()); + throw new CayenneRuntimeException("Error generating PK: %s", ex, ex.getMessage()); } } } diff --git a/cayenne/src/main/java/org/apache/cayenne/dba/JdbcPkGenerator.java b/cayenne/src/main/java/org/apache/cayenne/dba/JdbcPkGenerator.java index 6423370ef..9e15df372 100644 --- a/cayenne/src/main/java/org/apache/cayenne/dba/JdbcPkGenerator.java +++ b/cayenne/src/main/java/org/apache/cayenne/dba/JdbcPkGenerator.java @@ -25,6 +25,8 @@ import org.apache.cayenne.ObjectId; import org.apache.cayenne.ResultIterator; import org.apache.cayenne.access.DataNode; import org.apache.cayenne.access.OperationObserver; +import org.apache.cayenne.access.types.ValueObjectType; +import org.apache.cayenne.access.types.ValueObjectTypeRegistry; import org.apache.cayenne.map.DbAttribute; import org.apache.cayenne.map.DbEntity; import org.apache.cayenne.map.DbKeyGenerator; @@ -185,17 +187,8 @@ public class JdbcPkGenerator implements PkGenerator { } } - /** - * Generates a unique and non-repeating primary key for specified dbEntity. - * <p> - * This implementation is naive since it does not lock the database rows - * when executing select and subsequent update. Adapter-specific - * implementations are more robust. - * </p> - * - * @since 3.0 - */ - public Object generatePk(DataNode node, DbAttribute pk) throws Exception { + @Override + public Object generatePk(DataNode node, DbAttribute pk, Class<?> javaType) throws Exception { DbEntity entity = pk.getEntity(); @@ -239,6 +232,17 @@ public class JdbcPkGenerator implements PkGenerator { } } + if (javaType != null) { + ValueObjectTypeRegistry registry = node.getEntityResolver().getValueObjectTypeRegistry(); + if (registry != null) { + ValueObjectType<Object, Long> converter = (ValueObjectType<Object, Long>) registry.getValueType(javaType); + if (converter != null) { + return converter.toJavaObject(value); + } + } + } + + // legacy behavior for plain numeric PKs if (pk.getType() == Types.BIGINT) { return value; } else { diff --git a/cayenne/src/main/java/org/apache/cayenne/dba/PkGenerator.java b/cayenne/src/main/java/org/apache/cayenne/dba/PkGenerator.java index 440ce5ec4..a7a79222f 100644 --- a/cayenne/src/main/java/org/apache/cayenne/dba/PkGenerator.java +++ b/cayenne/src/main/java/org/apache/cayenne/dba/PkGenerator.java @@ -19,12 +19,12 @@ package org.apache.cayenne.dba; -import java.util.List; - import org.apache.cayenne.access.DataNode; import org.apache.cayenne.map.DbAttribute; import org.apache.cayenne.map.DbEntity; +import java.util.List; + /** * Defines methods to support automatic primary key generation. */ @@ -66,9 +66,19 @@ public interface PkGenerator { /** * Generates a unique and non-repeating primary key for specified PK attribute. * - * @since 3.0 + * @deprecated unused by the framework that relies on {@link #generatePk(DataNode, DbAttribute, Class)} + */ + @Deprecated(since = "5.0", forRemoval = true) + default Object generatePk(DataNode dataNode, DbAttribute pk) throws Exception { + return generatePk(dataNode, pk, null); + } + + /** + * Generates a unique and non-repeating primary key for the specified PK attribute + * + * @since 5.0 */ - Object generatePk(DataNode dataNode, DbAttribute pk) throws Exception; + Object generatePk(DataNode dataNode, DbAttribute pk, Class<?> javaType) throws Exception; /** * Install the adapter associated with current PkGenerator diff --git a/cayenne/src/main/java/org/apache/cayenne/dba/sqlserver/SQLServerPkGenerator.java b/cayenne/src/main/java/org/apache/cayenne/dba/sqlserver/SQLServerPkGenerator.java index 36800e375..5dd23a2e4 100644 --- a/cayenne/src/main/java/org/apache/cayenne/dba/sqlserver/SQLServerPkGenerator.java +++ b/cayenne/src/main/java/org/apache/cayenne/dba/sqlserver/SQLServerPkGenerator.java @@ -105,14 +105,14 @@ public class SQLServerPkGenerator extends OraclePkGenerator { } @Override - public Object generatePk(DataNode node, DbAttribute pk) throws Exception { + public Object generatePk(DataNode node, DbAttribute pk, Class<?> javaType) throws Exception { DbEntity entity = pk.getEntity(); //check key on UNIQUEIDENTIFIER; UNIQUEIDENTIFIER is a character with a length of 36 if (TypesMapping.isCharacter(pk.getType()) && pk.getMaxLength() == MAX_LENGTH_GUID) { return guidPkFromDatabase(node, entity); } else { - return super.generatePk(node, pk); + return super.generatePk(node, pk, javaType); } } diff --git a/cayenne/src/test/java/org/apache/cayenne/access/DataContextExtrasIT.java b/cayenne/src/test/java/org/apache/cayenne/access/DataContextExtrasIT.java index 1e4ed51b0..eee773d11 100644 --- a/cayenne/src/test/java/org/apache/cayenne/access/DataContextExtrasIT.java +++ b/cayenne/src/test/java/org/apache/cayenne/access/DataContextExtrasIT.java @@ -253,7 +253,7 @@ public class DataContextExtrasIT { PkGenerator newGenerator = new JdbcPkGenerator(jdbcAdapter) { @Override - public Object generatePk(DataNode node, DbAttribute pk) { + public Object generatePk(DataNode node, DbAttribute pk, Class<?> javaType) { throw new CayenneRuntimeException("Intentional"); } };
