improved JDO FactoryExpression handling

This commit is contained in:
Timo Westkämper 2012-11-29 11:59:38 +02:00
parent 02caf656d2
commit 253273ce04
8 changed files with 113 additions and 70 deletions

View File

@ -16,8 +16,8 @@
<packaging>jar</packaging>
<properties>
<dn.version>2.1.2</dn.version>
<dn.plugin.version>2.0.1</dn.plugin.version>
<dn.version>3.2.0-m1</dn.version>
<dn.plugin.version>3.2.0-m1</dn.plugin.version>
</properties>
<dependencies>
@ -70,12 +70,18 @@
</dependency>
<!-- test -->
<dependency>
<groupId>org.datanucleus</groupId>
<artifactId>datanucleus-core</artifactId>
<version>${dn.version}</version>
<scope>test</scope>
<dependency>
<groupId>org.datanucleus</groupId>
<artifactId>datanucleus-api-jdo</artifactId>
<version>${dn.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.datanucleus</groupId>
<artifactId>datanucleus-core</artifactId>
<version>${dn.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.datanucleus</groupId>
@ -161,11 +167,11 @@
</execution>
</executions>
<dependencies>
<dependency>
<groupId>javax.jdo</groupId>
<artifactId>jdo2-api</artifactId>
<version>2.3-ec</version>
<scope>runtime</scope>
<dependency>
<groupId>org.datanucleus</groupId>
<artifactId>datanucleus-api-jdo</artifactId>
<version>${dn.version}</version>
<scope>runtime</scope>
</dependency>
</dependencies>
</plugin>

View File

@ -1,6 +1,6 @@
/*
* Copyright 2011, Mysema Ltd
*
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
@ -29,6 +29,7 @@ import javax.jdo.Query;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.common.collect.Lists;
import com.mysema.commons.lang.CloseableIterator;
import com.mysema.commons.lang.IteratorAdapter;
import com.mysema.query.DefaultQueryMetadata;
@ -51,7 +52,7 @@ import com.mysema.query.types.QTuple;
*
* @param <Q>
*/
public abstract class AbstractJDOQLQuery<Q extends AbstractJDOQLQuery<Q>> extends ProjectableQuery<Q>{
public abstract class AbstractJDOQLQuery<Q extends AbstractJDOQLQuery<Q>> extends ProjectableQuery<Q> {
private static final Logger logger = LoggerFactory.getLogger(JDOQLQueryImpl.class);
@ -77,6 +78,9 @@ public abstract class AbstractJDOQLQuery<Q extends AbstractJDOQLQuery<Q>> extend
@Nullable
protected Integer maxFetchDepth;
@Nullable
private FactoryExpression<?> projection;
public AbstractJDOQLQuery(@Nullable PersistenceManager persistenceManager) {
this(persistenceManager, JDOQLTemplates.DEFAULT, new DefaultQueryMetadata(), false);
@ -111,7 +115,7 @@ public abstract class AbstractJDOQLQuery<Q extends AbstractJDOQLQuery<Q>> extend
Query query = createQuery(true);
query.setUnique(true);
reset();
Long rv = (Long) execute(query);
Long rv = (Long) execute(query, true);
if (rv != null) {
return rv.longValue();
} else {
@ -129,7 +133,7 @@ public abstract class AbstractJDOQLQuery<Q extends AbstractJDOQLQuery<Q>> extend
private Expression<?> getSource() {
return queryMixin.getMetadata().getJoins().get(0).getTarget();
}
private Query createQuery(boolean forCount) {
Expression<?> source = getSource();
@ -146,13 +150,9 @@ public abstract class AbstractJDOQLQuery<Q extends AbstractJDOQLQuery<Q>> extend
if (!forCount) {
List<? extends Expression<?>> projection = queryMixin.getMetadata().getProjection();
Class<?> exprType = projection.get(0).getClass();
if (exprType.equals(QTuple.class)) {
query.setResultClass(JDOTuple.class);
} else if (FactoryExpression.class.isAssignableFrom(exprType)) {
query.setResultClass(projection.get(0).getType());
if (projection.get(0) instanceof FactoryExpression) {
this.projection = (FactoryExpression<?>)projection.get(0);
}
if (!fetchGroups.isEmpty()) {
query.getFetchPlan().setGroups(fetchGroups);
}
@ -179,8 +179,18 @@ public abstract class AbstractJDOQLQuery<Q extends AbstractJDOQLQuery<Q>> extend
}
}
private Object project(FactoryExpression<?> expr, Object row) {
if (row == null) {
return null;
} else if (row.getClass().isArray()) {
return expr.newInstance((Object[])row);
} else {
return expr.newInstance(new Object[]{row});
}
}
@Nullable
private Object execute(Query query) {
private Object execute(Query query, boolean forCount) {
Object rv;
if (!orderedConstants.isEmpty()) {
rv = query.executeWithArray(orderedConstants.toArray());
@ -190,6 +200,18 @@ public abstract class AbstractJDOQLQuery<Q extends AbstractJDOQLQuery<Q>> extend
if (isDetach()) {
rv = detach(rv);
}
if (projection != null && !forCount) {
if (rv instanceof List) {
List<?> original = (List<?>)rv;
rv = Lists.newArrayList();
for (Object o : original) {
((List)rv).add(project(projection, o));
}
} else {
rv = project(projection, rv);
}
}
return rv;
}
@ -226,7 +248,7 @@ public abstract class AbstractJDOQLQuery<Q extends AbstractJDOQLQuery<Q>> extend
@SuppressWarnings("unchecked")
public <RT> List<RT> list(Expression<RT> expr) {
queryMixin.addToProjection(expr);
Object rv = execute(createQuery(false));
Object rv = execute(createQuery(false), false);
reset();
return rv instanceof List ? (List<RT>)rv : Collections.singletonList((RT)rv);
}
@ -237,12 +259,12 @@ public abstract class AbstractJDOQLQuery<Q extends AbstractJDOQLQuery<Q>> extend
Query countQuery = createQuery(true);
countQuery.setUnique(true);
countQuery.setResult("count(this)");
long total = (Long) execute(countQuery);
long total = (Long) execute(countQuery, true);
if (total > 0) {
QueryModifiers modifiers = queryMixin.getMetadata().getModifiers();
Query query = createQuery(false);
reset();
return new SearchResults<RT>((List<RT>) execute(query), modifiers, total);
return new SearchResults<RT>((List<RT>) execute(query, false), modifiers, total);
} else {
reset();
return SearchResults.emptyResults();
@ -271,6 +293,12 @@ public abstract class AbstractJDOQLQuery<Q extends AbstractJDOQLQuery<Q>> extend
}
}
@Override
@Nullable
public Tuple uniqueResult(Expression<?>[] args) {
return uniqueResult(new QTuple(args));
}
@Override
@SuppressWarnings("unchecked")
@Nullable
@ -278,14 +306,7 @@ public abstract class AbstractJDOQLQuery<Q extends AbstractJDOQLQuery<Q>> extend
queryMixin.addToProjection(expr);
return (RT)uniqueResult();
}
@Override
@Nullable
public Tuple uniqueResult(Expression<?>[] args) {
return uniqueResult(new QTuple(args));
}
@SuppressWarnings("unchecked")
@Nullable
private Object uniqueResult() {
if (getMetadata().getModifiers().getLimit() == null) {
@ -293,7 +314,7 @@ public abstract class AbstractJDOQLQuery<Q extends AbstractJDOQLQuery<Q>> extend
}
Query query = createQuery(false);
reset();
Object rv = execute(query);
Object rv = execute(query, false);
if (rv instanceof List) {
List<?> list = (List)rv;
if (!list.isEmpty()) {

View File

@ -27,6 +27,7 @@ import javax.jdo.Query;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.common.collect.Lists;
import com.mysema.commons.lang.CloseableIterator;
import com.mysema.commons.lang.IteratorAdapter;
import com.mysema.query.DefaultQueryMetadata;
@ -36,12 +37,11 @@ import com.mysema.query.QueryMetadata;
import com.mysema.query.QueryModifiers;
import com.mysema.query.SearchResults;
import com.mysema.query.Tuple;
import com.mysema.query.jdo.JDOTuple;
import com.mysema.query.sql.SQLCommonQuery;
import com.mysema.query.sql.SQLSerializer;
import com.mysema.query.sql.SQLTemplates;
import com.mysema.query.types.ConstructorExpression;
import com.mysema.query.types.Expression;
import com.mysema.query.types.FactoryExpression;
import com.mysema.query.types.QTuple;
/**
@ -70,6 +70,10 @@ public final class JDOSQLQuery extends AbstractSQLQuery<JDOSQLQuery> implements
private final PersistenceManager persistenceManager;
private List<Query> queries = new ArrayList<Query>(2);
@Nullable
private FactoryExpression<?> projection;
public JDOSQLQuery(@Nullable PersistenceManager persistenceManager, SQLTemplates templates) {
this(persistenceManager, templates, new DefaultQueryMetadata(), false);
@ -94,7 +98,7 @@ public final class JDOSQLQuery extends AbstractSQLQuery<JDOSQLQuery> implements
Query query = createQuery(true);
query.setUnique(true);
reset();
Long rv = (Long) execute(query);
Long rv = (Long) execute(query, true);
if (rv != null) {
return rv.longValue();
} else {
@ -121,11 +125,8 @@ public final class JDOSQLQuery extends AbstractSQLQuery<JDOSQLQuery> implements
if (!forCount) {
List<? extends Expression<?>> projection = queryMixin.getMetadata().getProjection();
Class<?> exprType = projection.get(0).getClass();
if (exprType.equals(QTuple.class)) {
query.setResultClass(JDOTuple.class);
} else if (ConstructorExpression.class.isAssignableFrom(exprType)) {
query.setResultClass(projection.get(0).getType());
if (projection.get(0) instanceof FactoryExpression) {
this.projection = (FactoryExpression<?>)projection.get(0);
}
} else {
query.setResultClass(Long.class);
@ -142,8 +143,18 @@ public final class JDOSQLQuery extends AbstractSQLQuery<JDOSQLQuery> implements
return persistenceManager.detachCopy(results);
}
}
private Object project(FactoryExpression<?> expr, Object row) {
if (row == null) {
return null;
} else if (row.getClass().isArray()) {
return expr.newInstance((Object[])row);
} else {
return expr.newInstance(new Object[]{row});
}
}
private Object execute(Query query) {
private Object execute(Query query, boolean forCount) {
Object rv;
if (!orderedConstants.isEmpty()) {
rv = query.executeWithArray(orderedConstants.toArray());
@ -153,6 +164,17 @@ public final class JDOSQLQuery extends AbstractSQLQuery<JDOSQLQuery> implements
if (isDetach()) {
rv = detach(rv);
}
if (projection != null && !forCount) {
if (rv instanceof List) {
List<?> original = (List<?>)rv;
rv = Lists.newArrayList();
for (Object o : original) {
((List)rv).add(project(projection, o));
}
} else {
rv = project(projection, rv);
}
}
return rv;
}
@ -180,7 +202,7 @@ public final class JDOSQLQuery extends AbstractSQLQuery<JDOSQLQuery> implements
@SuppressWarnings("unchecked")
public <RT> List<RT> list(Expression<RT> expr) {
queryMixin.addToProjection(expr);
Object rv = execute(createQuery(false));
Object rv = execute(createQuery(false), false);
reset();
return rv instanceof List ? (List<RT>)rv : Collections.singletonList((RT)rv);
}
@ -190,12 +212,12 @@ public final class JDOSQLQuery extends AbstractSQLQuery<JDOSQLQuery> implements
queryMixin.addToProjection(expr);
Query countQuery = createQuery(true);
countQuery.setUnique(true);
long total = (Long) execute(countQuery);
long total = (Long) execute(countQuery, true);
if (total > 0) {
QueryModifiers modifiers = queryMixin.getMetadata().getModifiers();
Query query = createQuery(false);
reset();
return new SearchResults<RT>((List<RT>) execute(query), modifiers, total);
return new SearchResults<RT>((List<RT>) execute(query, false), modifiers, total);
} else {
reset();
return SearchResults.emptyResults();
@ -239,7 +261,7 @@ public final class JDOSQLQuery extends AbstractSQLQuery<JDOSQLQuery> implements
}
Query query = createQuery(false);
reset();
Object rv = execute(query);
Object rv = execute(query, false);
if (rv instanceof List) {
List<?> list = (List<?>)rv;
if (!list.isEmpty()) {

View File

@ -178,12 +178,13 @@ public class JDOQLQueryStandardTest extends AbstractJDOTest {
}
@Test
@Ignore
public void ConstructorProjection(){
List<Projection> projections = query().from(store)
List<Projection> results = query().from(store)
.list(ConstructorExpression.create(Projection.class, store.name));
assertFalse(projections.isEmpty());
for (Projection projection : projections){
assertNotNull(projection);
assertFalse(results.isEmpty());
for (Projection result : results){
assertNotNull(result);
}
}

View File

@ -55,7 +55,7 @@ public class FetchPlanTest extends AbstractJDOTest{
.addFetchGroup("myfetchgroup2")
.setMaxFetchDepth(2)
.list(product);
query.close();
// query.close();
Field queriesField = AbstractJDOQLQuery.class.getDeclaredField("queries");
queriesField.setAccessible(true);

View File

@ -1,10 +1,4 @@
javax.jdo.PersistenceManagerFactoryClass=org.datanucleus.jdo.JDOPersistenceManagerFactory
#javax.jdo.option.ConnectionDriverName=org.hsqldb.jdbcDriver
#javax.jdo.option.ConnectionURL=jdbc:hsqldb:mem:nucleus1
#javax.jdo.option.ConnectionUserName=sa
#javax.jdo.option.ConnectionPassword=
#javax.jdo.option.Mapping=hsql
javax.jdo.PersistenceManagerFactoryClass=org.datanucleus.api.jdo.JDOPersistenceManagerFactory
javax.jdo.option.ConnectionDriverName=org.h2.Driver
javax.jdo.option.ConnectionURL=jdbc:h2:target/test

View File

@ -73,8 +73,9 @@ public abstract class AbstractJPAQuery<Q extends AbstractJPAQuery<Q>> extends JP
@Nullable
protected FlushModeType flushMode;
protected boolean factoryExpressionUsed = false;
@Nullable
protected FactoryExpression<?> projection;
public AbstractJPAQuery(EntityManager em) {
this(em, JPAProvider.getTemplates(em), new DefaultQueryMetadata());
@ -185,7 +186,7 @@ public abstract class AbstractJPAQuery<Q extends AbstractJPAQuery<Q>> extends JP
}
} else {
factoryExpressionUsed = true;
this.projection = (FactoryExpression<?>)projection.get(0);
if (wrapped != null) {
getMetadata().clearProjection();
getMetadata().addProjection(wrapped);
@ -204,16 +205,15 @@ public abstract class AbstractJPAQuery<Q extends AbstractJPAQuery<Q>> extends JP
*/
private List<?> getResultList(Query query) {
// TODO : use lazy list here?
if (factoryExpressionUsed) {
if (projection != null) {
List<?> results = query.getResultList();
List<Object> rv = new ArrayList<Object>(results.size());
FactoryExpression<?> expr = (FactoryExpression<?>)getMetadata().getProjection().get(0);
for (Object o : results) {
if (o != null) {
if (!o.getClass().isArray()) {
o = new Object[]{o};
}
rv.add(expr.newInstance((Object[])o));
rv.add(projection.newInstance((Object[])o));
} else {
rv.add(null);
}
@ -232,14 +232,13 @@ public abstract class AbstractJPAQuery<Q extends AbstractJPAQuery<Q>> extends JP
*/
@Nullable
private Object getSingleResult(Query query) {
if (factoryExpressionUsed) {
if (projection != null) {
Object result = query.getSingleResult();
FactoryExpression<?> expr = (FactoryExpression<?>)getMetadata().getProjection().get(0);
if (result != null) {
if (!result.getClass().isArray()) {
result = new Object[]{result};
}
return expr.newInstance((Object[])result);
return projection.newInstance((Object[])result);
} else {
return null;
}

View File

@ -83,7 +83,7 @@ public final class JPAQuery extends AbstractJPAQuery<JPAQuery> implements JPQLQu
*/
public JPAQuery clone(EntityManager entityManager) {
JPAQuery q = new JPAQuery(entityManager, JPAProvider.getTemplates(entityManager), getMetadata().clone());
q.factoryExpressionUsed = factoryExpressionUsed;
q.projection = projection;
q.flushMode = flushMode;
q.hints.putAll(hints);
q.lockMode = lockMode;