Skip to content

Commit b052080

Browse files
committed
CAY-2900 Meaningful generated PKs could lead to the ClassCastException
1 parent 336bcf1 commit b052080

7 files changed

Lines changed: 180 additions & 10 deletions

File tree

RELEASE-NOTES.txt

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

2728
----------------------------------
2829
Release: 4.2.2

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

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@
3131
import org.apache.cayenne.dba.TypesMapping;
3232
import org.apache.cayenne.log.JdbcEventLogger;
3333
import org.apache.cayenne.map.DbAttribute;
34+
import org.apache.cayenne.map.ObjAttribute;
35+
import org.apache.cayenne.map.ObjEntity;
3436
import org.apache.cayenne.query.BatchQuery;
3537
import org.apache.cayenne.query.BatchQueryRow;
3638
import org.apache.cayenne.query.InsertBatchQuery;
@@ -261,14 +263,10 @@ protected void processGeneratedKeys(Statement statement, OperationObserver obser
261263

262264
ResultSet keysRS = statement.getGeneratedKeys();
263265

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

273271
RowDescriptorBuilder builder = new RowDescriptorBuilder();
274272

@@ -283,7 +281,7 @@ protected void processGeneratedKeys(Statement statement, OperationObserver obser
283281
// use column name from result set, but type and Java class from DB attribute
284282
columns[0] = new ColumnDescriptor(keysRS.getMetaData(), 1);
285283
columns[0].setJdbcType(key.getType());
286-
columns[0].setJavaClass(TypesMapping.getJavaBySqlType(key));
284+
columns[0].setJavaClass(typeForGeneratedPK(key));
287285
builder.setColumns(columns);
288286
} else {
289287
builder.setResultSet(keysRS);
@@ -302,4 +300,16 @@ protected void processGeneratedKeys(Statement statement, OperationObserver obser
302300
}
303301
observer.nextGeneratedRows(query, iterator, objectIds);
304302
}
303+
304+
private String typeForGeneratedPK(DbAttribute key) {
305+
String entityName = getQuery().getRows().get(0).getObjectId().getEntityName();
306+
ObjEntity objEntity = dataNode.getEntityResolver().getObjEntity(entityName);
307+
if(objEntity != null) {
308+
ObjAttribute attributeForDbAttribute = objEntity.getAttributeForDbAttribute(key);
309+
if(attributeForDbAttribute != null) {
310+
return attributeForDbAttribute.getType();
311+
}
312+
}
313+
return TypesMapping.getJavaBySqlType(key);
314+
}
305315
}

cayenne-server/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-server/src/test/java/org/apache/cayenne/access/DataContextEntityWithMeaningfulPKIT.java

Lines changed: 17 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.server.CayenneProjects;
@@ -330,4 +331,20 @@ public void testPaginatedQueryBigInteger() {
330331
}
331332
}
332333

334+
@Test
335+
public void testGeneratedBigIntegerPK() {
336+
MeaningfulPkBigintGenerated pkObj1 = context.newObject(MeaningfulPkBigintGenerated.class);
337+
MeaningfulPkBigintGenerated pkObj2 = context.newObject(MeaningfulPkBigintGenerated.class);
338+
MeaningfulPkBigintGenerated pkObj3 = context.newObject(MeaningfulPkBigintGenerated.class);
339+
340+
context.commitChanges();
341+
342+
assertNotNull(pkObj1.getPk());
343+
assertNotNull(pkObj2.getPk());
344+
assertNotNull(pkObj3.getPk());
345+
assertTrue(pkObj1.getPk().compareTo(BigInteger.ZERO) > 0);
346+
assertTrue(pkObj2.getPk().compareTo(BigInteger.ZERO) > 0);
347+
assertTrue(pkObj3.getPk().compareTo(BigInteger.ZERO) > 0);
348+
}
349+
333350
}
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-server/src/test/resources/meaningful-pk.map.xml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,10 @@
1212
<db-entity name="MEANINGFUL_PK_BIGINT">
1313
<db-attribute name="PK" type="INTEGER" isPrimaryKey="true" isMandatory="true"/>
1414
</db-entity>
15+
<db-entity name="MEANINGFUL_PK_BIGINT_GENERATED">
16+
<db-attribute name="PK" type="INTEGER" isPrimaryKey="true" isGenerated="true" isMandatory="true"/>
17+
<db-attribute name="TEST_ATTR" type="VARCHAR" length="255"/>
18+
</db-entity>
1519
<db-entity name="MEANINGFUL_PK_DEP">
1620
<db-attribute name="DESCR" type="VARCHAR" length="50"/>
1721
<db-attribute name="MASTER_PK" type="INTEGER"/>
@@ -48,6 +52,10 @@
4852
<obj-entity name="MeaningfulPkBigint" className="org.apache.cayenne.testdo.meaningful_pk.MeaningfulPkBigint" dbEntityName="MEANINGFUL_PK_BIGINT">
4953
<obj-attribute name="pk" type="java.math.BigInteger" db-attribute-path="PK"/>
5054
</obj-entity>
55+
<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">
56+
<obj-attribute name="pk" type="java.math.BigInteger" db-attribute-path="PK"/>
57+
<obj-attribute name="testAttr" type="java.lang.String" db-attribute-path="TEST_ATTR"/>
58+
</obj-entity>
5159
<obj-entity name="MeaningfulPkDep2" className="org.apache.cayenne.testdo.meaningful_pk.MeaningfulPkDep2" clientClassName="org.apache.cayenne.testdo.meaningful_pk.MeaningfulPkDep2" dbEntityName="MEANINGFUL_PK_DEP2">
5260
<obj-attribute name="descr" type="java.lang.String" db-attribute-path="DESCR"/>
5361
<obj-attribute name="pk" type="java.lang.String" db-attribute-path="PK"/>

0 commit comments

Comments
 (0)