Skip to content

Commit 074cd9e

Browse files
committed
CAY-2964 ClassCastException for non-generated meaningful PKs
1 parent 5ad7100 commit 074cd9e

6 files changed

Lines changed: 52 additions & 36 deletions

File tree

RELEASE-NOTES.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ CAY-2958 Empty join is saved
2424
CAY-2959 Modeler: DbRelationship dialog "Cancel" doesn't cancel
2525
CAY-2960 Undoing renamed relationship change throws
2626
CAY-2961 PostgreSQL "text" column is reverse-engineered as CLOB
27+
CAY-2964 ClassCastException for non-generated meaningful PKs
2728

2829
----------------------------------
2930
Release: 5.0-M2

cayenne/src/main/java/org/apache/cayenne/access/flush/PermanentObjectIdVisitor.java

Lines changed: 19 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,6 @@
1919

2020
package org.apache.cayenne.access.flush;
2121

22-
import java.util.Map;
23-
2422
import org.apache.cayenne.CayenneRuntimeException;
2523
import org.apache.cayenne.ObjectId;
2624
import org.apache.cayenne.access.DataDomain;
@@ -37,6 +35,8 @@
3735
import org.apache.cayenne.map.ObjEntity;
3836
import org.apache.cayenne.reflect.ClassDescriptor;
3937

38+
import java.util.Map;
39+
4040
/**
4141
* Visitor that fills replacement map of {@link ObjectId}s of inserted objects.
4242
*
@@ -65,9 +65,9 @@ public Void visitInsert(InsertDbRowOp dbRow) {
6565
return null;
6666
}
6767

68-
if((lastObjEntity == null && lastDbEntity == null) || !id.getEntityName().equals(lastEntityName)) {
68+
if ((lastObjEntity == null && lastDbEntity == null) || !id.getEntityName().equals(lastEntityName)) {
6969
lastEntityName = id.getEntityName();
70-
if(lastEntityName.startsWith(ASTDbPath.DB_PREFIX)) {
70+
if (lastEntityName.startsWith(ASTDbPath.DB_PREFIX)) {
7171
lastDbEntity = resolver.getDbEntity(lastEntityName.substring(ASTDbPath.DB_PREFIX.length()));
7272
lastObjEntity = null;
7373
lastDescriptor = null;
@@ -87,7 +87,7 @@ public Void visitInsert(InsertDbRowOp dbRow) {
8787
@Override
8888
public Void visitDelete(DeleteDbRowOp dbRow) {
8989
// flattened ids will be temporary for delete operation, replace it
90-
if(dbRow.getChangeId().isTemporary() && dbRow.getChangeId().isReplacementIdAttached()) {
90+
if (dbRow.getChangeId().isTemporary() && dbRow.getChangeId().isReplacementIdAttached()) {
9191
dbRow.setChangeId(dbRow.getChangeId().createReplacementId());
9292
}
9393
return null;
@@ -110,18 +110,17 @@ private void createPermanentId(InsertDbRowOp dbRow) {
110110
continue;
111111
}
112112

113+
ObjAttribute objAttr = lastObjEntity != null ? lastObjEntity.getAttributeForDbAttribute(dbAttr) : null;
114+
113115
// handle meaningful PK
114-
if(lastObjEntity != null) {
115-
ObjAttribute objAttr = lastObjEntity.getAttributeForDbAttribute(dbAttr);
116-
if (objAttr != null) {
117-
Object value = lastDescriptor.getProperty(objAttr.getName()).readPropertyDirectly(dbRow.getObject());
118-
if (value != null) {
119-
// primitive 0 has to be treated as NULL, or otherwise we can't generate PK for POJO's
120-
Class<?> javaClass = objAttr.getJavaClass();
121-
if (!javaClass.isPrimitive() || !(value instanceof Number number) || number.intValue() != 0) {
122-
idMap.put(dbAttrName, value);
123-
continue;
124-
}
116+
if (objAttr != null) {
117+
Object value = lastDescriptor.getProperty(objAttr.getName()).readPropertyDirectly(dbRow.getObject());
118+
if (value != null) {
119+
// primitive 0 has to be treated as NULL, or otherwise we can't generate PK for POJO's
120+
Class<?> javaClass = objAttr.getJavaClass();
121+
if (!javaClass.isPrimitive() || !(value instanceof Number number) || number.intValue() != 0) {
122+
idMap.put(dbAttrName, value);
123+
continue;
125124
}
126125
}
127126
}
@@ -140,11 +139,13 @@ private void createPermanentId(InsertDbRowOp dbRow) {
140139

141140
// finally, use database generation mechanism
142141
try {
143-
Object pkValue = pkGenerator.generatePk(lastNode, dbAttr);
142+
Class<?> javaType = objAttr != null ? objAttr.getJavaClass() : null;
143+
144+
Object pkValue = pkGenerator.generatePk(lastNode, dbAttr, javaType);
144145
idMap.put(dbAttrName, pkValue);
145146
autoPkDone = true;
146147
} catch (Exception ex) {
147-
throw new CayenneRuntimeException("Error generating PK: %s", ex, ex.getMessage());
148+
throw new CayenneRuntimeException("Error generating PK: %s", ex, ex.getMessage());
148149
}
149150
}
150151
}

cayenne/src/main/java/org/apache/cayenne/dba/JdbcPkGenerator.java

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@
2525
import org.apache.cayenne.ResultIterator;
2626
import org.apache.cayenne.access.DataNode;
2727
import org.apache.cayenne.access.OperationObserver;
28+
import org.apache.cayenne.access.types.ValueObjectType;
29+
import org.apache.cayenne.access.types.ValueObjectTypeRegistry;
2830
import org.apache.cayenne.map.DbAttribute;
2931
import org.apache.cayenne.map.DbEntity;
3032
import org.apache.cayenne.map.DbKeyGenerator;
@@ -185,17 +187,8 @@ public int runUpdate(DataNode node, String sql) throws SQLException {
185187
}
186188
}
187189

188-
/**
189-
* Generates a unique and non-repeating primary key for specified dbEntity.
190-
* <p>
191-
* This implementation is naive since it does not lock the database rows
192-
* when executing select and subsequent update. Adapter-specific
193-
* implementations are more robust.
194-
* </p>
195-
*
196-
* @since 3.0
197-
*/
198-
public Object generatePk(DataNode node, DbAttribute pk) throws Exception {
190+
@Override
191+
public Object generatePk(DataNode node, DbAttribute pk, Class<?> javaType) throws Exception {
199192

200193
DbEntity entity = pk.getEntity();
201194

@@ -239,6 +232,17 @@ public Object generatePk(DataNode node, DbAttribute pk) throws Exception {
239232
}
240233
}
241234

235+
if (javaType != null) {
236+
ValueObjectTypeRegistry registry = node.getEntityResolver().getValueObjectTypeRegistry();
237+
if (registry != null) {
238+
ValueObjectType<Object, Long> converter = (ValueObjectType<Object, Long>) registry.getValueType(javaType);
239+
if (converter != null) {
240+
return converter.toJavaObject(value);
241+
}
242+
}
243+
}
244+
245+
// legacy behavior for plain numeric PKs
242246
if (pk.getType() == Types.BIGINT) {
243247
return value;
244248
} else {

cayenne/src/main/java/org/apache/cayenne/dba/PkGenerator.java

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,12 @@
1919

2020
package org.apache.cayenne.dba;
2121

22-
import java.util.List;
23-
2422
import org.apache.cayenne.access.DataNode;
2523
import org.apache.cayenne.map.DbAttribute;
2624
import org.apache.cayenne.map.DbEntity;
2725

26+
import java.util.List;
27+
2828
/**
2929
* Defines methods to support automatic primary key generation.
3030
*/
@@ -66,9 +66,19 @@ public interface PkGenerator {
6666
/**
6767
* Generates a unique and non-repeating primary key for specified PK attribute.
6868
*
69-
* @since 3.0
69+
* @deprecated unused by the framework that relies on {@link #generatePk(DataNode, DbAttribute, Class)}
70+
*/
71+
@Deprecated(since = "5.0", forRemoval = true)
72+
default Object generatePk(DataNode dataNode, DbAttribute pk) throws Exception {
73+
return generatePk(dataNode, pk, null);
74+
}
75+
76+
/**
77+
* Generates a unique and non-repeating primary key for the specified PK attribute
78+
*
79+
* @since 5.0
7080
*/
71-
Object generatePk(DataNode dataNode, DbAttribute pk) throws Exception;
81+
Object generatePk(DataNode dataNode, DbAttribute pk, Class<?> javaType) throws Exception;
7282

7383
/**
7484
* Install the adapter associated with current PkGenerator

cayenne/src/main/java/org/apache/cayenne/dba/sqlserver/SQLServerPkGenerator.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -105,14 +105,14 @@ public List<String> dropAutoPkStatements(List<DbEntity> dbEntities) {
105105
}
106106

107107
@Override
108-
public Object generatePk(DataNode node, DbAttribute pk) throws Exception {
108+
public Object generatePk(DataNode node, DbAttribute pk, Class<?> javaType) throws Exception {
109109
DbEntity entity = pk.getEntity();
110110

111111
//check key on UNIQUEIDENTIFIER; UNIQUEIDENTIFIER is a character with a length of 36
112112
if (TypesMapping.isCharacter(pk.getType()) && pk.getMaxLength() == MAX_LENGTH_GUID) {
113113
return guidPkFromDatabase(node, entity);
114114
} else {
115-
return super.generatePk(node, pk);
115+
return super.generatePk(node, pk, javaType);
116116
}
117117
}
118118

cayenne/src/test/java/org/apache/cayenne/access/DataContextExtrasIT.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -253,7 +253,7 @@ public void commitChangesError() {
253253
PkGenerator newGenerator = new JdbcPkGenerator(jdbcAdapter) {
254254

255255
@Override
256-
public Object generatePk(DataNode node, DbAttribute pk) {
256+
public Object generatePk(DataNode node, DbAttribute pk, Class<?> javaType) {
257257
throw new CayenneRuntimeException("Intentional");
258258
}
259259
};

0 commit comments

Comments
 (0)