diff --git a/querydsl-core/src/main/java/com/mysema/query/support/NumberConversion.java b/querydsl-core/src/main/java/com/mysema/query/support/NumberConversion.java new file mode 100644 index 000000000..6146f5845 --- /dev/null +++ b/querydsl-core/src/main/java/com/mysema/query/support/NumberConversion.java @@ -0,0 +1,56 @@ +/* + * Copyright 2012, 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 + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.mysema.query.support; + +import java.util.Collections; +import java.util.List; + +import com.mysema.query.types.Expression; +import com.mysema.query.types.ExpressionBase; +import com.mysema.query.types.FactoryExpression; +import com.mysema.query.types.Visitor; +import com.mysema.util.MathUtils; + +/** + * @author tiwe + * + * @param + */ +public class NumberConversion extends ExpressionBase implements FactoryExpression { + + private static final long serialVersionUID = 7840412008633901748L; + + private final List> exprs; + + public NumberConversion(Expression expr) { + super(expr.getType()); + exprs = Collections.>singletonList(expr); + } + + @Override + public R accept(Visitor v, C context) { + return v.visit(this, context); + } + + @Override + public List> getArgs() { + return exprs; + } + + @Override + public T newInstance(Object... args) { + return (T)MathUtils.cast((Number)args[0], (Class)getType()); + } + +} diff --git a/querydsl-core/src/main/java/com/mysema/query/support/NumberConversions.java b/querydsl-core/src/main/java/com/mysema/query/support/NumberConversions.java new file mode 100644 index 000000000..9674f19dc --- /dev/null +++ b/querydsl-core/src/main/java/com/mysema/query/support/NumberConversions.java @@ -0,0 +1,61 @@ +/* + * Copyright 2012, 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 + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.mysema.query.support; + +import java.util.List; + +import com.mysema.query.types.Expression; +import com.mysema.query.types.ExpressionBase; +import com.mysema.query.types.FactoryExpression; +import com.mysema.query.types.Visitor; +import com.mysema.util.MathUtils; + +/** + * @author tiwe + * + * @param + */ +public class NumberConversions extends ExpressionBase implements FactoryExpression { + + private static final long serialVersionUID = -7834053123363933721L; + + private final FactoryExpression expr; + + public NumberConversions(FactoryExpression expr) { + super(expr.getType()); + this.expr = expr; + } + + @Override + public R accept(Visitor v, C context) { + return v.visit(this, context); + } + + @Override + public List> getArgs() { + return expr.getArgs(); + } + + @Override + public T newInstance(Object... args) { + for (int i = 0; i < args.length; i++) { + Class type = expr.getArgs().get(i).getType(); + if (args[i] instanceof Number && !args[i].getClass().equals(type)) { + args[i] = MathUtils.cast((Number)args[i], (Class)type); + } + } + return expr.newInstance(args); + } + +} diff --git a/querydsl-jpa/src/main/java/com/mysema/query/jpa/JPQLGrammar.java b/querydsl-jpa/src/main/java/com/mysema/query/jpa/JPQLGrammar.java index f64904f5c..a7d62fd04 100644 --- a/querydsl-jpa/src/main/java/com/mysema/query/jpa/JPQLGrammar.java +++ b/querydsl-jpa/src/main/java/com/mysema/query/jpa/JPQLGrammar.java @@ -19,8 +19,6 @@ import com.mysema.query.types.Expression; import com.mysema.query.types.Ops; import com.mysema.query.types.expr.ComparableExpression; import com.mysema.query.types.expr.ComparableOperation; -import com.mysema.query.types.expr.NumberExpression; -import com.mysema.query.types.expr.NumberOperation; import com.mysema.query.types.expr.StringExpression; import com.mysema.query.types.expr.StringOperation; @@ -46,30 +44,6 @@ public final class JPQLGrammar { public static > ComparableExpression min(CollectionExpression left) { return ComparableOperation.create((Class)left.getParameter(0), Ops.QuantOps.MIN_IN_COL, (Expression)left); } - - /** - * SUM returns Long when applied to state-fields of integral types (other - * than BigInteger); Double when applied to state-fields of floating point - * types; BigInteger when applied to state-fields of type BigInteger; and - * BigDecimal when applied to state-fields of type BigDecimal. - */ - public static > NumberExpression sum(Expression left) { - Class type = left.getType(); - if (type.equals(Byte.class) || type.equals(Integer.class) || type.equals(Short.class)) { - type = Long.class; - } else if (type.equals(Float.class)) { - type = Double.class; - } - return NumberOperation.create((Class) type, Ops.AggOps.SUM_AGG, left); - } - - public static > NumberExpression sumAsLong(Expression left) { - return sum(left).longValue(); - } - - public static > NumberExpression sumAsDouble(Expression left) { - return sum(left).doubleValue(); - } public static StringExpression type(EntityPath path) { return StringOperation.create(JPQLTemplates.TYPE, path); diff --git a/querydsl-jpa/src/main/java/com/mysema/query/jpa/JPQLQueryMixin.java b/querydsl-jpa/src/main/java/com/mysema/query/jpa/JPQLQueryMixin.java index 0babc8c07..54d379de7 100644 --- a/querydsl-jpa/src/main/java/com/mysema/query/jpa/JPQLQueryMixin.java +++ b/querydsl-jpa/src/main/java/com/mysema/query/jpa/JPQLQueryMixin.java @@ -23,10 +23,15 @@ import com.mysema.query.JoinFlag; import com.mysema.query.QueryMetadata; import com.mysema.query.support.Context; import com.mysema.query.support.ListAccessVisitor; +import com.mysema.query.support.NumberConversion; +import com.mysema.query.support.NumberConversions; import com.mysema.query.support.QueryMixin; import com.mysema.query.types.EntityPath; import com.mysema.query.types.Expression; import com.mysema.query.types.ExpressionUtils; +import com.mysema.query.types.FactoryExpression; +import com.mysema.query.types.Operation; +import com.mysema.query.types.Ops; import com.mysema.query.types.Path; import com.mysema.query.types.Predicate; import com.mysema.query.types.TemplateExpressionImpl; @@ -75,6 +80,32 @@ public class JPQLQueryMixin extends QueryMixin { } return getSelf(); } + + public Expression convert(Expression expr){ + if (isAggSumWithConversion(expr)) { + expr = new NumberConversion(expr); + } else if (expr instanceof FactoryExpression) { + FactoryExpression factorye = (FactoryExpression)expr; + for (Expression e : factorye.getArgs()) { + if (isAggSumWithConversion(e)) { + expr = new NumberConversions(factorye); + break; + } + } + + } + return super.convert(expr); + } + + private boolean isAggSumWithConversion(Expression expr) { + if (expr instanceof Operation && ((Operation)expr).getOperator() == Ops.AggOps.SUM_AGG) { + Class type = ((Operation)expr).getType(); + if (type.equals(Float.class) || type.equals(Integer.class) || type.equals(Short.class) || type.equals(Byte.class)) { + return true; + } + } + return false; + } @Override protected Predicate normalize(Predicate predicate, boolean where) { diff --git a/querydsl-jpa/src/main/java/com/mysema/query/jpa/JPQLSerializer.java b/querydsl-jpa/src/main/java/com/mysema/query/jpa/JPQLSerializer.java index b560776c8..16f3633c3 100644 --- a/querydsl-jpa/src/main/java/com/mysema/query/jpa/JPQLSerializer.java +++ b/querydsl-jpa/src/main/java/com/mysema/query/jpa/JPQLSerializer.java @@ -37,6 +37,7 @@ import javax.persistence.metamodel.SingularAttribute; import com.mysema.query.JoinExpression; import com.mysema.query.JoinType; import com.mysema.query.QueryMetadata; +import com.mysema.query.support.Expressions; import com.mysema.query.support.SerializerBase; import com.mysema.query.types.Constant; import com.mysema.query.types.ConstantImpl; 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 c0fbd3fdb..40a38be49 100644 --- a/querydsl-jpa/src/test/java/com/mysema/query/AbstractStandardTest.java +++ b/querydsl-jpa/src/test/java/com/mysema/query/AbstractStandardTest.java @@ -13,7 +13,6 @@ */ package com.mysema.query; -import static com.mysema.query.jpa.JPQLGrammar.sum; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; @@ -55,6 +54,7 @@ import com.mysema.query.jpa.domain.Company.Rating; import com.mysema.query.jpa.domain.DomesticCat; import com.mysema.query.jpa.domain.DoubleProjection; import com.mysema.query.jpa.domain.Employee; +import com.mysema.query.jpa.domain.FloatProjection; import com.mysema.query.jpa.domain.Foo; import com.mysema.query.jpa.domain.JobFunction; import com.mysema.query.jpa.domain.QAnimal; @@ -64,6 +64,7 @@ import com.mysema.query.jpa.domain.QCat; import com.mysema.query.jpa.domain.QCompany; import com.mysema.query.jpa.domain.QDoubleProjection; import com.mysema.query.jpa.domain.QEmployee; +import com.mysema.query.jpa.domain.QFloatProjection; import com.mysema.query.jpa.domain.QFoo; import com.mysema.query.jpa.domain.QShow; import com.mysema.query.jpa.domain.QUser; @@ -709,14 +710,14 @@ public abstract class AbstractStandardTest { @Ignore public void Sum() throws RecognitionException, TokenStreamException { // NOT SUPPORTED - query().from(cat).list(sum(cat.kittens.size())); + query().from(cat).list(cat.kittens.size().sum()); } @Test @Ignore public void Sum_2() throws RecognitionException, TokenStreamException { // NOT SUPPORTED - query().from(cat).where(sum(cat.kittens.size()).gt(0)).list(cat); + query().from(cat).where(cat.kittens.size().sum().gt(0)).list(cat); } @Test @@ -731,6 +732,19 @@ public abstract class AbstractStandardTest { assertEquals(val, projection.val, 0.001); } + @Test + public void Sum_as_Float() { + float val = query().from(cat).uniqueResult(cat.floatProperty.sum()); + assertTrue(val > 0); + } + + @Test + public void Sum_as_Float_Projected() { + float val = query().from(cat).uniqueResult(cat.floatProperty.sum()); + FloatProjection projection = query().from(cat).uniqueResult(new QFloatProjection(cat.floatProperty.sum())); + assertEquals(val, projection.val, 0.001); + } + @Test public void Substring() { for (String str : query().from(cat).list(cat.name.substring(1,2))) { diff --git a/querydsl-jpa/src/test/java/com/mysema/query/jpa/FeaturesTest.java b/querydsl-jpa/src/test/java/com/mysema/query/jpa/FeaturesTest.java index 80d3e70fd..162d50a2d 100644 --- a/querydsl-jpa/src/test/java/com/mysema/query/jpa/FeaturesTest.java +++ b/querydsl-jpa/src/test/java/com/mysema/query/jpa/FeaturesTest.java @@ -13,19 +13,14 @@ */ package com.mysema.query.jpa; -import static com.mysema.query.jpa.JPQLGrammar.sum; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; -import java.math.BigDecimal; -import java.math.BigInteger; - import org.junit.Test; import com.mysema.query.jpa.domain.QAccount; import com.mysema.query.jpa.domain.QInheritedProperties; -import com.mysema.query.types.expr.NumberExpression; import com.mysema.query.types.path.NumberPath; public class FeaturesTest extends AbstractQueryTest { @@ -139,35 +134,35 @@ public class FeaturesTest extends AbstractQueryTest { // toString("distinct cat.bodyWeight", distinct(cat.bodyWeight)); } - /** - * specs : - * http://opensource.atlassian.com/projects/hibernate/browse/HHH-1538 - */ - @SuppressWarnings("unchecked") - @Test - public void Bug326650() { - assertEquals(Long.class, sum(var(Byte.class)).getType()); - assertEquals(Long.class, sum(var(Short.class)).getType()); - assertEquals(Long.class, sum(var(Integer.class)).getType()); - assertEquals(Long.class, sum(var(Long.class)).getType()); - - assertEquals(Double.class, sum(var(Float.class)).getType()); - assertEquals(Double.class, sum(var(Double.class)).getType()); - - assertEquals(BigInteger.class, sum(var(BigInteger.class)).getType()); - assertEquals(BigDecimal.class, sum(var(BigDecimal.class)).getType()); - - // sum to var - NumberExpression sum = (NumberExpression) sum(var(Integer.class)); // via Java level cast - sum = sum(var(Integer.class)).longValue(); - assertNotNull(sum); - - // sum comparison - - sum(var(Integer.class)).gt(0); - sum(var(Integer.class)).intValue().gt(0); - - } +// /** +// * specs : +// * http://opensource.atlassian.com/projects/hibernate/browse/HHH-1538 +// */ +// @SuppressWarnings("unchecked") +// @Test +// public void Bug326650() { +// assertEquals(Long.class, sum(var(Byte.class)).getType()); +// assertEquals(Long.class, sum(var(Short.class)).getType()); +// assertEquals(Long.class, sum(var(Integer.class)).getType()); +// assertEquals(Long.class, sum(var(Long.class)).getType()); +// +// assertEquals(Double.class, sum(var(Float.class)).getType()); +// assertEquals(Double.class, sum(var(Double.class)).getType()); +// +// assertEquals(BigInteger.class, sum(var(BigInteger.class)).getType()); +// assertEquals(BigDecimal.class, sum(var(BigDecimal.class)).getType()); +// +// // sum to var +// NumberExpression sum = (NumberExpression) sum(var(Integer.class)); // via Java level cast +// sum = sum(var(Integer.class)).longValue(); +// assertNotNull(sum); +// +// // sum comparison +// +// sum(var(Integer.class)).gt(0); +// sum(var(Integer.class)).intValue().gt(0); +// +// } private > NumberPath var(Class cl){ return new NumberPath(cl, "var"); diff --git a/querydsl-jpa/src/test/java/com/mysema/query/jpa/ParsingTest.java b/querydsl-jpa/src/test/java/com/mysema/query/jpa/ParsingTest.java index c7bc39602..38c510974 100644 --- a/querydsl-jpa/src/test/java/com/mysema/query/jpa/ParsingTest.java +++ b/querydsl-jpa/src/test/java/com/mysema/query/jpa/ParsingTest.java @@ -15,7 +15,6 @@ package com.mysema.query.jpa; import static com.mysema.query.alias.Alias.$; import static com.mysema.query.alias.Alias.alias; -import static com.mysema.query.jpa.JPQLGrammar.sum; import static org.junit.Assert.assertEquals; import org.junit.Ignore; @@ -70,7 +69,7 @@ public class ParsingTest extends AbstractQueryTest{ public void DocoExamples910() throws Exception { query().from(cat) .groupBy(cat.color) - .select(cat.color, sum(cat.weight), cat.count()).parse(); + .select(cat.color, cat.weight.sum(), cat.count()).parse(); } @Test @@ -78,7 +77,7 @@ public class ParsingTest extends AbstractQueryTest{ query().from(cat) .groupBy(cat.color) .having(cat.color.in(Color.TABBY, Color.BLACK)) - .select(cat.color, sum(cat.weight), cat.count()).parse(); + .select(cat.color, cat.weight.sum(), cat.count()).parse(); } @Test @@ -87,7 +86,7 @@ public class ParsingTest extends AbstractQueryTest{ query().from(cat).join(cat.kittens, kitten) .groupBy(cat) .having(kitten.weight.avg().gt(100.0)) - .orderBy(kitten.count().asc(), sum(kitten.weight).desc()) + .orderBy(kitten.count().asc(), kitten.weight.sum().desc()) .select(cat) .parse(); } @@ -139,9 +138,9 @@ public class ParsingTest extends AbstractQueryTest{ sub().from(catalog).where( catalog.effectiveDate.lt(DateExpression.currentDate())) .list(catalog.effectiveDate)))) - .groupBy(ord).having(sum(price.amount).gt(0l)) - .orderBy(sum(price.amount).desc()) - .select(ord.id, sum(price.amount), item.count()); + .groupBy(ord).having(price.amount.sum().gt(0l)) + .orderBy(price.amount.sum().desc()) + .select(ord.id, price.amount.sum(), item.count()); Customer c1 = new Customer(); Catalog c2 = new Catalog(); @@ -151,9 +150,9 @@ public class ParsingTest extends AbstractQueryTest{ .from(catalog).join(catalog.prices, price).where( ord.paid.not().and(ord.customer.eq(c1)).and( price.product.eq(product)).and(catalog.eq(c2))) - .groupBy(ord).having(sum(price.amount).gt(0l)) - .orderBy(sum(price.amount).desc()) - .select(ord.id, sum(price.amount), item.count()); + .groupBy(ord).having(price.amount.sum().gt(0l)) + .orderBy(price.amount.sum().desc()) + .select(ord.id, price.amount.sum(), item.count()); } @@ -550,14 +549,14 @@ public class ParsingTest extends AbstractQueryTest{ @Ignore public void Sum() throws RecognitionException, TokenStreamException { // NOT SUPPORTED - query().from(cat).select(sum(cat.kittens.size())).parse(); + query().from(cat).select(cat.kittens.size().sum()).parse(); } @Test @Ignore public void Sum_2() throws RecognitionException, TokenStreamException { // NOT SUPPORTED - query().from(cat).where(sum(cat.kittens.size()).gt(0)).select(cat).parse(); + query().from(cat).where(cat.kittens.size().sum().gt(0)).select(cat).parse(); } @Test diff --git a/querydsl-jpa/src/test/java/com/mysema/query/jpa/domain/Animal.java b/querydsl-jpa/src/test/java/com/mysema/query/jpa/domain/Animal.java index 1833f0c0f..cd079ec29 100644 --- a/querydsl-jpa/src/test/java/com/mysema/query/jpa/domain/Animal.java +++ b/querydsl-jpa/src/test/java/com/mysema/query/jpa/domain/Animal.java @@ -41,6 +41,8 @@ public class Animal { // needed for JPA tests @Type(type="com.mysema.query.ExtDoubleType") private double bodyWeight; + + private float floatProperty; private Color color; @@ -140,4 +142,12 @@ public class Animal { this.weight = weight; } + public float getFloatProperty() { + return floatProperty; + } + + public void setFloatProperty(float floatProperty) { + this.floatProperty = floatProperty; + } + } diff --git a/querydsl-jpa/src/test/java/com/mysema/query/jpa/domain/Cat.java b/querydsl-jpa/src/test/java/com/mysema/query/jpa/domain/Cat.java index aaa98f814..61a6389b6 100644 --- a/querydsl-jpa/src/test/java/com/mysema/query/jpa/domain/Cat.java +++ b/querydsl-jpa/src/test/java/com/mysema/query/jpa/domain/Cat.java @@ -67,6 +67,7 @@ public class Cat extends Animal { public Cat(String name, int id, double bodyWeight){ this(name, id); setBodyWeight(bodyWeight); + setFloatProperty((float)bodyWeight); } public int getBreed() { diff --git a/querydsl-jpa/src/test/java/com/mysema/query/jpa/domain/FloatProjection.java b/querydsl-jpa/src/test/java/com/mysema/query/jpa/domain/FloatProjection.java new file mode 100644 index 000000000..049df79d2 --- /dev/null +++ b/querydsl-jpa/src/test/java/com/mysema/query/jpa/domain/FloatProjection.java @@ -0,0 +1,14 @@ +package com.mysema.query.jpa.domain; + +import com.mysema.query.annotations.QueryProjection; + +public class FloatProjection { + + public float val; + + @QueryProjection + public FloatProjection(float val) { + this.val = val; + } + +}