Add enum conversion #575

This commit is contained in:
Timo Westkämper 2013-11-29 15:12:36 +02:00
parent 3204eb8d63
commit 866523a4f1
12 changed files with 189 additions and 27 deletions

View File

@ -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 <T>
*/
public class EnumConversion<T> extends ExpressionBase<T> implements FactoryExpression<T> {
private static final long serialVersionUID = 7840412008633901748L;
private final List<Expression<?>> exprs;
private final Enum<?>[] values;
public EnumConversion(Expression<T> expr) {
super(expr.getType());
exprs = Collections.<Expression<?>>singletonList(expr);
try {
values = (Enum<?>[]) expr.getType().getMethod("values").invoke(null);
} catch (Exception e) {
throw new RuntimeException(e.getMessage(), e);
}
}
@Override
public <R, C> R accept(Visitor<R, C> v, C context) {
return v.visit(this, context);
}
@Override
public List<Expression<?>> 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;
}
}
}

View File

@ -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<T> extends ExpressionBase<T> implements FactoryEx
private final FactoryExpression<T> expr;
private final Map<Class<?>, Enum<?>[]> values = Maps.newHashMap();
public NumberConversions(FactoryExpression<T> expr) {
super(expr.getType());
this.expr = expr;
@ -50,11 +54,30 @@ public class NumberConversions<T> extends ExpressionBase<T> implements FactoryEx
return expr.getArgs();
}
private <E extends Enum<E>> Enum<E>[] getValues(Class<E> enumClass) {
Enum<E>[] values = (Enum<E>[]) this.values.get(enumClass);
if (values == null) {
try {
values = (Enum<E>[]) 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)){

View File

@ -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> color = new EnumPath<Color>(Color.class, "path");
EnumConversion<Color> conv = new EnumConversion<Color>(color);
assertEquals(Color.BLUE, conv.newInstance("BLUE"));
}
@Test
public void Ordinal() {
EnumPath<Color> color = new EnumPath<Color>(Color.class, "path");
EnumConversion<Color> conv = new EnumConversion<Color>(color);
assertEquals(Color.RED, conv.newInstance(2));
}
}

View File

@ -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> color = new EnumPath<Color>(Color.class, "path");
QTuple qTuple = new QTuple(color);
NumberConversions<Tuple> conversions = new NumberConversions<Tuple>(qTuple);
assertEquals(Color.BLUE, conversions.newInstance("BLUE").get(color));
}
@Test
public void Ordinal() {
EnumPath<Color> color = new EnumPath<Color>(Color.class, "path");
QTuple qTuple = new QTuple(color);
NumberConversions<Tuple> conversions = new NumberConversions<Tuple>(qTuple);
assertEquals(Color.RED, conversions.newInstance(2).get(color));
}
@Test
public void Safe_Number_Conversion() {
StringPath strPath = new StringPath("strPath");

View File

@ -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<RT>(expr);
} else if (Enum.class.isAssignableFrom(expr.getType())) {
return new EnumConversion<RT>(expr);
} else if (expr instanceof FactoryExpression) {
FactoryExpression<RT> factorye = (FactoryExpression<RT>)expr;
for (Expression<?> e : factorye.getArgs()) {
if (Number.class.isAssignableFrom(e.getType())) {
return new NumberConversions<RT>(factorye);
} else if (Enum.class.isAssignableFrom(e.getType())) {
return new NumberConversions<RT>(factorye);
}
}
}

View File

@ -114,7 +114,8 @@ public abstract class AbstractHibernateSQLQuery<Q extends AbstractHibernateSQLQu
HibernateUtil.setConstants(query, constants, queryMixin.getMetadata().getParams());
// set entity paths
List<? extends Expression<?>> 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) {

View File

@ -111,7 +111,8 @@ public abstract class AbstractJPASQLQuery<Q extends AbstractJPASQLQuery<Q> & com
logQuery(queryString);
List<? extends Expression<?>> 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 {

View File

@ -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;

View File

@ -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<Cat> 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<Cat> 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<Integer> sq1 = sq().from(cat).unique(cat.id.max());
SubQueryExpression<Integer> sq2 = sq().from(cat).unique(cat.id.min());
List<Integer> 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<Integer> sq1 = sq().from(cat).unique(cat.id.max());
SubQueryExpression<Integer> sq2 = sq().from(cat).unique(cat.id.min());
List<Integer> 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<Tuple> 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<Tuple> 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<Tuple> 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<Tuple> rows = query().from(cat).list(cat.all());
assertEquals(6, rows.size());
print(rows);

View File

@ -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();
}
}

View File

@ -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();
}
}

View File

@ -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<Cat> k) {
setId(id);
setName( name);