mirror of
https://github.com/querydsl/querydsl.git
synced 2026-06-13 21:01:01 +08:00
#717563 : unified Projectable.uniqueResult() semantics
This commit is contained in:
parent
6d2a1efddd
commit
c0bdc03969
@ -0,0 +1,14 @@
|
||||
package com.mysema.query.collections;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import com.mysema.query.NonUniqueResultException;
|
||||
|
||||
public class UniqueResultContract extends AbstractQueryTest{
|
||||
|
||||
@Test(expected=NonUniqueResultException.class)
|
||||
public void Unique_Result_Throws_Exception_On_Multiple_Results(){
|
||||
MiniApi.from(cat, cats).where(cat.name.isNotNull()).uniqueResult(cat);
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,19 @@
|
||||
package com.mysema.query;
|
||||
|
||||
/**
|
||||
* @author tiwe
|
||||
*
|
||||
*/
|
||||
public class NonUniqueResultException extends QueryException{
|
||||
|
||||
private static final long serialVersionUID = -1757423191400510323L;
|
||||
|
||||
public NonUniqueResultException() {
|
||||
super("Only one result is allowed for uniqueResult calls");
|
||||
}
|
||||
|
||||
public NonUniqueResultException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
}
|
||||
@ -196,6 +196,7 @@ public interface Projectable {
|
||||
* @param first
|
||||
* @param second
|
||||
* @param rest
|
||||
* @throws NonUniqueResultException if there is more than one matching result
|
||||
* @return
|
||||
*/
|
||||
@Nullable
|
||||
@ -203,10 +204,9 @@ public interface Projectable {
|
||||
|
||||
/**
|
||||
* return a unique result for the given projection or null if not result is found
|
||||
*
|
||||
* <p>for multiple results only the first one is returned</p>
|
||||
*
|
||||
* @param args
|
||||
* @throws NonUniqueResultException if there is more than one matching result
|
||||
* @return
|
||||
*/
|
||||
@Nullable
|
||||
@ -214,12 +214,11 @@ public interface Projectable {
|
||||
|
||||
/**
|
||||
* return a unique result for the given projection or null if not result is found
|
||||
*
|
||||
* <p>for multiple results only the first one is returned</p>
|
||||
*
|
||||
* @param <RT>
|
||||
* return type
|
||||
* @param projection
|
||||
* @throws NonUniqueResultException if there is more than one matching result
|
||||
* @return the result or null for an empty result
|
||||
*/
|
||||
@Nullable
|
||||
|
||||
@ -49,7 +49,8 @@ public interface SimpleProjectable<T> {
|
||||
|
||||
/**
|
||||
* Get the projection as a unique result
|
||||
*
|
||||
*
|
||||
* @throws NonUniqueResultException if there is more than one matching result
|
||||
* @return
|
||||
*/
|
||||
@Nullable
|
||||
|
||||
@ -13,6 +13,7 @@ import java.util.Map;
|
||||
import org.apache.commons.collections15.IteratorUtils;
|
||||
|
||||
import com.mysema.commons.lang.CloseableIterator;
|
||||
import com.mysema.query.NonUniqueResultException;
|
||||
import com.mysema.query.Projectable;
|
||||
import com.mysema.query.SearchResults;
|
||||
import com.mysema.query.types.Expression;
|
||||
@ -128,15 +129,29 @@ public abstract class ProjectableQuery<Q extends ProjectableQuery<Q>>
|
||||
|
||||
public Object[] uniqueResult(Expression<?>[] args) {
|
||||
queryMixin.setUnique(true);
|
||||
Iterator<Object[]> it = iterate(args);
|
||||
return it.hasNext() ? it.next() : null;
|
||||
return getUniqueResult(iterate(args));
|
||||
}
|
||||
|
||||
@Override
|
||||
public <RT> RT uniqueResult(Expression<RT> expr) {
|
||||
queryMixin.setUnique(true);
|
||||
limit(1l);
|
||||
Iterator<RT> it = iterate(expr);
|
||||
return it.hasNext() ? it.next() : null;
|
||||
if (queryMixin.getMetadata().getModifiers().getLimit() == null){
|
||||
limit(2l);
|
||||
}
|
||||
return getUniqueResult(iterate(expr));
|
||||
}
|
||||
|
||||
protected <T> T getUniqueResult(Iterator<T> it) {
|
||||
if (it.hasNext()){
|
||||
T rv = it.next();
|
||||
if (it.hasNext()){
|
||||
throw new NonUniqueResultException();
|
||||
}
|
||||
return rv;
|
||||
}else{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@ -15,6 +15,7 @@ import org.hibernate.search.Search;
|
||||
import com.mysema.commons.lang.Assert;
|
||||
import com.mysema.commons.lang.CloseableIterator;
|
||||
import com.mysema.commons.lang.IteratorAdapter;
|
||||
import com.mysema.query.NonUniqueResultException;
|
||||
import com.mysema.query.QueryMetadata;
|
||||
import com.mysema.query.QueryModifiers;
|
||||
import com.mysema.query.SearchResults;
|
||||
@ -159,7 +160,11 @@ public class SearchQuery<T> implements SimpleQuery<SearchQuery<T>>, SimpleProjec
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public T uniqueResult() {
|
||||
return (T) createQuery(false).uniqueResult();
|
||||
try{
|
||||
return (T) createQuery(false).uniqueResult();
|
||||
}catch (org.hibernate.NonUniqueResultException e){
|
||||
throw new NonUniqueResultException();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@ -15,6 +15,7 @@ import java.util.List;
|
||||
import org.hibernate.Session;
|
||||
import org.junit.Test;
|
||||
|
||||
import com.mysema.query.NonUniqueResultException;
|
||||
import com.mysema.query.SearchResults;
|
||||
import com.mysema.query.types.OrderSpecifier;
|
||||
import com.mysema.query.types.expr.BooleanExpression;
|
||||
@ -57,6 +58,11 @@ public class SearchQueryTest extends AbstractQueryTest{
|
||||
assertEquals(u, list.get(0));
|
||||
}
|
||||
|
||||
@Test(expected=NonUniqueResultException.class)
|
||||
public void Unique_Result_Throws_Exception_On_Multiple_Results(){
|
||||
query().where(user.middleName.eq("X")).uniqueResult();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void Ordering(){
|
||||
BooleanExpression filter = user.middleName.eq("X");
|
||||
|
||||
@ -24,6 +24,7 @@ import org.slf4j.LoggerFactory;
|
||||
import com.mysema.commons.lang.CloseableIterator;
|
||||
import com.mysema.commons.lang.IteratorAdapter;
|
||||
import com.mysema.query.DefaultQueryMetadata;
|
||||
import com.mysema.query.NonUniqueResultException;
|
||||
import com.mysema.query.QueryException;
|
||||
import com.mysema.query.QueryMetadata;
|
||||
import com.mysema.query.QueryModifiers;
|
||||
@ -270,9 +271,24 @@ public abstract class AbstractJDOQLQuery<Q extends AbstractJDOQLQuery<Q>> extend
|
||||
@Nullable
|
||||
public <RT> RT uniqueResult(Expression<RT> expr) {
|
||||
queryMixin.addToProjection(expr);
|
||||
Query query = createQuery(false);
|
||||
query.setUnique(true);
|
||||
if (getMetadata().getModifiers().getLimit() == null){
|
||||
limit(2);
|
||||
}
|
||||
Query query = createQuery(false);
|
||||
reset();
|
||||
return (RT) execute(query);
|
||||
Object rv = execute(query);
|
||||
if (rv instanceof List){
|
||||
List<RT> list = (List)rv;
|
||||
if (!list.isEmpty()){
|
||||
if (list.size() > 1){
|
||||
throw new NonUniqueResultException();
|
||||
}
|
||||
return list.get(0);
|
||||
}else{
|
||||
return null;
|
||||
}
|
||||
}else{
|
||||
return (RT)rv;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -17,6 +17,7 @@ import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
|
||||
import com.mysema.query.BooleanBuilder;
|
||||
import com.mysema.query.NonUniqueResultException;
|
||||
import com.mysema.query.jdo.test.domain.Book;
|
||||
import com.mysema.query.jdo.test.domain.Product;
|
||||
import com.mysema.query.jdo.test.domain.QBook;
|
||||
@ -67,6 +68,11 @@ public class BasicsTest extends AbstractJDOTest {
|
||||
public void CountTests() {
|
||||
assertEquals("count", 2, query().from(product).count());
|
||||
}
|
||||
|
||||
@Test(expected=NonUniqueResultException.class)
|
||||
public void Unique_Result_Throws_Exception_On_Multiple_Results(){
|
||||
query().from(product).uniqueResult(product);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void SimpleTest() throws IOException{
|
||||
|
||||
@ -22,6 +22,7 @@ import org.slf4j.LoggerFactory;
|
||||
import com.mysema.commons.lang.CloseableIterator;
|
||||
import com.mysema.commons.lang.IteratorAdapter;
|
||||
import com.mysema.query.DefaultQueryMetadata;
|
||||
import com.mysema.query.NonUniqueResultException;
|
||||
import com.mysema.query.QueryException;
|
||||
import com.mysema.query.QueryMetadata;
|
||||
import com.mysema.query.QueryModifiers;
|
||||
@ -351,8 +352,12 @@ public abstract class AbstractHibernateQuery<Q extends AbstractHibernateQuery<Q>
|
||||
String queryString = toQueryString();
|
||||
logQuery(queryString);
|
||||
Query query = createQuery(queryString, modifiers);
|
||||
reset();
|
||||
return (RT) query.uniqueResult();
|
||||
reset();
|
||||
try{
|
||||
return (RT) query.uniqueResult();
|
||||
}catch (org.hibernate.NonUniqueResultException e){
|
||||
throw new NonUniqueResultException();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -19,6 +19,7 @@ import org.slf4j.LoggerFactory;
|
||||
import com.mysema.commons.lang.CloseableIterator;
|
||||
import com.mysema.commons.lang.IteratorAdapter;
|
||||
import com.mysema.query.DefaultQueryMetadata;
|
||||
import com.mysema.query.NonUniqueResultException;
|
||||
import com.mysema.query.QueryMetadata;
|
||||
import com.mysema.query.QueryModifiers;
|
||||
import com.mysema.query.SearchResults;
|
||||
@ -201,8 +202,12 @@ public final class HibernateSQLQuery extends AbstractSQLQuery<HibernateSQLQuery>
|
||||
@SuppressWarnings("unchecked")
|
||||
public <RT> RT uniqueResult(Expression<RT> expr) {
|
||||
Query query = createQuery(expr);
|
||||
reset();
|
||||
return (RT) query.uniqueResult();
|
||||
reset();
|
||||
try{
|
||||
return (RT) query.uniqueResult();
|
||||
}catch (org.hibernate.NonUniqueResultException e){
|
||||
throw new NonUniqueResultException();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -13,7 +13,6 @@ import java.util.Map;
|
||||
import javax.annotation.Nullable;
|
||||
import javax.persistence.EntityManager;
|
||||
import javax.persistence.LockModeType;
|
||||
import javax.persistence.NoResultException;
|
||||
import javax.persistence.Query;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
@ -22,6 +21,7 @@ import org.slf4j.LoggerFactory;
|
||||
import com.mysema.commons.lang.CloseableIterator;
|
||||
import com.mysema.commons.lang.IteratorAdapter;
|
||||
import com.mysema.query.DefaultQueryMetadata;
|
||||
import com.mysema.query.NonUniqueResultException;
|
||||
import com.mysema.query.QueryException;
|
||||
import com.mysema.query.QueryMetadata;
|
||||
import com.mysema.query.QueryModifiers;
|
||||
@ -216,11 +216,12 @@ public abstract class AbstractJPAQuery<Q extends AbstractJPAQuery<Q>> extends JP
|
||||
reset();
|
||||
try{
|
||||
return (RT) query.getSingleResult();
|
||||
}catch(NoResultException e){
|
||||
}catch(javax.persistence.NoResultException e){
|
||||
logger.debug(e.getMessage(),e);
|
||||
return null;
|
||||
}catch(javax.persistence.NonUniqueResultException e){
|
||||
throw new NonUniqueResultException();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
|
||||
@ -19,6 +19,7 @@ import org.slf4j.LoggerFactory;
|
||||
import com.mysema.commons.lang.CloseableIterator;
|
||||
import com.mysema.commons.lang.IteratorAdapter;
|
||||
import com.mysema.query.DefaultQueryMetadata;
|
||||
import com.mysema.query.NonUniqueResultException;
|
||||
import com.mysema.query.QueryMetadata;
|
||||
import com.mysema.query.QueryModifiers;
|
||||
import com.mysema.query.SearchResults;
|
||||
@ -176,9 +177,11 @@ public final class JPASQLQuery extends AbstractSQLQuery<JPASQLQuery> implements
|
||||
reset();
|
||||
try{
|
||||
return (RT) query.getSingleResult();
|
||||
}catch(NoResultException e){
|
||||
}catch(javax.persistence.NoResultException e){
|
||||
logger.debug(e.getMessage(),e);
|
||||
return null;
|
||||
}catch(javax.persistence.NonUniqueResultException e){
|
||||
throw new NonUniqueResultException();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -14,6 +14,7 @@ import org.apache.lucene.search.Sort;
|
||||
import com.mysema.commons.lang.CloseableIterator;
|
||||
import com.mysema.commons.lang.EmptyCloseableIterator;
|
||||
import com.mysema.commons.lang.IteratorAdapter;
|
||||
import com.mysema.query.NonUniqueResultException;
|
||||
import com.mysema.query.QueryException;
|
||||
import com.mysema.query.QueryMetadata;
|
||||
import com.mysema.query.QueryModifiers;
|
||||
@ -202,7 +203,7 @@ SimpleProjectable<T> {
|
||||
}
|
||||
final ScoreDoc[] scoreDocs = searcher.search(createQuery(), maxDoc).scoreDocs;
|
||||
if (scoreDocs.length > 1) {
|
||||
throw new QueryException("More than one result found!");
|
||||
throw new NonUniqueResultException();
|
||||
} else if (scoreDocs.length == 1) {
|
||||
return transformer.transform(searcher.doc(scoreDocs[0].doc));
|
||||
} else {
|
||||
|
||||
@ -15,6 +15,7 @@ import com.mongodb.DBCollection;
|
||||
import com.mongodb.DBCursor;
|
||||
import com.mongodb.DBObject;
|
||||
import com.mysema.commons.lang.CloseableIterator;
|
||||
import com.mysema.query.NonUniqueResultException;
|
||||
import com.mysema.query.QueryMetadata;
|
||||
import com.mysema.query.QueryModifiers;
|
||||
import com.mysema.query.SearchResults;
|
||||
@ -148,7 +149,15 @@ public class MongodbQuery<K> implements SimpleQuery<MongodbQuery<K>>, SimpleProj
|
||||
@Override
|
||||
public K uniqueResult() {
|
||||
DBCursor c = createCursor().limit(1);
|
||||
return c.hasNext() ? transformer.transform(c.next()) : null;
|
||||
if (c.hasNext()){
|
||||
K rv = transformer.transform(c.next());
|
||||
if (c.hasNext()){
|
||||
throw new NonUniqueResultException();
|
||||
}
|
||||
return rv;
|
||||
}else{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@ -20,6 +20,7 @@ import org.junit.Test;
|
||||
|
||||
import com.google.code.morphia.Datastore;
|
||||
import com.google.code.morphia.Morphia;
|
||||
import com.mysema.query.NonUniqueResultException;
|
||||
import com.mysema.query.SearchResults;
|
||||
import com.mysema.query.mongodb.domain.Address;
|
||||
import com.mysema.query.mongodb.domain.City;
|
||||
@ -59,6 +60,11 @@ public class MongodbQueryTest {
|
||||
public void UniqueResult(){
|
||||
assertEquals("Jantunen", where(user.firstName.eq("Jaakko")).uniqueResult().getLastName());
|
||||
}
|
||||
|
||||
@Test(expected=NonUniqueResultException.class)
|
||||
public void UniqueResultContract(){
|
||||
where(user.firstName.isNotNull()).uniqueResult();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void LongPath(){
|
||||
|
||||
@ -27,6 +27,7 @@ import com.mysema.commons.lang.IteratorAdapter;
|
||||
import com.mysema.query.DefaultQueryMetadata;
|
||||
import com.mysema.query.JoinExpression;
|
||||
import com.mysema.query.JoinFlag;
|
||||
import com.mysema.query.NonUniqueResultException;
|
||||
import com.mysema.query.QueryException;
|
||||
import com.mysema.query.QueryFlag;
|
||||
import com.mysema.query.QueryMetadata;
|
||||
@ -548,10 +549,18 @@ public abstract class AbstractSQLQuery<Q extends AbstractSQLQuery<Q>> extends
|
||||
|
||||
@Override
|
||||
public <RT> RT uniqueResult(Expression<RT> expr) {
|
||||
if (getMetadata().getModifiers().getLimit() == null
|
||||
&& !expr.toString().contains("count(")){
|
||||
limit(2);
|
||||
}
|
||||
CloseableIterator<RT> iterator = iterate(expr);
|
||||
try{
|
||||
if (iterator.hasNext()){
|
||||
return iterator.next();
|
||||
RT rv = iterator.next();
|
||||
if (iterator.hasNext()){
|
||||
throw new NonUniqueResultException();
|
||||
}
|
||||
return rv;
|
||||
}else{
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -29,7 +29,7 @@ public abstract class BeanPopulationBaseTest extends AbstractBaseTest{
|
||||
assertEquals(1l, update(e).populate(employee).where(e.id.eq(employee.getId())).execute());
|
||||
|
||||
// Query
|
||||
Employee smith = query().from(e).where(e.lastname.eq("Smith")).uniqueResult(e);
|
||||
Employee smith = query().from(e).where(e.lastname.eq("Smith")).limit(1).uniqueResult(e);
|
||||
assertEquals("John", smith.getFirstname());
|
||||
|
||||
// Delete (no changes needed)
|
||||
@ -50,18 +50,21 @@ public abstract class BeanPopulationBaseTest extends AbstractBaseTest{
|
||||
|
||||
// Query
|
||||
Employee smith = extQuery().from(e).where(e.lastname.eq("Smith"))
|
||||
.limit(1)
|
||||
.uniqueResult(Employee.class, e.lastname, e.firstname);
|
||||
assertEquals("John", smith.getFirstname());
|
||||
assertEquals("Smith", smith.getLastname());
|
||||
|
||||
// Query with alias
|
||||
smith = extQuery().from(e).where(e.lastname.eq("Smith"))
|
||||
.limit(1)
|
||||
.uniqueResult(Employee.class, e.lastname.as("lastname"), e.firstname.as("firstname"));
|
||||
assertEquals("John", smith.getFirstname());
|
||||
assertEquals("Smith", smith.getLastname());
|
||||
|
||||
// Query into custom type
|
||||
OtherEmployee other = extQuery().from(e).where(e.lastname.eq("Smith"))
|
||||
.limit(1)
|
||||
.uniqueResult(OtherEmployee.class, e.lastname, e.firstname);
|
||||
assertEquals("John", other.getFirstname());
|
||||
assertEquals("Smith", other.getLastname());
|
||||
|
||||
@ -807,7 +807,7 @@ public abstract class SelectBaseTest extends AbstractBaseTest{
|
||||
@Test
|
||||
public void Unique_Constructor_Projection(){
|
||||
// unique constructor projection
|
||||
IdName idAndName = query().from(survey).uniqueResult(new QIdName(survey.id, survey.name));
|
||||
IdName idAndName = query().from(survey).limit(1).uniqueResult(new QIdName(survey.id, survey.name));
|
||||
assertNotNull(idAndName);
|
||||
assertNotNull(idAndName.getId());
|
||||
assertNotNull(idAndName.getName());
|
||||
@ -817,7 +817,7 @@ public abstract class SelectBaseTest extends AbstractBaseTest{
|
||||
@Test
|
||||
public void Unique_Single(){
|
||||
// unique single
|
||||
String s = query().from(survey).uniqueResult(survey.name);
|
||||
String s = query().from(survey).limit(1).uniqueResult(survey.name);
|
||||
assertNotNull(s);
|
||||
|
||||
}
|
||||
@ -825,13 +825,18 @@ public abstract class SelectBaseTest extends AbstractBaseTest{
|
||||
@Test
|
||||
public void Unique_Wildcard(){
|
||||
// unique wildcard
|
||||
Object[] row = query().from(survey).uniqueResult(survey.all());
|
||||
Object[] row = query().from(survey).limit(1).uniqueResult(survey.all());
|
||||
assertNotNull(row);
|
||||
assertEquals(2, row.length);
|
||||
assertNotNull(row[0]);
|
||||
assertNotNull(row[1]);
|
||||
|
||||
}
|
||||
|
||||
@Test(expected=NonUniqueResultException.class)
|
||||
public void UniqueResultContract(){
|
||||
query().from(employee).uniqueResult(employee.all());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void Various() throws SQLException {
|
||||
@ -872,7 +877,7 @@ public abstract class SelectBaseTest extends AbstractBaseTest{
|
||||
@SkipForQuoted
|
||||
public void Wildcard_All() {
|
||||
expectedQuery = "select * from EMPLOYEE2 e";
|
||||
query().from(employee).uniqueResult(Wildcard.all);
|
||||
query().from(employee).list(Wildcard.all);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
Loading…
Reference in New Issue
Block a user