You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cayenne.apache.org by nt...@apache.org on 2017/05/26 11:55:02 UTC
cayenne git commit: Fix for broken ColumnSelect with custom types
columns
Repository: cayenne
Updated Branches:
refs/heads/master f3f01263f -> eac1f3107
Fix for broken ColumnSelect with custom types columns
Project: http://git-wip-us.apache.org/repos/asf/cayenne/repo
Commit: http://git-wip-us.apache.org/repos/asf/cayenne/commit/eac1f310
Tree: http://git-wip-us.apache.org/repos/asf/cayenne/tree/eac1f310
Diff: http://git-wip-us.apache.org/repos/asf/cayenne/diff/eac1f310
Branch: refs/heads/master
Commit: eac1f31073045fec6eafef3f3fd6cb05f0201994
Parents: f3f0126
Author: Nikita Timofeev <st...@gmail.com>
Authored: Fri May 26 14:50:40 2017 +0300
Committer: Nikita Timofeev <st...@gmail.com>
Committed: Fri May 26 14:50:40 2017 +0300
----------------------------------------------------------------------
.../org/apache/cayenne/java8/Java8TimeIT.java | 21 +++++
.../cayenne/access/jdbc/ColumnDescriptor.java | 9 +-
.../select/DefaultSelectTranslator.java | 89 +++++++++++++-------
.../java/org/apache/cayenne/exp/Property.java | 1 +
.../apache/cayenne/access/NumericTypesIT.java | 51 +++++++----
.../java/org/apache/cayenne/access/UUIDIT.java | 23 ++++-
.../src/test/resources/testmap.map.xml | 8 +-
7 files changed, 151 insertions(+), 51 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/cayenne/blob/eac1f310/cayenne-java8/src/test/java/org/apache/cayenne/java8/Java8TimeIT.java
----------------------------------------------------------------------
diff --git a/cayenne-java8/src/test/java/org/apache/cayenne/java8/Java8TimeIT.java b/cayenne-java8/src/test/java/org/apache/cayenne/java8/Java8TimeIT.java
index fb1a794..8524aa1 100644
--- a/cayenne-java8/src/test/java/org/apache/cayenne/java8/Java8TimeIT.java
+++ b/cayenne-java8/src/test/java/org/apache/cayenne/java8/Java8TimeIT.java
@@ -114,4 +114,25 @@ public class Java8TimeIT extends RuntimeBase {
}
+ @Test
+ public void columnSelectWithJava8Type() {
+ ObjectContext context = runtime.newContext();
+ LocalDateTime localDateTime = LocalDateTime.now();
+
+ LocalDateTimeTestEntity localDateTimeTestEntity = context.newObject(LocalDateTimeTestEntity.class);
+ localDateTimeTestEntity.setTimestamp(localDateTime);
+
+ context.commitChanges();
+
+ LocalDateTime value = ObjectSelect.query(LocalDateTimeTestEntity.class)
+ .column(LocalDateTimeTestEntity.TIMESTAMP)
+ .selectOne(context);
+ assertEquals(localDateTime, value);
+
+ LocalDateTime value2 = ObjectSelect.query(LocalDateTimeTestEntity.class)
+ .min(LocalDateTimeTestEntity.TIMESTAMP)
+ .selectOne(context);
+ assertEquals(localDateTime, value2);
+ }
+
}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/eac1f310/cayenne-server/src/main/java/org/apache/cayenne/access/jdbc/ColumnDescriptor.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/access/jdbc/ColumnDescriptor.java b/cayenne-server/src/main/java/org/apache/cayenne/access/jdbc/ColumnDescriptor.java
index 7cbaab4..632f5e3 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/access/jdbc/ColumnDescriptor.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/access/jdbc/ColumnDescriptor.java
@@ -68,10 +68,17 @@ public class ColumnDescriptor {
* @since 3.1
*/
public ColumnDescriptor(String columnName, int jdbcType) {
+ this(columnName, jdbcType, TypesMapping.getJavaBySqlType(jdbcType));
+ }
+
+ /**
+ * @since 4.0
+ */
+ public ColumnDescriptor(String columnName, int jdbcType, String javaClass) {
this.name = columnName;
this.dataRowKey = columnName;
this.jdbcType = jdbcType;
- this.javaClass = TypesMapping.getJavaBySqlType(jdbcType);
+ this.javaClass = javaClass;
}
/**
http://git-wip-us.apache.org/repos/asf/cayenne/blob/eac1f310/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/DefaultSelectTranslator.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/DefaultSelectTranslator.java b/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/DefaultSelectTranslator.java
index ea0ea53..59bdc07 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/DefaultSelectTranslator.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/DefaultSelectTranslator.java
@@ -413,24 +413,19 @@ public class DefaultSelectTranslator extends QueryAssembler implements SelectTra
for(Property<?> property : query.getColumns()) {
int expressionType = property.getExpression().getType();
- boolean objectProperty = expressionType == Expression.FULL_OBJECT;
- // evaluate ObjPath with Persistent type as toOne relations and use it as full object
- if(Persistent.class.isAssignableFrom(property.getType())) {
- if(expressionType == Expression.OBJ_PATH) {
- objectProperty = true;
- } else {
- // should we warn or throw an error?
- }
- }
// forbid direct selection of toMany relationships columns
- if((expressionType == Expression.OBJ_PATH || expressionType == Expression.DB_PATH) &&
- (Collection.class.isAssignableFrom(property.getType()) ||
- Map.class.isAssignableFrom(property.getType()))) {
+ if((expressionType == Expression.OBJ_PATH || expressionType == Expression.DB_PATH)
+ && (Collection.class.isAssignableFrom(property.getType())
+ || Map.class.isAssignableFrom(property.getType()))) {
throw new CayenneRuntimeException("Can't directly select toMany relationship columns. " +
"Either select it with aggregate functions like count() or with flat() function to select full related objects.");
}
+ // evaluate ObjPath with Persistent type as toOne relations and use it as full object
+ boolean objectProperty = expressionType == Expression.FULL_OBJECT
+ || (expressionType == Expression.OBJ_PATH && Persistent.class.isAssignableFrom(property.getType()));
+
// Qualifier Translator in case of Object Columns have side effect -
// it will create required joins, that we catch with listener above.
// And we force created join alias for all columns of Object we select.
@@ -438,8 +433,8 @@ public class DefaultSelectTranslator extends QueryAssembler implements SelectTra
qualifierTranslator.setForceJoinForRelations(objectProperty);
StringBuilder builder = qualifierTranslator.appendPart(new StringBuilder());
- // If we want full object, use appendQueryColumns method, to fully process class descriptor
if(objectProperty) {
+ // If we want full object, use appendQueryColumns method, to fully process class descriptor
List<ColumnDescriptor> classColumns = new ArrayList<>();
ObjEntity entity = entityResolver.getObjEntity(property.getType());
if(getQueryMetadata().getPageSize() > 0) {
@@ -452,26 +447,26 @@ public class DefaultSelectTranslator extends QueryAssembler implements SelectTra
columns.add(descriptor);
groupByColumns.put(descriptor, Collections.<DbAttributeBinding>emptyList());
}
- continue;
- }
-
- int type = TypesMapping.getSqlTypeByJava(property.getType());
+ } else {
+ // This property will go as scalar value
+ String alias = property.getAlias();
+ if (alias != null) {
+ builder.append(" AS ").append(alias);
+ }
- String alias = property.getAlias();
- if(alias != null) {
- builder.append(" AS ").append(alias);
- }
- ColumnDescriptor descriptor = new ColumnDescriptor(builder.toString(), type);
- descriptor.setDataRowKey(alias);
- descriptor.setIsExpression(true);
- columns.add(descriptor);
+ int type = getJdbcTypeForProperty(property);
+ ColumnDescriptor descriptor = new ColumnDescriptor(builder.toString(), type, property.getType().getName());
+ descriptor.setDataRowKey(alias);
+ descriptor.setIsExpression(true);
+ columns.add(descriptor);
- if(isAggregate(property)) {
- haveAggregate = true;
- } else {
- groupByColumns.put(descriptor, bindingListener.getBindings());
+ if (isAggregate(property)) {
+ haveAggregate = true;
+ } else {
+ groupByColumns.put(descriptor, bindingListener.getBindings());
+ }
+ bindingListener.reset();
}
- bindingListener.reset();
}
setAddBindingListener(null);
@@ -481,6 +476,40 @@ public class DefaultSelectTranslator extends QueryAssembler implements SelectTra
return columns;
}
+ private int getJdbcTypeForProperty(Property<?> property) {
+ int expressionType = property.getExpression().getType();
+ if(expressionType == Expression.OBJ_PATH) {
+ // Scan obj path, stop as soon as DbAttribute found
+ for (PathComponent<ObjAttribute, ObjRelationship> component :
+ getQueryMetadata().getObjEntity().resolvePath(property.getExpression(), getPathAliases())) {
+ if(component.getAttribute() != null) {
+ Iterator<CayenneMapEntry> dbPathIterator = component.getAttribute().getDbPathIterator();
+ while (dbPathIterator.hasNext()) {
+ Object pathPart = dbPathIterator.next();
+ if (pathPart instanceof DbAttribute) {
+ return ((DbAttribute) pathPart).getType();
+ }
+ }
+ }
+ }
+ } else if(expressionType == Expression.DB_PATH) {
+ // Scan db path, stop as soon as DbAttribute found
+ for (PathComponent<DbAttribute, DbRelationship> component :
+ getQueryMetadata().getDbEntity().resolvePath(property.getExpression(), getPathAliases())) {
+ if(component.getAttribute() != null) {
+ return component.getAttribute().getType();
+ }
+ }
+ }
+ // NOTE: If no attribute found or expression have some other type
+ // return JDBC type based on Java type of the property.
+ // This can lead to incorrect behavior in case we deal with some custom type
+ // backed by ExtendedType with logic based on correct JDBC type provided (luckily not very common case).
+ // In general we can't map any meaningful type, as we don't know outcome of the expression in the property.
+ // If this ever become a problem correct type can be provided at the query call time, using meta data.
+ return TypesMapping.getSqlTypeByJava(property.getType());
+ }
+
private boolean isAggregate(Property<?> property) {
final boolean[] isAggregate = new boolean[1];
Expression exp = property.getExpression();
http://git-wip-us.apache.org/repos/asf/cayenne/blob/eac1f310/cayenne-server/src/main/java/org/apache/cayenne/exp/Property.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/exp/Property.java b/cayenne-server/src/main/java/org/apache/cayenne/exp/Property.java
index 9ceaf6f..437930d 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/exp/Property.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/exp/Property.java
@@ -161,6 +161,7 @@ public class Property<E> {
}
/**
+ * This method returns fresh copy of the expression for each call.
* @return expression that represents this Property
*/
public Expression getExpression() {
http://git-wip-us.apache.org/repos/asf/cayenne/blob/eac1f310/cayenne-server/src/test/java/org/apache/cayenne/access/NumericTypesIT.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/test/java/org/apache/cayenne/access/NumericTypesIT.java b/cayenne-server/src/test/java/org/apache/cayenne/access/NumericTypesIT.java
index 6ad368d..29107b1 100644
--- a/cayenne-server/src/test/java/org/apache/cayenne/access/NumericTypesIT.java
+++ b/cayenne-server/src/test/java/org/apache/cayenne/access/NumericTypesIT.java
@@ -24,6 +24,8 @@ import org.apache.cayenne.configuration.server.ServerRuntime;
import org.apache.cayenne.di.Inject;
import org.apache.cayenne.exp.Expression;
import org.apache.cayenne.exp.ExpressionFactory;
+import org.apache.cayenne.exp.Property;
+import org.apache.cayenne.query.ObjectSelect;
import org.apache.cayenne.query.SelectQuery;
import org.apache.cayenne.test.jdbc.DBHelper;
import org.apache.cayenne.test.jdbc.TableHelper;
@@ -95,11 +97,11 @@ public class NumericTypesIT extends ServerCase {
LongEntity test = context.newObject(LongEntity.class);
- Long i = new Long(Integer.MAX_VALUE + 10l);
+ Long i = Integer.MAX_VALUE + 10L;
test.setLongField(i);
context.commitChanges();
- SelectQuery q = new SelectQuery(LongEntity.class);
+ SelectQuery<LongEntity> q = new SelectQuery<>(LongEntity.class);
LongEntity testRead = (LongEntity) context.performQuery(q).get(0);
assertNotNull(testRead.getLongField());
assertEquals(i, testRead.getLongField());
@@ -117,7 +119,7 @@ public class NumericTypesIT extends ServerCase {
test.setBigIntegerField(i);
context.commitChanges();
- SelectQuery q = new SelectQuery(BigIntegerEntity.class);
+ SelectQuery<BigIntegerEntity> q = new SelectQuery<>(BigIntegerEntity.class);
BigIntegerEntity testRead = (BigIntegerEntity) context.performQuery(q).get(0);
assertNotNull(testRead.getBigIntegerField());
assertEquals(i, testRead.getBigIntegerField());
@@ -135,7 +137,7 @@ public class NumericTypesIT extends ServerCase {
test.setBigDecimalField(i);
context.commitChanges();
- SelectQuery q = new SelectQuery(BigDecimalEntity.class);
+ SelectQuery<BigDecimalEntity> q = new SelectQuery<>(BigDecimalEntity.class);
BigDecimalEntity testRead = (BigDecimalEntity) context.performQuery(q).get(0);
assertNotNull(testRead.getBigDecimalField());
assertEquals(i, testRead.getBigDecimalField());
@@ -150,7 +152,7 @@ public class NumericTypesIT extends ServerCase {
// test
Expression qual = ExpressionFactory.matchExp("smallintCol", new Short("9999"));
- List<?> objects = context.performQuery(new SelectQuery(
+ List<?> objects = context.performQuery(new SelectQuery<>(
SmallintTestEntity.class,
qual));
assertEquals(1, objects.size());
@@ -172,8 +174,8 @@ public class NumericTypesIT extends ServerCase {
createTinyintDataSet();
// test
- Expression qual = ExpressionFactory.matchExp("tinyintCol", new Byte((byte) 81));
- List<?> objects = context.performQuery(new SelectQuery(
+ Expression qual = ExpressionFactory.matchExp("tinyintCol", (byte) 81);
+ List<?> objects = context.performQuery(new SelectQuery<>(
TinyintTestEntity.class,
qual));
assertEquals(1, objects.size());
@@ -186,7 +188,7 @@ public class NumericTypesIT extends ServerCase {
public void testTinyintInInsert() throws Exception {
TinyintTestEntity object = (TinyintTestEntity) (context)
.newObject("TinyintTestEntity");
- object.setTinyintCol(new Byte((byte) 1));
+ object.setTinyintCol((byte) 1);
context.commitChanges();
}
@@ -202,7 +204,7 @@ public class NumericTypesIT extends ServerCase {
// fetch true...
Expression trueQ = ExpressionFactory.matchExp("bitColumn", Boolean.TRUE);
- List<?> trueResult = context1.performQuery(new SelectQuery(
+ List<?> trueResult = context1.performQuery(new SelectQuery<>(
BitTestEntity.class,
trueQ));
assertEquals(1, trueResult.size());
@@ -216,7 +218,7 @@ public class NumericTypesIT extends ServerCase {
// fetch false
Expression falseQ = ExpressionFactory.matchExp("bitColumn", Boolean.FALSE);
- List<?> falseResult = context1.performQuery(new SelectQuery(
+ List<?> falseResult = context1.performQuery(new SelectQuery<>(
BitTestEntity.class,
falseQ));
assertEquals(1, falseResult.size());
@@ -245,7 +247,7 @@ public class NumericTypesIT extends ServerCase {
// fetch true...
Expression trueQ = ExpressionFactory.matchExp("booleanColumn", Boolean.TRUE);
- List<?> trueResult = context1.performQuery(new SelectQuery(
+ List<?> trueResult = context1.performQuery(new SelectQuery<>(
BooleanTestEntity.class,
trueQ));
assertEquals(1, trueResult.size());
@@ -259,7 +261,7 @@ public class NumericTypesIT extends ServerCase {
// fetch false
Expression falseQ = ExpressionFactory.matchExp("booleanColumn", Boolean.FALSE);
- List<?> falseResult = context1.performQuery(new SelectQuery(
+ List<?> falseResult = context1.performQuery(new SelectQuery<>(
BooleanTestEntity.class,
falseQ));
assertEquals(1, falseResult.size());
@@ -299,11 +301,30 @@ public class NumericTypesIT extends ServerCase {
DecimalPKTest1 object = context.newObject(DecimalPKTest1.class);
object.setName("o2");
- object.setDecimalPK(new Double(1.25));
+ object.setDecimalPK(1.25);
context.commitChanges();
- ObjectId syntheticId = new ObjectId("DecimalPKTest1", "DECIMAL_PK", new Double(
- 1.25));
+ ObjectId syntheticId = new ObjectId("DecimalPKTest1", "DECIMAL_PK", 1.25);
assertSame(object, context.getGraphManager().getNode(syntheticId));
}
+
+ @Test
+ public void testBigIntegerColumnSelect() {
+ BigIntegerEntity test = context.newObject(BigIntegerEntity.class);
+ BigInteger i = new BigInteger("1234567890");
+ test.setBigIntegerField(i);
+ context.commitChanges();
+
+ BigInteger readValue = ObjectSelect.query(BigIntegerEntity.class)
+ .column(BigIntegerEntity.BIG_INTEGER_FIELD).selectOne(context);
+
+ assertEquals(i, readValue);
+
+ Property<BigInteger> calculated =
+ Property.create(ExpressionFactory.exp("bigIntegerField + 1"), BigInteger.class);
+
+ BigInteger readValue2 = ObjectSelect.query(BigIntegerEntity.class)
+ .column(calculated).selectOne(context);
+ assertEquals(i.add(BigInteger.ONE), readValue2);
+ }
}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/eac1f310/cayenne-server/src/test/java/org/apache/cayenne/access/UUIDIT.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/test/java/org/apache/cayenne/access/UUIDIT.java b/cayenne-server/src/test/java/org/apache/cayenne/access/UUIDIT.java
index 1b0dcba..37e3785 100644
--- a/cayenne-server/src/test/java/org/apache/cayenne/access/UUIDIT.java
+++ b/cayenne-server/src/test/java/org/apache/cayenne/access/UUIDIT.java
@@ -21,6 +21,9 @@ package org.apache.cayenne.access;
import org.apache.cayenne.Cayenne;
import org.apache.cayenne.ObjectContext;
import org.apache.cayenne.di.Inject;
+import org.apache.cayenne.exp.ExpressionFactory;
+import org.apache.cayenne.exp.Property;
+import org.apache.cayenne.query.ObjectSelect;
import org.apache.cayenne.query.SelectQuery;
import org.apache.cayenne.test.jdbc.DBHelper;
import org.apache.cayenne.test.jdbc.TableHelper;
@@ -62,7 +65,7 @@ public class UUIDIT extends ServerCase {
test.setUuid(id);
context.commitChanges();
- SelectQuery q = new SelectQuery(UuidTestEntity.class);
+ SelectQuery<UuidTestEntity> q = new SelectQuery<>(UuidTestEntity.class);
UuidTestEntity testRead = (UuidTestEntity) context.performQuery(q).get(0);
assertNotNull(testRead.getUuid());
assertEquals(id, testRead.getUuid());
@@ -96,4 +99,22 @@ public class UUIDIT extends ServerCase {
assertEquals(id, o1.getId());
assertEquals(id, o1.getObjectId().getIdSnapshot().get("ID"));
}
+
+ @Test
+ public void testUUIDColumnSelect() throws Exception {
+ UuidTestEntity test = context.newObject(UuidTestEntity.class);
+ UUID id = UUID.randomUUID();
+ test.setUuid(id);
+ context.commitChanges();
+
+ UUID readValue = ObjectSelect.query(UuidTestEntity.class)
+ .column(UuidTestEntity.UUID).selectOne(context);
+
+ assertEquals(id, readValue);
+
+ UUID readValue2 = ObjectSelect.query(UuidTestEntity.class)
+ .column(Property.create(ExpressionFactory.dbPathExp("UUID"), UUID.class)).selectOne(context);
+
+ assertEquals(id, readValue2);
+ }
}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/eac1f310/cayenne-server/src/test/resources/testmap.map.xml
----------------------------------------------------------------------
diff --git a/cayenne-server/src/test/resources/testmap.map.xml b/cayenne-server/src/test/resources/testmap.map.xml
index 31c0f70..8e082d9 100644
--- a/cayenne-server/src/test/resources/testmap.map.xml
+++ b/cayenne-server/src/test/resources/testmap.map.xml
@@ -18,7 +18,7 @@
</procedure>
<procedure name="cayenne_tst_upd_proc">
<procedure-parameter name="paintingPrice" type="INTEGER" direction="in"/>
- </procedure>Ï
+ </procedure>
<procedure name="cayenne_tst_upd_proc2">
</procedure>
<db-entity name="ARTGROUP">
@@ -244,8 +244,8 @@
<obj-relationship name="paintingArray" source="ROArtist" target="Painting" deleteRule="Deny" db-relationship-path="paintingArray"/>
<obj-relationship name="toArtist" source="ROPainting" target="Artist" deleteRule="Nullify" db-relationship-path="toArtist"/>
<query name="EjbqlQueryTest" type="EJBQLQuery">
- <property name="cayenne.GenericSelectQuery.fetchingDataRows" value="true"/>
<property name="cayenne.GenericSelectQuery.cacheStrategy" value="SHARED_CACHE"/>
+ <property name="cayenne.GenericSelectQuery.fetchingDataRows" value="true"/>
<ejbql><![CDATA[select a from Artist a]]></ejbql>
</query>
<query name="NonSelectingQuery" type="SQLTemplate" root="data-map" root-name="testmap">
@@ -290,13 +290,13 @@ VALUES (#bind($id), #bind($title), #bind($price))]]></sql>
<property name="cayenne.GenericSelectQuery.cacheStrategy" value="SHARED_CACHE"/>
</query>
<query name="SelectTestLower" type="SQLTemplate" root="data-map" root-name="testmap">
- <property name="cayenne.GenericSelectQuery.fetchingDataRows" value="true"/>
<property name="cayenne.SQLTemplate.columnNameCapitalization" value="LOWER"/>
+ <property name="cayenne.GenericSelectQuery.fetchingDataRows" value="true"/>
<sql><![CDATA[select * from ARTIST]]></sql>
</query>
<query name="SelectTestUpper" type="SQLTemplate" root="data-map" root-name="testmap">
- <property name="cayenne.GenericSelectQuery.fetchingDataRows" value="true"/>
<property name="cayenne.SQLTemplate.columnNameCapitalization" value="UPPER"/>
+ <property name="cayenne.GenericSelectQuery.fetchingDataRows" value="true"/>
<sql><![CDATA[select * from ARTIST]]></sql>
</query>
</data-map>