From 866523a4f112eaad01e70f35d58d2866f3d1119e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20Westk=C3=A4mper?= Date: Fri, 29 Nov 2013 15:12:36 +0200 Subject: [PATCH] Add enum conversion #575 --- .../mysema/query/support/EnumConversion.java | 75 +++++++++++++++++++ .../query/support/NumberConversions.java | 25 ++++++- .../query/support/EnumConversionTest.java | 26 +++++++ .../query/support/NumberConversionsTest.java | 20 +++++ .../com/mysema/query/jpa/Conversions.java | 5 ++ .../sql/AbstractHibernateSQLQuery.java | 3 +- .../query/jpa/sql/AbstractJPASQLQuery.java | 3 +- .../com/mysema/query/AbstractJPATest.java | 2 + .../com/mysema/query/AbstractSQLTest.java | 25 ++++--- .../com/mysema/query/HibernateSQLBase.java | 13 ++-- .../java/com/mysema/query/JPASQLBase.java | 13 ++-- .../java/com/mysema/query/jpa/domain/Cat.java | 6 ++ 12 files changed, 189 insertions(+), 27 deletions(-) create mode 100644 querydsl-core/src/main/java/com/mysema/query/support/EnumConversion.java create mode 100644 querydsl-core/src/test/java/com/mysema/query/support/EnumConversionTest.java diff --git a/querydsl-core/src/main/java/com/mysema/query/support/EnumConversion.java b/querydsl-core/src/main/java/com/mysema/query/support/EnumConversion.java new file mode 100644 index 000000000..bada8fcf0 --- /dev/null +++ b/querydsl-core/src/main/java/com/mysema/query/support/EnumConversion.java @@ -0,0 +1,75 @@ +/* + * 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; + +/** + * EnumConversion ensures that the results of an enum projection confirm to the type of the + * projection expression + * + * @author tiwe + * + * @param + */ +public class EnumConversion extends ExpressionBase implements FactoryExpression { + + private static final long serialVersionUID = 7840412008633901748L; + + private final List> exprs; + + private final Enum[] values; + + public EnumConversion(Expression expr) { + super(expr.getType()); + exprs = Collections.>singletonList(expr); + try { + values = (Enum[]) expr.getType().getMethod("values").invoke(null); + } catch (Exception e) { + throw new RuntimeException(e.getMessage(), e); + } + } + + @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) { + if (args[0] != null) { + if (args[0] instanceof String) { + return (T)Enum.valueOf((Class)getType(), (String)args[0]); + } else if (args[0] instanceof Number) { + return (T)values[((Number)args[0]).intValue()]; + } else { + return (T)args[0]; + } + } else { + return null; + } + } + +} 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 index 4aeb0b015..1458a60b3 100644 --- a/querydsl-core/src/main/java/com/mysema/query/support/NumberConversions.java +++ b/querydsl-core/src/main/java/com/mysema/query/support/NumberConversions.java @@ -14,7 +14,9 @@ package com.mysema.query.support; import java.util.List; +import java.util.Map; +import com.google.common.collect.Maps; import com.mysema.query.types.Expression; import com.mysema.query.types.ExpressionBase; import com.mysema.query.types.FactoryExpression; @@ -35,6 +37,8 @@ public class NumberConversions extends ExpressionBase implements FactoryEx private final FactoryExpression expr; + private final Map, Enum[]> values = Maps.newHashMap(); + public NumberConversions(FactoryExpression expr) { super(expr.getType()); this.expr = expr; @@ -50,11 +54,30 @@ public class NumberConversions extends ExpressionBase implements FactoryEx return expr.getArgs(); } + private > Enum[] getValues(Class enumClass) { + Enum[] values = (Enum[]) this.values.get(enumClass); + if (values == null) { + try { + values = (Enum[]) enumClass.getMethod("values").invoke(null); + this.values.put(enumClass, values); + } catch (Exception e) { + throw new RuntimeException(e.getMessage(), e); + } + } + return values; + } + @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)) { + if (Enum.class.isAssignableFrom(type) && !type.isInstance(args[i])) { + if (args[i] instanceof String) { + args[i] = Enum.valueOf((Class)type, (String)args[i]); + } else if (args[i] instanceof Number) { + args[i] = getValues((Class)type)[((Number)args[i]).intValue()]; + } + } else if (args[i] instanceof Number && !type.isInstance(args[i])) { if (type.equals(Boolean.class)) { args[i] = ((Number)args[i]).intValue() > 0; } else if (Number.class.isAssignableFrom(type)){ diff --git a/querydsl-core/src/test/java/com/mysema/query/support/EnumConversionTest.java b/querydsl-core/src/test/java/com/mysema/query/support/EnumConversionTest.java new file mode 100644 index 000000000..b82f5c00a --- /dev/null +++ b/querydsl-core/src/test/java/com/mysema/query/support/EnumConversionTest.java @@ -0,0 +1,26 @@ +package com.mysema.query.support; + +import static org.junit.Assert.assertEquals; + +import org.junit.Test; + +import com.mysema.query.types.path.EnumPath; + +public class EnumConversionTest { + + public enum Color {GREEN, BLUE, RED, YELLOW, BLACK, WHITE} + + @Test + public void Name() { + EnumPath color = new EnumPath(Color.class, "path"); + EnumConversion conv = new EnumConversion(color); + assertEquals(Color.BLUE, conv.newInstance("BLUE")); + } + + @Test + public void Ordinal() { + EnumPath color = new EnumPath(Color.class, "path"); + EnumConversion conv = new EnumConversion(color); + assertEquals(Color.RED, conv.newInstance(2)); + } +} diff --git a/querydsl-core/src/test/java/com/mysema/query/support/NumberConversionsTest.java b/querydsl-core/src/test/java/com/mysema/query/support/NumberConversionsTest.java index d3002be41..5a3197ab5 100644 --- a/querydsl-core/src/test/java/com/mysema/query/support/NumberConversionsTest.java +++ b/querydsl-core/src/test/java/com/mysema/query/support/NumberConversionsTest.java @@ -7,11 +7,31 @@ import org.junit.Test; import com.mysema.query.Tuple; import com.mysema.query.types.QTuple; +import com.mysema.query.types.path.EnumPath; import com.mysema.query.types.path.NumberPath; import com.mysema.query.types.path.StringPath; public class NumberConversionsTest { + + public enum Color {GREEN, BLUE, RED, YELLOW, BLACK, WHITE} + + @Test + public void Name() { + EnumPath color = new EnumPath(Color.class, "path"); + QTuple qTuple = new QTuple(color); + NumberConversions conversions = new NumberConversions(qTuple); + assertEquals(Color.BLUE, conversions.newInstance("BLUE").get(color)); + } + + @Test + public void Ordinal() { + EnumPath color = new EnumPath(Color.class, "path"); + QTuple qTuple = new QTuple(color); + NumberConversions conversions = new NumberConversions(qTuple); + assertEquals(Color.RED, conversions.newInstance(2).get(color)); + } + @Test public void Safe_Number_Conversion() { StringPath strPath = new StringPath("strPath"); diff --git a/querydsl-jpa/src/main/java/com/mysema/query/jpa/Conversions.java b/querydsl-jpa/src/main/java/com/mysema/query/jpa/Conversions.java index 95ec90780..2c62ebdc7 100644 --- a/querydsl-jpa/src/main/java/com/mysema/query/jpa/Conversions.java +++ b/querydsl-jpa/src/main/java/com/mysema/query/jpa/Conversions.java @@ -15,6 +15,7 @@ package com.mysema.query.jpa; import javax.persistence.Entity; +import com.mysema.query.support.EnumConversion; import com.mysema.query.support.NumberConversion; import com.mysema.query.support.NumberConversions; import com.mysema.query.types.Expression; @@ -59,11 +60,15 @@ public final class Conversions { } } else if (Number.class.isAssignableFrom(expr.getType())) { return new NumberConversion(expr); + } else if (Enum.class.isAssignableFrom(expr.getType())) { + return new EnumConversion(expr); } else if (expr instanceof FactoryExpression) { FactoryExpression factorye = (FactoryExpression)expr; for (Expression e : factorye.getArgs()) { if (Number.class.isAssignableFrom(e.getType())) { return new NumberConversions(factorye); + } else if (Enum.class.isAssignableFrom(e.getType())) { + return new NumberConversions(factorye); } } } diff --git a/querydsl-jpa/src/main/java/com/mysema/query/jpa/hibernate/sql/AbstractHibernateSQLQuery.java b/querydsl-jpa/src/main/java/com/mysema/query/jpa/hibernate/sql/AbstractHibernateSQLQuery.java index dfef1f03b..033fef1fb 100644 --- a/querydsl-jpa/src/main/java/com/mysema/query/jpa/hibernate/sql/AbstractHibernateSQLQuery.java +++ b/querydsl-jpa/src/main/java/com/mysema/query/jpa/hibernate/sql/AbstractHibernateSQLQuery.java @@ -114,7 +114,8 @@ public abstract class AbstractHibernateSQLQuery> projection = queryMixin.getMetadata().getProjection(); - if (projection.get(0) instanceof EntityPath || projection.get(0).getType().isAnnotationPresent(Entity.class)) { + if (!FactoryExpression.class.isAssignableFrom(projection.get(0).getClass()) && + (projection.get(0) instanceof EntityPath || projection.get(0).getType().isAnnotationPresent(Entity.class))) { if (projection.size() == 1) { Expression expr = projection.get(0); if (expr instanceof Operation) { diff --git a/querydsl-jpa/src/main/java/com/mysema/query/jpa/sql/AbstractJPASQLQuery.java b/querydsl-jpa/src/main/java/com/mysema/query/jpa/sql/AbstractJPASQLQuery.java index 8f5b0a206..66ebba13a 100644 --- a/querydsl-jpa/src/main/java/com/mysema/query/jpa/sql/AbstractJPASQLQuery.java +++ b/querydsl-jpa/src/main/java/com/mysema/query/jpa/sql/AbstractJPASQLQuery.java @@ -111,7 +111,8 @@ public abstract class AbstractJPASQLQuery & com logQuery(queryString); List> projection = queryMixin.getMetadata().getProjection(); Query query; - if (projection.get(0) instanceof EntityPath || projection.get(0).getType().isAnnotationPresent(Entity.class)) { + if (!FactoryExpression.class.isAssignableFrom(projection.get(0).getClass()) && + (projection.get(0) instanceof EntityPath || projection.get(0).getType().isAnnotationPresent(Entity.class))) { if (projection.size() == 1) { query = entityManager.createNativeQuery(queryString, projection.get(0).getType()); } else { diff --git a/querydsl-jpa/src/test/java/com/mysema/query/AbstractJPATest.java b/querydsl-jpa/src/test/java/com/mysema/query/AbstractJPATest.java index de9df8118..103bad167 100644 --- a/querydsl-jpa/src/test/java/com/mysema/query/AbstractJPATest.java +++ b/querydsl-jpa/src/test/java/com/mysema/query/AbstractJPATest.java @@ -56,6 +56,7 @@ import com.mysema.query.jpa.domain.Animal; import com.mysema.query.jpa.domain.Author; import com.mysema.query.jpa.domain.Book; import com.mysema.query.jpa.domain.Cat; +import com.mysema.query.jpa.domain.Color; import com.mysema.query.jpa.domain.Company; import com.mysema.query.jpa.domain.Company.Rating; import com.mysema.query.jpa.domain.DomesticCat; @@ -188,6 +189,7 @@ public abstract class AbstractJPATest { cat.setBirthdate(birthDate); cat.setDateField(date); cat.setTimeField(time); + cat.setColor(Color.BLACK); save(cat); savedCats.add(cat); prev = cat; diff --git a/querydsl-jpa/src/test/java/com/mysema/query/AbstractSQLTest.java b/querydsl-jpa/src/test/java/com/mysema/query/AbstractSQLTest.java index 2cb5ca6a7..58b138e46 100644 --- a/querydsl-jpa/src/test/java/com/mysema/query/AbstractSQLTest.java +++ b/querydsl-jpa/src/test/java/com/mysema/query/AbstractSQLTest.java @@ -16,11 +16,13 @@ import org.junit.Test; import com.mysema.query.jpa.AbstractSQLQuery; import com.mysema.query.jpa.domain.Cat; +import com.mysema.query.jpa.domain.Color; import com.mysema.query.jpa.domain.QCat; import com.mysema.query.jpa.domain.sql.SAnimal; import com.mysema.query.sql.SQLSubQuery; import com.mysema.query.types.ConstructorExpression; import com.mysema.query.types.Expression; +import com.mysema.query.types.Projections; import com.mysema.query.types.SubQueryExpression; import com.mysema.query.types.expr.DateExpression; import com.mysema.query.types.expr.Wildcard; @@ -52,12 +54,20 @@ public abstract class AbstractSQLTest { assertEquals(6l, query().from(cat).where(cat.dtype.eq("C")).distinct().count()); } + @Test + public void Enum_Binding() { + List cats = query().from(cat) + .list(Projections.bean(Cat.class, QCat.cat.color)); + assertFalse(cats.isEmpty()); + + for (Cat cat : cats) { + assertEquals(Color.BLACK, cat.getColor()); + } + } + @Test @Ignore public void EntityProjections() { - // not yet supported - SAnimal cat = new SAnimal("cat"); - List cats = query().from(cat).orderBy(cat.name.asc()) .list(ConstructorExpression.create(Cat.class, cat.name, cat.id)); assertEquals(6, cats.size()); @@ -68,7 +78,6 @@ public abstract class AbstractSQLTest { @Test public void EntityQueries() { - SAnimal cat = new SAnimal("cat"); SAnimal mate = new SAnimal("mate"); QCat catEntity = QCat.cat; @@ -172,7 +181,6 @@ public abstract class AbstractSQLTest { @Test public void Null_As_UniqueResult() { - SAnimal cat = new SAnimal("cat"); assertNull(query().from(cat).where(cat.name.eq(UUID.randomUUID().toString())) .uniqueResult(cat.name)); } @@ -196,7 +204,6 @@ public abstract class AbstractSQLTest { @Test @SuppressWarnings("unchecked") public void Union() throws SQLException { - SAnimal cat = new SAnimal("cat"); SubQueryExpression sq1 = sq().from(cat).unique(cat.id.max()); SubQueryExpression sq2 = sq().from(cat).unique(cat.id.min()); List list = query().union(sq1, sq2).list(); @@ -206,7 +213,6 @@ public abstract class AbstractSQLTest { @Test @SuppressWarnings("unchecked") public void Union_All() { - SAnimal cat = new SAnimal("cat"); SubQueryExpression sq1 = sq().from(cat).unique(cat.id.max()); SubQueryExpression sq2 = sq().from(cat).unique(cat.id.min()); List list = query().unionAll(sq1, sq2).list(); @@ -216,7 +222,6 @@ public abstract class AbstractSQLTest { @Test @ExcludeIn({Target.DERBY, Target.POSTGRES}) public void Union2() { - SAnimal cat = new SAnimal("cat"); List rows = query().union( new SQLSubQuery().from(cat).where(cat.name.eq("Beck")).distinct().list(cat.name, cat.id), new SQLSubQuery().from(cat).where(cat.name.eq("Kate")).distinct().list(cat.name, null)) @@ -231,7 +236,6 @@ public abstract class AbstractSQLTest { @Test @ExcludeIn(Target.DERBY) public void Union3() { - SAnimal cat = new SAnimal("cat"); SAnimal cat2 = new SAnimal("cat2"); List rows = query().union( new SQLSubQuery().from(cat).innerJoin(cat2).on(cat2.id.eq(cat.id)).list(cat.id, cat2.id), @@ -259,7 +263,6 @@ public abstract class AbstractSQLTest { @Test @ExcludeIn({Target.DERBY, Target.ORACLE}) public void Union5() { - SAnimal cat = new SAnimal("cat"); SAnimal cat2 = new SAnimal("cat2"); List rows = query().union( new SQLSubQuery().from(cat).join(cat2).on(cat2.id.eq(cat.id.add(1))).list(cat.id, cat2.id), @@ -287,8 +290,6 @@ public abstract class AbstractSQLTest { @Test @ExcludeIn(Target.H2) public void Wildcard() { - SAnimal cat = new SAnimal("cat"); - List rows = query().from(cat).list(cat.all()); assertEquals(6, rows.size()); print(rows); diff --git a/querydsl-jpa/src/test/java/com/mysema/query/HibernateSQLBase.java b/querydsl-jpa/src/test/java/com/mysema/query/HibernateSQLBase.java index 3eb615d60..f218c54cc 100644 --- a/querydsl-jpa/src/test/java/com/mysema/query/HibernateSQLBase.java +++ b/querydsl-jpa/src/test/java/com/mysema/query/HibernateSQLBase.java @@ -24,6 +24,7 @@ import org.junit.rules.MethodRule; import org.junit.runner.RunWith; import com.mysema.query.jpa.domain.Cat; +import com.mysema.query.jpa.domain.Color; import com.mysema.query.jpa.domain.QCat; import com.mysema.query.jpa.domain.sql.SAnimal; import com.mysema.query.jpa.hibernate.sql.HibernateSQLQuery; @@ -55,12 +56,12 @@ public class HibernateSQLBase extends AbstractSQLTest { @Before public void setUp() { if (query().from(cat).notExists()) { - session.save(new Cat("Beck",1)); - session.save(new Cat("Kate",2)); - session.save(new Cat("Kitty",3)); - session.save(new Cat("Bobby",4)); - session.save(new Cat("Harold",5)); - session.save(new Cat("Tim",6)); + session.save(new Cat("Beck", 1, Color.BLACK)); + session.save(new Cat("Kate", 2, Color.BLACK)); + session.save(new Cat("Kitty", 3, Color.BLACK)); + session.save(new Cat("Bobby", 4, Color.BLACK)); + session.save(new Cat("Harold", 5, Color.BLACK)); + session.save(new Cat("Tim", 6, Color.BLACK)); session.flush(); } } diff --git a/querydsl-jpa/src/test/java/com/mysema/query/JPASQLBase.java b/querydsl-jpa/src/test/java/com/mysema/query/JPASQLBase.java index eaa8a9775..006a1f186 100644 --- a/querydsl-jpa/src/test/java/com/mysema/query/JPASQLBase.java +++ b/querydsl-jpa/src/test/java/com/mysema/query/JPASQLBase.java @@ -25,6 +25,7 @@ import org.junit.rules.MethodRule; import org.junit.runner.RunWith; import com.mysema.query.jpa.domain.Cat; +import com.mysema.query.jpa.domain.Color; import com.mysema.query.jpa.domain.QCat; import com.mysema.query.jpa.domain.sql.SAnimal; import com.mysema.query.jpa.sql.JPASQLQuery; @@ -58,12 +59,12 @@ public class JPASQLBase extends AbstractSQLTest { @Before public void setUp() { if (query().from(cat).notExists()) { - entityManager.persist(new Cat("Beck", 1)); - entityManager.persist(new Cat("Kate", 2)); - entityManager.persist(new Cat("Kitty", 3)); - entityManager.persist(new Cat("Bobby", 4)); - entityManager.persist(new Cat("Harold", 5)); - entityManager.persist(new Cat("Tim", 6)); + entityManager.persist(new Cat("Beck", 1, Color.BLACK)); + entityManager.persist(new Cat("Kate", 2, Color.BLACK)); + entityManager.persist(new Cat("Kitty", 3, Color.BLACK)); + entityManager.persist(new Cat("Bobby", 4, Color.BLACK)); + entityManager.persist(new Cat("Harold", 5, Color.BLACK)); + entityManager.persist(new Cat("Tim", 6, Color.BLACK)); entityManager.flush(); } } 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 5e5d9aaf0..000612565 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 @@ -65,6 +65,12 @@ public class Cat extends Animal { setName( name); } + public Cat(String name, int id, Color color) { + setId(id); + setName( name); + setColor(color); + } + public Cat(String name, int id, List k) { setId(id); setName( name);