From 767561fa228a8009cd8efe51a3b39656d1bc838e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20Westk=C3=A4mper?= Date: Fri, 26 Nov 2010 14:27:29 +0000 Subject: [PATCH] #676458 : added support for Projectable.exists() and notExists() --- .../query/collections/AbstractColQuery.java | 19 ++++-- .../query/collections/DefaultQueryEngine.java | 21 ++++++- .../mysema/query/collections/QueryEngine.java | 7 +++ .../mysema/query/collections/ExistsTest.java | 20 +++++++ .../java/com/mysema/query/Projectable.java | 10 ++++ .../query/support/ProjectableAdapter.java | 11 ++++ .../query/support/ProjectableQuery.java | 5 ++ .../query/support/DummyProjectable.java | 10 ++++ .../mysema/query/jdo/AbstractJDOQLQuery.java | 42 ++++++++----- .../query/jdo/sql/AbstractSQLQuery.java | 10 +++- .../mysema/query/JDOQLQueryStandardTest.java | 26 +++++--- .../mysema/query/jpa/AbstractSQLQuery.java | 10 +++- .../com/mysema/query/jpa/JPQLQueryBase.java | 9 +++ .../com/mysema/query/jpa/sql/JPASQLQuery.java | 5 +- .../mysema/query/AbstractStandardTest.java | 60 +++++++++++-------- .../mysema/query/sql/AbstractSQLQuery.java | 49 ++++++++------- .../java/com/mysema/query/SelectBaseTest.java | 58 +++++++++++------- 17 files changed, 267 insertions(+), 105 deletions(-) create mode 100644 querydsl-collections/src/test/java/com/mysema/query/collections/ExistsTest.java diff --git a/querydsl-collections/src/main/java/com/mysema/query/collections/AbstractColQuery.java b/querydsl-collections/src/main/java/com/mysema/query/collections/AbstractColQuery.java index d8250b886..18178577d 100644 --- a/querydsl-collections/src/main/java/com/mysema/query/collections/AbstractColQuery.java +++ b/querydsl-collections/src/main/java/com/mysema/query/collections/AbstractColQuery.java @@ -63,14 +63,23 @@ public abstract class AbstractColQuery> extends P } } - @SuppressWarnings("unchecked") - private Expression createAlias(CollectionExpression target, Path alias){ - return OperationImpl.create((Class)alias.getType(), Ops.ALIAS, target, alias); + @Override + public boolean exists(){ + try { + return queryEngine.exists(getMetadata(), iterables); + } catch (Exception e) { + throw new QueryException(e.getMessage(), e); + }finally{ + reset(); + } + } + + private Expression createAlias(CollectionExpression target, Path alias){ + return OperationImpl.create(alias.getType(), Ops.ALIAS, target, alias); } - @SuppressWarnings("unchecked") private Expression createAlias(MapExpression target, Path alias){ - return OperationImpl.create((Class)alias.getType(), Ops.ALIAS, target, alias); + return OperationImpl.create(alias.getType(), Ops.ALIAS, target, alias); } @SuppressWarnings("unchecked") diff --git a/querydsl-collections/src/main/java/com/mysema/query/collections/DefaultQueryEngine.java b/querydsl-collections/src/main/java/com/mysema/query/collections/DefaultQueryEngine.java index 924136751..91f1b0e30 100644 --- a/querydsl-collections/src/main/java/com/mysema/query/collections/DefaultQueryEngine.java +++ b/querydsl-collections/src/main/java/com/mysema/query/collections/DefaultQueryEngine.java @@ -20,6 +20,7 @@ import com.mysema.commons.lang.IteratorAdapter; import com.mysema.query.JoinExpression; import com.mysema.query.JoinType; import com.mysema.query.QueryMetadata; +import com.mysema.query.QueryModifiers; import com.mysema.query.types.ArrayConstructorExpression; import com.mysema.query.types.Expression; import com.mysema.query.types.Operation; @@ -51,6 +52,21 @@ public class DefaultQueryEngine implements QueryEngine { } } + @Override + public boolean exists(QueryMetadata metadata, Map, Iterable> iterables) { + QueryModifiers modifiers = metadata.getModifiers(); + metadata.setLimit(1l); + try{ + if (metadata.getJoins().size() == 1){ + return !evaluateSingleSource(metadata, iterables, true).isEmpty(); + }else{ + return !evaluateMultipleSources(metadata, iterables, true).isEmpty(); + } + }finally{ + metadata.setModifiers(modifiers); + } + } + @Override public List list(QueryMetadata metadata, Map, Iterable> iterables, Expression projection){ if (metadata.getJoins().size() == 1){ @@ -70,7 +86,7 @@ public class DefaultQueryEngine implements QueryEngine { } } return rv; - }else{ + }else{ for (T o : list){ if (!rv.contains(o)){ rv.add(o); @@ -183,7 +199,6 @@ public class DefaultQueryEngine implements QueryEngine { } Expression expr = new ArrayConstructorExpression(Object[].class, orderByExpr); Evaluator orderEvaluator = evaluatorFactory.create(metadata, sources, expr); - Collections.sort(list, new MultiComparator(orderEvaluator, directions)); } @@ -194,4 +209,6 @@ public class DefaultQueryEngine implements QueryEngine { return list; } + + } diff --git a/querydsl-collections/src/main/java/com/mysema/query/collections/QueryEngine.java b/querydsl-collections/src/main/java/com/mysema/query/collections/QueryEngine.java index 6507509c3..0381115b6 100644 --- a/querydsl-collections/src/main/java/com/mysema/query/collections/QueryEngine.java +++ b/querydsl-collections/src/main/java/com/mysema/query/collections/QueryEngine.java @@ -42,4 +42,11 @@ public interface QueryEngine { */ List list(QueryMetadata metadata, Map, Iterable> iterables, Expression projection); + /** + * @param metadata + * @param iterables + * @return + */ + boolean exists(QueryMetadata metadata, Map, Iterable> iterables); + } diff --git a/querydsl-collections/src/test/java/com/mysema/query/collections/ExistsTest.java b/querydsl-collections/src/test/java/com/mysema/query/collections/ExistsTest.java new file mode 100644 index 000000000..00d48f832 --- /dev/null +++ b/querydsl-collections/src/test/java/com/mysema/query/collections/ExistsTest.java @@ -0,0 +1,20 @@ +package com.mysema.query.collections; + +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +public class ExistsTest extends AbstractQueryTest{ + + @Test + public void Exists(){ + assertTrue(query().from(cat, cats).where(cat.name.eq("Bob")).exists()); + } + + @Test + public void NotExists(){ + assertTrue(query().from(cat, cats).where(cat.name.eq("Bobby")).notExists()); + } + + +} diff --git a/querydsl-core/src/main/java/com/mysema/query/Projectable.java b/querydsl-core/src/main/java/com/mysema/query/Projectable.java index 967c4afa3..048624104 100644 --- a/querydsl-core/src/main/java/com/mysema/query/Projectable.java +++ b/querydsl-core/src/main/java/com/mysema/query/Projectable.java @@ -35,6 +35,16 @@ public interface Projectable { @Nonnegative long countDistinct(); + /** + * @return true, if rows matching the given criteria exist, otherwise false + */ + boolean exists(); + + /** + * @return true, if no rows matching the given criteria exist, otherwise false + */ + boolean notExists(); + /** * iterate over the results for the given projection * diff --git a/querydsl-core/src/main/java/com/mysema/query/support/ProjectableAdapter.java b/querydsl-core/src/main/java/com/mysema/query/support/ProjectableAdapter.java index c6fcff52c..dbc341c57 100644 --- a/querydsl-core/src/main/java/com/mysema/query/support/ProjectableAdapter.java +++ b/querydsl-core/src/main/java/com/mysema/query/support/ProjectableAdapter.java @@ -42,6 +42,16 @@ public class ProjectableAdapter

implements Projectable { return projectable.countDistinct(); } + @Override + public boolean exists() { + return projectable.exists(); + } + + @Override + public boolean notExists() { + return projectable.notExists(); + } + @Override public CloseableIterator iterate(Expression first, Expression second, Expression... rest) { return projectable.iterate(first, second, rest); @@ -117,6 +127,7 @@ public class ProjectableAdapter

implements Projectable { return projectable.map(key, value); } + @Override public String toString() { return projectable.toString(); } diff --git a/querydsl-core/src/main/java/com/mysema/query/support/ProjectableQuery.java b/querydsl-core/src/main/java/com/mysema/query/support/ProjectableQuery.java index 9e94f2301..dfe5305b1 100644 --- a/querydsl-core/src/main/java/com/mysema/query/support/ProjectableQuery.java +++ b/querydsl-core/src/main/java/com/mysema/query/support/ProjectableQuery.java @@ -37,6 +37,11 @@ public abstract class ProjectableQuery> return count(); } + @Override + public boolean notExists(){ + return !exists(); + } + @Override public final CloseableIterator iterate(Expression first, Expression second, Expression... rest) { return iterate(merge(first, second, rest)); diff --git a/querydsl-core/src/test/java/com/mysema/query/support/DummyProjectable.java b/querydsl-core/src/test/java/com/mysema/query/support/DummyProjectable.java index eaeb85365..44ce89427 100644 --- a/querydsl-core/src/test/java/com/mysema/query/support/DummyProjectable.java +++ b/querydsl-core/src/test/java/com/mysema/query/support/DummyProjectable.java @@ -36,4 +36,14 @@ public class DummyProjectable extends ProjectableQuery{ return SearchResults.emptyResults(); } + @Override + public boolean exists() { + return false; + } + + @Override + public boolean notExists() { + return true; + } + } diff --git a/querydsl-jdo/src/main/java/com/mysema/query/jdo/AbstractJDOQLQuery.java b/querydsl-jdo/src/main/java/com/mysema/query/jdo/AbstractJDOQLQuery.java index dadaeb815..457965d56 100644 --- a/querydsl-jdo/src/main/java/com/mysema/query/jdo/AbstractJDOQLQuery.java +++ b/querydsl-jdo/src/main/java/com/mysema/query/jdo/AbstractJDOQLQuery.java @@ -44,14 +44,14 @@ import com.mysema.query.types.QTuple; public abstract class AbstractJDOQLQuery> extends ProjectableQuery{ private static final Logger logger = LoggerFactory.getLogger(JDOQLQueryImpl.class); - + private final Closeable closeable = new Closeable(){ @Override public void close() throws IOException { - AbstractJDOQLQuery.this.close(); - } + AbstractJDOQLQuery.this.close(); + } }; - + private final boolean detach; private List orderedConstants = new ArrayList(); @@ -59,12 +59,12 @@ public abstract class AbstractJDOQLQuery> extend @Nullable private final PersistenceManager persistenceManager; - private List queries = new ArrayList(2); + private final List queries = new ArrayList(2); private final JDOQLTemplates templates; - - private Set fetchGroups = new HashSet(); - + + private final Set fetchGroups = new HashSet(); + @Nullable private Integer maxFetchDepth; @@ -96,6 +96,7 @@ public abstract class AbstractJDOQLQuery> extend } } + @Override public long count() { Query query = createQuery(true); query.setUnique(true); @@ -108,15 +109,26 @@ public abstract class AbstractJDOQLQuery> extend } } + @Override + public boolean exists(){ + boolean rv = limit(1).uniqueResult(getSource()) != null; + close(); + return rv; + } + + private Expression getSource(){ + return queryMixin.getMetadata().getJoins().get(0).getTarget(); + } + private Query createQuery(boolean forCount) { - Expression source = queryMixin.getMetadata().getJoins().get(0).getTarget(); + Expression source = getSource(); // serialize JDOQLSerializer serializer = new JDOQLSerializer(getTemplates(), source); serializer.serialize(queryMixin.getMetadata(), forCount, false); logQuery(serializer.toString()); - + // create Query Query query = persistenceManager.newQuery(serializer.toString()); orderedConstants = serializer.getConstants(); @@ -130,7 +142,7 @@ public abstract class AbstractJDOQLQuery> extend } else if (FactoryExpression.class.isAssignableFrom(exprType)){ query.setResultClass(projection.get(0).getType()); } - + if (!fetchGroups.isEmpty()){ query.getFetchPlan().setGroups(fetchGroups); } @@ -141,7 +153,7 @@ public abstract class AbstractJDOQLQuery> extend return query; } - + protected void logQuery(String queryString){ if (logger.isDebugEnabled()){ logger.debug(queryString.replace('\n', ' ')); @@ -194,6 +206,7 @@ public abstract class AbstractJDOQLQuery> extend return new IteratorAdapter(list(projection).iterator(), closeable); } + @Override @SuppressWarnings("unchecked") public List list(Expression[] args) { queryMixin.addToProjection(args); @@ -202,6 +215,7 @@ public abstract class AbstractJDOQLQuery> extend return (rv instanceof List) ? ((List)rv) : Collections.singletonList((Object[])rv); } + @Override @SuppressWarnings("unchecked") public List list(Expression expr) { queryMixin.addToProjection(expr); @@ -237,12 +251,11 @@ public abstract class AbstractJDOQLQuery> extend maxFetchDepth = depth; return (Q)this; } - @Override public String toString(){ if (!queryMixin.getMetadata().getJoins().isEmpty()){ - Expression source = queryMixin.getMetadata().getJoins().get(0).getTarget(); + Expression source = getSource(); JDOQLSerializer serializer = new JDOQLSerializer(getTemplates(), source); serializer.serialize(queryMixin.getMetadata(), false, false); return serializer.toString().trim(); @@ -251,6 +264,7 @@ public abstract class AbstractJDOQLQuery> extend } } + @Override @SuppressWarnings("unchecked") public RT uniqueResult(Expression expr) { queryMixin.addToProjection(expr); diff --git a/querydsl-jdo/src/main/java/com/mysema/query/jdo/sql/AbstractSQLQuery.java b/querydsl-jdo/src/main/java/com/mysema/query/jdo/sql/AbstractSQLQuery.java index 8762c079e..429bf1ede 100644 --- a/querydsl-jdo/src/main/java/com/mysema/query/jdo/sql/AbstractSQLQuery.java +++ b/querydsl-jdo/src/main/java/com/mysema/query/jdo/sql/AbstractSQLQuery.java @@ -15,12 +15,13 @@ import com.mysema.query.types.Ops; import com.mysema.query.types.Path; import com.mysema.query.types.Predicate; import com.mysema.query.types.SubQueryExpression; +import com.mysema.query.types.TemplateExpressionImpl; import com.mysema.query.types.expr.NumberExpression; import com.mysema.query.types.expr.NumberOperation; /** * Base class for JDO based SQLQuery implementations - * + * * @author tiwe * * @param @@ -29,6 +30,8 @@ public abstract class AbstractSQLQuery> extends Pr private static final NumberExpression COUNT_ALL_AGG_EXPR = NumberOperation.create(Integer.class, Ops.AggOps.COUNT_ALL_AGG); + private static final Expression ONE = TemplateExpressionImpl.create(Integer.class, "1"); + @SuppressWarnings("unchecked") public AbstractSQLQuery(QueryMetadata metadata) { super(new QueryMixin(metadata)); @@ -40,6 +43,11 @@ public abstract class AbstractSQLQuery> extends Pr return uniqueResult(COUNT_ALL_AGG_EXPR); } + @Override + public boolean exists(){ + return limit(1).uniqueResult(ONE) != null; + } + public T from(Expression... args) { return queryMixin.from(args); } diff --git a/querydsl-jdo/src/test/java/com/mysema/query/JDOQLQueryStandardTest.java b/querydsl-jdo/src/test/java/com/mysema/query/JDOQLQueryStandardTest.java index ff78e846f..0937d0eef 100644 --- a/querydsl-jdo/src/test/java/com/mysema/query/JDOQLQueryStandardTest.java +++ b/querydsl-jdo/src/test/java/com/mysema/query/JDOQLQueryStandardTest.java @@ -8,6 +8,7 @@ package com.mysema.query; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; import java.util.Arrays; import java.util.Calendar; @@ -39,8 +40,7 @@ public class JDOQLQueryStandardTest extends AbstractJDOTest { public static class Projection { - public Projection(String str) { - } + public Projection(String str) {} } @@ -60,7 +60,7 @@ public class JDOQLQueryStandardTest extends AbstractJDOTest { } private static String productName = "ABCD"; - + private static String otherName = "ABC0"; @BeforeClass @@ -97,7 +97,7 @@ public class JDOQLQueryStandardTest extends AbstractJDOTest { } - private QueryExecution standardTest = new QueryExecution(Module.JDOQL, Target.H2){ + private final QueryExecution standardTest = new QueryExecution(Module.JDOQL, Target.H2){ @Override protected Pair>> createQuery() { return Pair.of( @@ -112,13 +112,13 @@ public class JDOQLQueryStandardTest extends AbstractJDOTest { } }; - private QProduct product = QProduct.product; + private final QProduct product = QProduct.product; - private QProduct otherProduct = new QProduct("otherProduct"); + private final QProduct otherProduct = new QProduct("otherProduct"); - private QStore store = QStore.store; + private final QStore store = QStore.store; - private QStore otherStore = new QStore("otherStore"); + private final QStore otherStore = new QStore("otherStore"); @Test public void StandardTest(){ @@ -194,4 +194,14 @@ public class JDOQLQueryStandardTest extends AbstractJDOTest { Param name = new Param(String.class,"name"); assertEquals("ABC0",query().from(product).where(product.name.eq(name)).uniqueResult(product.name)); } + + @Test + public void Exists(){ + assertTrue(query().from(product).where(product.name.eq("ABC0")).exists()); + } + + @Test + public void NotExists(){ + assertTrue(query().from(product).where(product.name.eq("XXX")).notExists()); + } } diff --git a/querydsl-jpa/src/main/java/com/mysema/query/jpa/AbstractSQLQuery.java b/querydsl-jpa/src/main/java/com/mysema/query/jpa/AbstractSQLQuery.java index a1b957ff7..58707a63e 100644 --- a/querydsl-jpa/src/main/java/com/mysema/query/jpa/AbstractSQLQuery.java +++ b/querydsl-jpa/src/main/java/com/mysema/query/jpa/AbstractSQLQuery.java @@ -16,10 +16,11 @@ import com.mysema.query.types.Ops; import com.mysema.query.types.Path; import com.mysema.query.types.Predicate; import com.mysema.query.types.SubQueryExpression; +import com.mysema.query.types.TemplateExpressionImpl; /** * Abstract super class for SQLQuery implementation for JPA and Hibernate - * + * * @author tiwe * * @param @@ -28,6 +29,8 @@ public abstract class AbstractSQLQuery> extends Pr private static final Expression COUNT_ALL_AGG_EXPR = OperationImpl.create(Integer.class, Ops.AggOps.COUNT_ALL_AGG); + private static final Expression ONE = TemplateExpressionImpl.create(Integer.class, "1"); + @SuppressWarnings("unchecked") public AbstractSQLQuery(QueryMetadata metadata) { super(new QueryMixin(metadata)); @@ -39,6 +42,11 @@ public abstract class AbstractSQLQuery> extends Pr return uniqueResult(COUNT_ALL_AGG_EXPR); } + @Override + public boolean exists(){ + return limit(1).uniqueResult(ONE) != null; + } + public T from(Expression... args) { return queryMixin.from(args); } diff --git a/querydsl-jpa/src/main/java/com/mysema/query/jpa/JPQLQueryBase.java b/querydsl-jpa/src/main/java/com/mysema/query/jpa/JPQLQueryBase.java index a43091e2f..45c03a9f2 100644 --- a/querydsl-jpa/src/main/java/com/mysema/query/jpa/JPQLQueryBase.java +++ b/querydsl-jpa/src/main/java/com/mysema/query/jpa/JPQLQueryBase.java @@ -11,9 +11,11 @@ import com.mysema.query.QueryMetadata; import com.mysema.query.support.ProjectableQuery; import com.mysema.query.types.CollectionExpression; import com.mysema.query.types.EntityPath; +import com.mysema.query.types.Expression; import com.mysema.query.types.MapExpression; import com.mysema.query.types.Path; import com.mysema.query.types.Predicate; +import com.mysema.query.types.TemplateExpressionImpl; /** * JPQLQueryBase is a base Query class for JPQL queries @@ -23,6 +25,8 @@ import com.mysema.query.types.Predicate; */ public abstract class JPQLQueryBase> extends ProjectableQuery { + private static final Expression ONE = TemplateExpressionImpl.create(Integer.class, "1"); + private Map constants; private final JPQLQueryMixin queryMixin; @@ -59,6 +63,11 @@ public abstract class JPQLQueryBase> extends Projecta queryMixin.getMetadata().reset(); } + @Override + public boolean exists(){ + return limit(1).uniqueResult(ONE) != null; + } + public Q fetch(){ return queryMixin.fetch(); } diff --git a/querydsl-jpa/src/main/java/com/mysema/query/jpa/sql/JPASQLQuery.java b/querydsl-jpa/src/main/java/com/mysema/query/jpa/sql/JPASQLQuery.java index e293f4cc9..c5684b56c 100644 --- a/querydsl-jpa/src/main/java/com/mysema/query/jpa/sql/JPASQLQuery.java +++ b/querydsl-jpa/src/main/java/com/mysema/query/jpa/sql/JPASQLQuery.java @@ -169,15 +169,16 @@ public final class JPASQLQuery extends AbstractSQLQuery implements return buildQueryString(false); } + @Override @SuppressWarnings("unchecked") public RT uniqueResult(Expression expr) { Query query = createQuery(expr); reset(); try{ - return (RT) query.getSingleResult(); + return (RT) query.getSingleResult(); }catch(NoResultException e){ logger.debug(e.getMessage(),e); - return null; + return null; } } diff --git a/querydsl-jpa/src/test/java/com/mysema/query/AbstractStandardTest.java b/querydsl-jpa/src/test/java/com/mysema/query/AbstractStandardTest.java index f0c128e60..c30806c62 100644 --- a/querydsl-jpa/src/test/java/com/mysema/query/AbstractStandardTest.java +++ b/querydsl-jpa/src/test/java/com/mysema/query/AbstractStandardTest.java @@ -9,6 +9,7 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; import java.util.ArrayList; import java.util.Arrays; @@ -53,15 +54,15 @@ public abstract class AbstractStandardTest { } } - + public static class QProjection extends ConstructorExpression{ - + private static final long serialVersionUID = -5866362075090550839L; public QProjection(StringExpression str, QCat cat){ super(Projection.class, new Class[]{String.class, Cat.class}, new Expression[]{str, cat}); } - + } private static final QCat cat = QCat.cat; @@ -76,7 +77,8 @@ public abstract class AbstractStandardTest { private final java.sql.Date date; - private Projections projections = new Projections(Module.HQL, getTarget()){ + private final Projections projections = new Projections(Module.HQL, getTarget()){ + @Override public Collection> list(ListPath expr, ListExpression other, A knownElement){ // NOTE : expr.get(0) is only supported in the where clause return Collections.>singleton(expr.size()); @@ -85,7 +87,7 @@ public abstract class AbstractStandardTest { private final List savedCats = new ArrayList(); - private QueryExecution standardTest = new QueryExecution( + private final QueryExecution standardTest = new QueryExecution( projections, new Filters(projections, Module.HQL, getTarget()), new MatchingFilters(Module.HQL, getTarget())){ @Override @@ -149,7 +151,7 @@ public abstract class AbstractStandardTest { cat.setBirthdate(birthDate); save(cat); savedCats.add(cat); - + Show show = new Show(); show.acts = new HashMap(); show.acts.put("a","A"); @@ -188,13 +190,23 @@ public abstract class AbstractStandardTest { public void Any_Simple(){ assertEquals(1, catQuery().where(cat.kittens.any().name.eq("Ruth123")).count()); } - + + @Test + public void Exists(){ + assertTrue(catQuery().where(cat.kittens.any().name.eq("Ruth123")).exists()); + } + + @Test + public void NotExists(){ + assertTrue(catQuery().where(cat.kittens.any().name.eq("XXX")).notExists()); + } + @Test public void Any_And(){ assertEquals(1, catQuery().where(cat.kittens.any().name.eq("Ruth123"), cat.kittens.any().bodyWeight.lt(10.0)).count()); assertEquals(0, catQuery().where(cat.kittens.any().name.eq("Ruth123"), cat.kittens.any().bodyWeight.gt(10.0)).count()); } - + @Test public void Aggregates_UniqueResult(){ // uniqueResult @@ -208,7 +220,7 @@ public abstract class AbstractStandardTest { assertEquals(Integer.valueOf(1), catQuery().list(cat.id.min()).get(0)); assertEquals(Integer.valueOf(6), catQuery().list(cat.id.max()).get(0)); } - + @Test public void DistinctResults(){ System.out.println("-- list results"); @@ -242,26 +254,26 @@ public abstract class AbstractStandardTest { assertEquals(0, catQuery().where(cat.name.endsWith("X")).count()); assertEquals(1, catQuery().where(cat.name.endsWithIgnoreCase("H123")).count()); } - + @Test public void Contains(){ // contains assertEquals(1, catQuery().where(cat.name.contains("eli")).count()); } - + @Test public void Length(){ // length assertEquals(6, catQuery().where(cat.name.length().gt(0)).count()); } - + @Test public void IndexOf(){ // indexOf assertEquals(Integer.valueOf(0), catQuery().where(cat.name.eq("Bob123")).uniqueResult(cat.name.indexOf("B"))); - assertEquals(Integer.valueOf(1), catQuery().where(cat.name.eq("Bob123")).uniqueResult(cat.name.indexOf("o"))); + assertEquals(Integer.valueOf(1), catQuery().where(cat.name.eq("Bob123")).uniqueResult(cat.name.indexOf("o"))); } - + @Test public void StringOperations(){ // case-sensitivity @@ -271,26 +283,26 @@ public abstract class AbstractStandardTest { assertEquals(Integer.valueOf(2), catQuery().where(cat.name.eq("Bob123")).uniqueResult(cat.name.indexOf("b"))); } } - + @Test public void Limit(){ // limit List names1 = Arrays.asList("Allen123","Bob123"); - assertEquals(names1, catQuery().orderBy(cat.name.asc()).limit(2).list(cat.name)); + assertEquals(names1, catQuery().orderBy(cat.name.asc()).limit(2).list(cat.name)); } - + @Test public void Offset(){ // offset List names2 = Arrays.asList("Felix123","Mary123","Ruth123","Some"); - assertEquals(names2, catQuery().orderBy(cat.name.asc()).offset(2).list(cat.name)); + assertEquals(names2, catQuery().orderBy(cat.name.asc()).offset(2).list(cat.name)); } - + @Test public void Limit_and_offset(){ // limit + offset List names3 = Arrays.asList("Felix123","Mary123"); - assertEquals(names3, catQuery().orderBy(cat.name.asc()).limit(2).offset(2).list(cat.name)); + assertEquals(names3, catQuery().orderBy(cat.name.asc()).limit(2).offset(2).list(cat.name)); } @Test @@ -327,7 +339,7 @@ public abstract class AbstractStandardTest { assertNotNull(projection); } } - + @Test public void ConstructorProjection2(){ List projections = query().from(cat).list(new QProjection(cat.name, cat)); @@ -354,12 +366,12 @@ public abstract class AbstractStandardTest { Param name = new Param(String.class,"name"); assertEquals("Bob123",query().from(cat).where(cat.name.eq(name)).uniqueResult(cat.name)); } - + @Test public void Null_as_uniqueResult(){ assertNull(query().from(cat).where(cat.name.eq(UUID.randomUUID().toString())).uniqueResult(cat)); } - + @Test public void Map_ContainsKey(){ QShow show = QShow.show; @@ -367,7 +379,7 @@ public abstract class AbstractStandardTest { assertEquals(1l, query().from(show).where(show.acts.containsKey("b")).count()); assertEquals(0l, query().from(show).where(show.acts.containsKey("c")).count()); } - + @Test public void Map_ContainsValue(){ QShow show = QShow.show; diff --git a/querydsl-sql/src/main/java/com/mysema/query/sql/AbstractSQLQuery.java b/querydsl-sql/src/main/java/com/mysema/query/sql/AbstractSQLQuery.java index 31b60999b..03fcf3a15 100644 --- a/querydsl-sql/src/main/java/com/mysema/query/sql/AbstractSQLQuery.java +++ b/querydsl-sql/src/main/java/com/mysema/query/sql/AbstractSQLQuery.java @@ -35,15 +35,7 @@ import com.mysema.query.SearchResults; import com.mysema.query.QueryFlag.Position; import com.mysema.query.support.ProjectableQuery; import com.mysema.query.support.QueryMixin; -import com.mysema.query.types.Expression; -import com.mysema.query.types.FactoryExpression; -import com.mysema.query.types.OrderSpecifier; -import com.mysema.query.types.ParamExpression; -import com.mysema.query.types.ParamNotSetException; -import com.mysema.query.types.Path; -import com.mysema.query.types.Predicate; -import com.mysema.query.types.QBean; -import com.mysema.query.types.SubQueryExpression; +import com.mysema.query.types.*; import com.mysema.query.types.query.ListSubQuery; import com.mysema.query.types.template.SimpleTemplate; import com.mysema.util.ResultSetAdapter; @@ -68,7 +60,7 @@ public abstract class AbstractSQLQuery> extends return (List) IteratorAdapter.asList(iterateMultiple()); } } - + @SuppressWarnings("unchecked") @Override public CloseableIterator iterate() { @@ -84,7 +76,7 @@ public abstract class AbstractSQLQuery> extends AbstractSQLQuery.this.orderBy(o); return this; } - + @Override public String toString(){ return AbstractSQLQuery.this.toString(); @@ -94,6 +86,8 @@ public abstract class AbstractSQLQuery> extends private static final Logger logger = LoggerFactory.getLogger(AbstractSQLQuery.class); + private static final Expression ONE = TemplateExpressionImpl.create(Integer.class, "1"); + @Nullable private final Connection conn; @@ -102,7 +96,7 @@ public abstract class AbstractSQLQuery> extends @Nullable private List> constantPaths; - + @Nullable private SubQueryExpression[] union; @@ -119,7 +113,7 @@ public abstract class AbstractSQLQuery> extends this.conn = conn; this.configuration = configuration; } - + @SuppressWarnings("unchecked") protected Q addJoinFlag(String flag){ List joins = queryMixin.getMetadata().getJoins(); @@ -131,15 +125,15 @@ public abstract class AbstractSQLQuery> extends Expression flag = SimpleTemplate.create(expr.getType(), prefix + "{0}", expr); return queryMixin.addFlag(new QueryFlag(position, flag)); } - + protected Q addFlag(Position position, String flag){ return queryMixin.addFlag(new QueryFlag(position, flag)); } - + protected Q addFlag(Position position, Expression flag){ return queryMixin.addFlag(new QueryFlag(position, flag)); } - + protected String buildQueryString(boolean forCountRow) { SQLSerializer serializer = createSerializer(); if (union != null) { @@ -163,6 +157,11 @@ public abstract class AbstractSQLQuery> extends } } + @Override + public boolean exists(){ + return limit(1).uniqueResult(ONE) != null; + } + protected SQLSerializer createSerializer() { return new SQLSerializer(configuration.getTemplates()); } @@ -236,11 +235,11 @@ public abstract class AbstractSQLQuery> extends private T get(ResultSet rs, Expression expr, int i, Class type) throws SQLException { return configuration.get(rs, expr instanceof Path ? (Path)expr : null, i, type); } - + private int set(PreparedStatement stmt, Path path, int i, Object value) throws SQLException{ return configuration.set(stmt, path, i, value); } - + public QueryMetadata getMetadata() { return queryMixin.getMetadata(); } @@ -311,8 +310,8 @@ public abstract class AbstractSQLQuery> extends } }else{ queryMixin.addToProjection(expr); - return iterateSingle(expr); - } + return iterateSingle(expr); + } } private CloseableIterator iterateMultiple() { @@ -398,7 +397,7 @@ public abstract class AbstractSQLQuery> extends } return (RT) rv; } else{ - return (RT) get(rs, expr, 1, expr.getType()); + return get(rs, expr, 1, expr.getType()); } } catch (IllegalAccessException e) { close(); @@ -469,14 +468,14 @@ public abstract class AbstractSQLQuery> extends queryMixin.getMetadata().reset(); constants = null; } - + protected void setParameters(PreparedStatement stmt, List objects, List> constantPaths, Map, ?> params){ if (objects.size() != constantPaths.size()){ throw new IllegalArgumentException("Expected " + objects.size() + " paths, but got " + constantPaths.size()); } int counter = 1; for (int i = 0; i < objects.size(); i++){ - Object o = objects.get(i); + Object o = objects.get(i); try { if (ParamExpression.class.isInstance(o)){ if (!params.containsKey(o)){ @@ -516,7 +515,7 @@ public abstract class AbstractSQLQuery> extends }finally{ iterator.close(); } - + } private long unsafeCount() throws SQLException { @@ -546,5 +545,5 @@ public abstract class AbstractSQLQuery> extends } } } - + } diff --git a/querydsl-sql/src/test/java/com/mysema/query/SelectBaseTest.java b/querydsl-sql/src/test/java/com/mysema/query/SelectBaseTest.java index 6dc439da2..36157f317 100644 --- a/querydsl-sql/src/test/java/com/mysema/query/SelectBaseTest.java +++ b/querydsl-sql/src/test/java/com/mysema/query/SelectBaseTest.java @@ -62,7 +62,9 @@ import com.mysema.query.types.path.PathBuilder; import com.mysema.query.types.query.ListSubQuery; import com.mysema.query.types.query.NumberSubQuery; import com.mysema.query.types.query.SimpleSubQuery; -import com.mysema.testutil.*; +import com.mysema.testutil.ExcludeIn; +import com.mysema.testutil.IncludeIn; +import com.mysema.testutil.Label; public abstract class SelectBaseTest extends AbstractBaseTest{ @@ -73,7 +75,7 @@ public abstract class SelectBaseTest extends AbstractBaseTest{ } - private QueryExecution standardTest = new QueryExecution(Module.SQL, getClass().getAnnotation(Label.class).value()){ + private final QueryExecution standardTest = new QueryExecution(Module.SQL, getClass().getAnnotation(Label.class).value()){ @Override protected Pair>> createQuery() { return Pair.of( @@ -248,7 +250,7 @@ public abstract class SelectBaseTest extends AbstractBaseTest{ NumberPath rowCount = new NumberPath(Long.class, "rowCount"); query().from(employee).uniqueResult(Wildcard.count().as(rowCount)); } - + @Test public void Custom_Projection(){ List tuples = query().from(employee).list( @@ -286,7 +288,7 @@ public abstract class SelectBaseTest extends AbstractBaseTest{ System.out.println(name); } } - + @Test public void Inner_Join() throws SQLException { query().from(employee).innerJoin(employee2) @@ -300,14 +302,14 @@ public abstract class SelectBaseTest extends AbstractBaseTest{ .on(employee.superiorIdKey.on(employee2)) .list(employee.id, employee2.id); } - + @Test public void Right_Join() throws SQLException { query().from(employee).rightJoin(employee2) .on(employee.superiorIdKey.on(employee2)) .list(employee.id, employee2.id); } - + @Test @IncludeIn({POSTGRES}) public void Full_Join() throws SQLException { @@ -326,6 +328,16 @@ public abstract class SelectBaseTest extends AbstractBaseTest{ } } + @Test + public void Exists(){ + assertTrue(query().from(employee).where(employee.firstname.eq("Barbara")).exists()); + } + + @Test + public void NotExists(){ + assertTrue(query().from(employee).where(employee.firstname.eq("Barb")).notExists()); + } + @Test public void Limit() throws SQLException { // limit @@ -333,7 +345,7 @@ public abstract class SelectBaseTest extends AbstractBaseTest{ .orderBy(employee.firstname.asc()) .limit(4).list(employee.id); } - + @Test public void Limit_And_Offset() throws SQLException { // limit and offset @@ -341,9 +353,9 @@ public abstract class SelectBaseTest extends AbstractBaseTest{ .orderBy(employee.firstname.asc()) .limit(4).offset(3).list(employee.id); } - + @Test - public void Limitt_and_Order(){ + public void Limit_and_Order(){ // limit List names1 = Arrays.asList("Barbara","Daisy","Helen","Jennifer"); assertEquals(names1, query().from(employee) @@ -417,13 +429,13 @@ public abstract class SelectBaseTest extends AbstractBaseTest{ public void Path_Alias(){ expectedQuery = "select e.LASTNAME, sum(e.SALARY) as salarySum from EMPLOYEE2 e group by e.LASTNAME having salarySum > ?"; - NumberExpression salarySum = employee.salary.sum().as("salarySum"); + NumberExpression salarySum = employee.salary.sum().as("salarySum"); query().from(employee) .groupBy(employee.lastname) .having(salarySum.gt(10000)) .list(employee.lastname, salarySum); } - + @Test public void Projection() throws IOException{ CloseableIterator results = query().from(survey).iterate(survey.all()); @@ -572,7 +584,7 @@ public abstract class SelectBaseTest extends AbstractBaseTest{ query().from(employee).where(where).list(employee.firstname); } } - + @Test public void SubQuery_InnerJoin(){ ListSubQuery sq = sq().from(employee2).list(employee2.id); @@ -580,7 +592,7 @@ public abstract class SelectBaseTest extends AbstractBaseTest{ query().from(employee).innerJoin(sq, sqEmp).on(sqEmp.id.eq(employee.id)).list(employee.id); } - + @Test public void SubQuery_LeftJoin(){ ListSubQuery sq = sq().from(employee2).list(employee2.id); @@ -588,7 +600,7 @@ public abstract class SelectBaseTest extends AbstractBaseTest{ query().from(employee).leftJoin(sq, sqEmp).on(sqEmp.id.eq(employee.id)).list(employee.id); } - + @Test public void SubQuery_RightJoin(){ ListSubQuery sq = sq().from(employee2).list(employee2.id); @@ -596,7 +608,7 @@ public abstract class SelectBaseTest extends AbstractBaseTest{ query().from(employee).rightJoin(sq, sqEmp).on(sqEmp.id.eq(employee.id)).list(employee.id); } - + @Test public void SubQuerySerialization(){ SQLSubQuery query = sq(); @@ -606,13 +618,13 @@ public abstract class SelectBaseTest extends AbstractBaseTest{ query.from(survey2); assertEquals("from SURVEY s, SURVEY s2", query.toString()); } - + @Test public void SubQuerySerialization2(){ NumberPath sal = new NumberPath(BigDecimal.class, "sal"); PathBuilder sq = new PathBuilder(Object[].class, "sq"); SQLSerializer serializer = new SQLSerializer(SQLTemplates.DEFAULT); - + serializer.handle( sq() .from(employee) @@ -655,7 +667,7 @@ public abstract class SelectBaseTest extends AbstractBaseTest{ assertEquals(2, row.length); assertEquals(Integer.class, row[0].getClass()); assertEquals(String.class, row[1].getClass()); - } + } } @Test @@ -668,7 +680,7 @@ public abstract class SelectBaseTest extends AbstractBaseTest{ assertEquals(IdName.class, row[2].getClass()); } } - + @SuppressWarnings("unchecked") @Test public void Union() throws SQLException { @@ -706,13 +718,13 @@ public abstract class SelectBaseTest extends AbstractBaseTest{ // iterator CloseableIterator iterator = query().union(sq1,sq2).iterate(); try{ - assertTrue(iterator.hasNext()); + assertTrue(iterator.hasNext()); assertTrue(iterator.next() != null); assertTrue(iterator.next() != null); assertFalse(iterator.hasNext()); }finally{ iterator.close(); - } + } } @SuppressWarnings("unchecked") @@ -730,13 +742,13 @@ public abstract class SelectBaseTest extends AbstractBaseTest{ // iterator CloseableIterator iterator = query().union(sq1,sq2).iterate(); try{ - assertTrue(iterator.hasNext()); + assertTrue(iterator.hasNext()); assertTrue(iterator.next() != null); assertTrue(iterator.next() != null); assertFalse(iterator.hasNext()); }finally{ iterator.close(); - } + } } @Test