Skip to content

Commit e24c57e

Browse files
committed
CAY-2900 Meaningful generated PKs could lead to the ClassCastException
1 parent 647edf3 commit e24c57e

File tree

7 files changed

+181
-8
lines changed

7 files changed

+181
-8
lines changed

RELEASE-NOTES.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ CAY-2883 License and notice templates are not processed by the Gradle build
3030
CAY-2885 Modeler: DbImport fails to load DB schema view
3131
CAY-2896 Inserting two identical objects into two datamaps stores both objects in the last used datamap
3232
CAY-2898 Crypto: NPE in a ColumnQuery
33+
CAY-2900 Meaningful generated PKs could lead to the ClassCastException
3334

3435
----------------------------------
3536
Release: 5.0-M1

cayenne/src/main/java/org/apache/cayenne/access/jdbc/BatchAction.java

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,10 @@
2828
import org.apache.cayenne.access.translator.DbAttributeBinding;
2929
import org.apache.cayenne.access.translator.batch.BatchTranslator;
3030
import org.apache.cayenne.dba.DbAdapter;
31-
import org.apache.cayenne.dba.TypesMapping;
3231
import org.apache.cayenne.log.JdbcEventLogger;
3332
import org.apache.cayenne.map.DbAttribute;
33+
import org.apache.cayenne.map.ObjAttribute;
34+
import org.apache.cayenne.map.ObjEntity;
3435
import org.apache.cayenne.query.BatchQuery;
3536
import org.apache.cayenne.query.BatchQueryRow;
3637
import org.apache.cayenne.query.InsertBatchQuery;
@@ -261,11 +262,10 @@ protected void processGeneratedKeys(Statement statement, OperationObserver obser
261262

262263
ResultSet keysRS = statement.getGeneratedKeys();
263264

264-
// TODO: andrus, 7/4/2007 -
265-
// (1) get the type of meaningful PK's from their ObjAttributes;
266-
// (2) use a different form of Statement.execute - "execute(String,String[])" to be able to map
267-
// generated column names (this way we can support multiple columns.. although need to check how well
268-
// this works with most common drivers)
265+
// TODO: andrus, 7/4/2007 - use a different form of Statement.execute -
266+
// "execute(String,String[])" to be able to map generated column names
267+
// (this way we can support multiple columns..
268+
// although need to check how well this works with most common drivers)
269269

270270
RowDescriptorBuilder builder = new RowDescriptorBuilder();
271271

@@ -280,7 +280,7 @@ protected void processGeneratedKeys(Statement statement, OperationObserver obser
280280
// use column name from result set, but type and Java class from DB attribute
281281
columns[0] = new ColumnDescriptor(keysRS.getMetaData(), 1);
282282
columns[0].setJdbcType(key.getType());
283-
columns[0].setJavaClass(key.getJavaClass());
283+
columns[0].setJavaClass(typeForGeneratedPK(key));
284284
builder.setColumns(columns);
285285
} else {
286286
builder.setResultSet(keysRS);
@@ -299,4 +299,16 @@ protected void processGeneratedKeys(Statement statement, OperationObserver obser
299299
}
300300
observer.nextGeneratedRows(query, iterator, objectIds);
301301
}
302+
303+
private String typeForGeneratedPK(DbAttribute key) {
304+
String entityName = getQuery().getRows().get(0).getObjectId().getEntityName();
305+
ObjEntity objEntity = dataNode.getEntityResolver().getObjEntity(entityName);
306+
if(objEntity != null) {
307+
ObjAttribute attributeForDbAttribute = objEntity.getAttributeForDbAttribute(key);
308+
if(attributeForDbAttribute != null) {
309+
return attributeForDbAttribute.getType();
310+
}
311+
}
312+
return key.getJavaClass();
313+
}
302314
}

cayenne/src/main/java/org/apache/cayenne/query/BatchQuery.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@
1919

2020
package org.apache.cayenne.query;
2121

22-
import org.apache.cayenne.map.DataMap;
2322
import org.apache.cayenne.map.DbAttribute;
2423
import org.apache.cayenne.map.DbEntity;
2524
import org.apache.cayenne.map.EntityResolver;

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

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
import org.apache.cayenne.testdo.meaningful_pk.MeaningfulPKTest1;
3333
import org.apache.cayenne.testdo.meaningful_pk.MeaningfulPk;
3434
import org.apache.cayenne.testdo.meaningful_pk.MeaningfulPkBigint;
35+
import org.apache.cayenne.testdo.meaningful_pk.MeaningfulPkBigintGenerated;
3536
import org.apache.cayenne.testdo.meaningful_pk.MeaningfulPkDep2;
3637
import org.apache.cayenne.testdo.meaningful_pk.MeaningfulPkTest2;
3738
import org.apache.cayenne.unit.di.runtime.CayenneProjects;
@@ -327,4 +328,21 @@ public void testPaginatedQueryBigInteger() {
327328
assertTrue(pk.getPk().compareTo(BigInteger.valueOf(120)) > 0);
328329
}
329330
}
331+
332+
@Test
333+
public void testGeneratedBigIntegerPK() {
334+
MeaningfulPkBigintGenerated pkObj1 = context.newObject(MeaningfulPkBigintGenerated.class);
335+
MeaningfulPkBigintGenerated pkObj2 = context.newObject(MeaningfulPkBigintGenerated.class);
336+
MeaningfulPkBigintGenerated pkObj3 = context.newObject(MeaningfulPkBigintGenerated.class);
337+
338+
context.commitChanges();
339+
340+
assertNotNull(pkObj1.getPk());
341+
assertNotNull(pkObj2.getPk());
342+
assertNotNull(pkObj3.getPk());
343+
assertTrue(pkObj1.getPk().compareTo(BigInteger.ZERO) > 0);
344+
assertTrue(pkObj2.getPk().compareTo(BigInteger.ZERO) > 0);
345+
assertTrue(pkObj3.getPk().compareTo(BigInteger.ZERO) > 0);
346+
}
347+
330348
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
/*****************************************************************
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* https://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
****************************************************************/
19+
package org.apache.cayenne.testdo.meaningful_pk;
20+
21+
import org.apache.cayenne.testdo.meaningful_pk.auto._MeaningfulPkBigintGenerated;
22+
23+
public class MeaningfulPkBigintGenerated extends _MeaningfulPkBigintGenerated {
24+
25+
private static final long serialVersionUID = 1L;
26+
27+
}
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
package org.apache.cayenne.testdo.meaningful_pk.auto;
2+
3+
import java.io.IOException;
4+
import java.io.ObjectInputStream;
5+
import java.io.ObjectOutputStream;
6+
import java.math.BigInteger;
7+
8+
import org.apache.cayenne.BaseDataObject;
9+
import org.apache.cayenne.exp.property.NumericProperty;
10+
import org.apache.cayenne.exp.property.PropertyFactory;
11+
import org.apache.cayenne.exp.property.StringProperty;
12+
13+
/**
14+
* Class _MeaningfulPkBigintGenerated was generated by Cayenne.
15+
* It is probably a good idea to avoid changing this class manually,
16+
* since it may be overwritten next time code is regenerated.
17+
* If you need to make any customizations, please use subclass.
18+
*/
19+
public abstract class _MeaningfulPkBigintGenerated extends BaseDataObject {
20+
21+
private static final long serialVersionUID = 1L;
22+
23+
public static final String PK_PK_COLUMN = "PK";
24+
25+
public static final NumericProperty<BigInteger> PK = PropertyFactory.createNumeric("pk", BigInteger.class);
26+
public static final StringProperty<String> TEST_ATTR = PropertyFactory.createString("testAttr", String.class);
27+
28+
protected BigInteger pk;
29+
protected String testAttr;
30+
31+
32+
public void setPk(BigInteger pk) {
33+
beforePropertyWrite("pk", this.pk, pk);
34+
this.pk = pk;
35+
}
36+
37+
public BigInteger getPk() {
38+
beforePropertyRead("pk");
39+
return this.pk;
40+
}
41+
42+
public void setTestAttr(String testAttr) {
43+
beforePropertyWrite("testAttr", this.testAttr, testAttr);
44+
this.testAttr = testAttr;
45+
}
46+
47+
public String getTestAttr() {
48+
beforePropertyRead("testAttr");
49+
return this.testAttr;
50+
}
51+
52+
@Override
53+
public Object readPropertyDirectly(String propName) {
54+
if(propName == null) {
55+
throw new IllegalArgumentException();
56+
}
57+
58+
switch(propName) {
59+
case "pk":
60+
return this.pk;
61+
case "testAttr":
62+
return this.testAttr;
63+
default:
64+
return super.readPropertyDirectly(propName);
65+
}
66+
}
67+
68+
@Override
69+
public void writePropertyDirectly(String propName, Object val) {
70+
if(propName == null) {
71+
throw new IllegalArgumentException();
72+
}
73+
74+
switch (propName) {
75+
case "pk":
76+
this.pk = (BigInteger)val;
77+
break;
78+
case "testAttr":
79+
this.testAttr = (String)val;
80+
break;
81+
default:
82+
super.writePropertyDirectly(propName, val);
83+
}
84+
}
85+
86+
private void writeObject(ObjectOutputStream out) throws IOException {
87+
writeSerialized(out);
88+
}
89+
90+
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
91+
readSerialized(in);
92+
}
93+
94+
@Override
95+
protected void writeState(ObjectOutputStream out) throws IOException {
96+
super.writeState(out);
97+
out.writeObject(this.pk);
98+
out.writeObject(this.testAttr);
99+
}
100+
101+
@Override
102+
protected void readState(ObjectInputStream in) throws IOException, ClassNotFoundException {
103+
super.readState(in);
104+
this.pk = (BigInteger)in.readObject();
105+
this.testAttr = (String)in.readObject();
106+
}
107+
108+
}

cayenne/src/test/resources/meaningful-pk.map.xml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,10 @@
1010
<db-entity name="MEANINGFUL_PK_BIGINT">
1111
<db-attribute name="PK" type="INTEGER" isPrimaryKey="true" isMandatory="true"/>
1212
</db-entity>
13+
<db-entity name="MEANINGFUL_PK_BIGINT_GENERATED">
14+
<db-attribute name="PK" type="INTEGER" isPrimaryKey="true" isGenerated="true" isMandatory="true"/>
15+
<db-attribute name="TEST_ATTR" type="VARCHAR" length="255"/>
16+
</db-entity>
1317
<db-entity name="MEANINGFUL_PK_DEP">
1418
<db-attribute name="DESCR" type="VARCHAR" length="50"/>
1519
<db-attribute name="MASTER_PK" type="INTEGER"/>
@@ -46,6 +50,10 @@
4650
<obj-entity name="MeaningfulPkBigint" className="org.apache.cayenne.testdo.meaningful_pk.MeaningfulPkBigint" dbEntityName="MEANINGFUL_PK_BIGINT">
4751
<obj-attribute name="pk" type="java.math.BigInteger" db-attribute-path="PK"/>
4852
</obj-entity>
53+
<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">
54+
<obj-attribute name="pk" type="java.math.BigInteger" db-attribute-path="PK"/>
55+
<obj-attribute name="testAttr" type="java.lang.String" db-attribute-path="TEST_ATTR"/>
56+
</obj-entity>
4957
<obj-entity name="MeaningfulPkDep2" className="org.apache.cayenne.testdo.meaningful_pk.MeaningfulPkDep2" dbEntityName="MEANINGFUL_PK_DEP2">
5058
<obj-attribute name="descr" type="java.lang.String" db-attribute-path="DESCR"/>
5159
<obj-attribute name="pk" type="java.lang.String" db-attribute-path="PK"/>

0 commit comments

Comments
 (0)