#176 improved collection.any() serialization

This commit is contained in:
Timo Westkämper 2012-06-22 01:08:26 +03:00
parent 6bb37746af
commit 80264eccc7
9 changed files with 104 additions and 67 deletions

View File

@ -14,6 +14,8 @@
package com.mysema.query.support;
import java.util.UUID;
import com.mysema.query.types.Constant;
import com.mysema.query.types.EntityPath;
import com.mysema.query.types.Expression;
@ -118,9 +120,10 @@ public class CollectionAnyVisitor implements Visitor<Expression<?>,Context>{
@SuppressWarnings("unchecked")
@Override
public Expression<?> visit(Path<?> expr, Context context) {
if (expr.getMetadata().getPathType() == PathType.COLLECTION_ANY){
if (expr.getMetadata().getPathType() == PathType.COLLECTION_ANY){
String variable = expr.accept(ToStringVisitor.DEFAULT, TEMPLATE).replace('.', '_');
EntityPath<?> replacement = new EntityPathBase(expr.getType(), variable);
String suffix = UUID.randomUUID().toString().replace("-", "").substring(0,5);
EntityPath<?> replacement = new EntityPathBase(expr.getType(), variable + suffix);
context.add(expr, replacement);
return replacement;
@ -134,7 +137,7 @@ public class CollectionAnyVisitor implements Visitor<Expression<?>,Context>{
}
return expr;
}
@Override
public Expression<?> visit(SubQueryExpression<?> expr, Context context) {
return expr;

View File

@ -59,6 +59,10 @@ public abstract class SerializerBase<S extends SerializerBase<S>> implements Vis
private static final Pattern OPERATION = Pattern.compile(NUMBER + WS + "[\\+\\-]" + WS + NUMBER);
private static final Pattern ADDITION = Pattern.compile(NUMBER + WS + "\\+" + WS + NUMBER);
//private static final Pattern SUBTRACTION = Pattern.compile(NUMBER + WS + "\\-" + WS + NUMBER);
private String constantPrefix = "a";
private String paramPrefix = "p";
@ -85,13 +89,13 @@ public abstract class SerializerBase<S extends SerializerBase<S>> implements Vis
rv.append(queryString.subSequence(end, m.start()));
}
String str = queryString.substring(m.start(), m.end());
boolean addition = ADDITION.matcher(str).matches();
String[] operands = OPERATOR.split(str);
char operator = str.charAt(operands[0].length());
BigDecimal result = null;
if (operator == '+') {
if (addition) {
result = new BigDecimal(operands[0]).add(new BigDecimal(operands[1]));
} else {
result = new BigDecimal(operands[0]).add(new BigDecimal(operands[1]));
result = new BigDecimal(operands[0]).subtract(new BigDecimal(operands[1]));
}
rv.append(result.toString());
end = m.end();

View File

@ -13,7 +13,7 @@
*/
package com.mysema.query.support;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.*;
import org.junit.Test;
@ -27,46 +27,46 @@ import com.mysema.query.types.TemplateExpressionImpl;
public class CollectionAnyVisitorTest {
private QCat cat = QCat.cat;
@Test
public void Path(){
assertEquals("cat_kittens", serialize(cat.kittens.any()));
assertMatches("cat_kittens.*", serialize(cat.kittens.any()));
}
@Test
public void Longer_Path(){
assertEquals("cat_kittens.name", serialize(cat.kittens.any().name));
assertMatches("cat_kittens.*\\.name", serialize(cat.kittens.any().name));
}
@Test
public void Very_Long_Path(){
assertEquals("cat_kittens_kittens.name", serialize(cat.kittens.any().kittens.any().name));
assertMatches("cat_kittens_kittens.*\\.name", serialize(cat.kittens.any().kittens.any().name));
}
@Test
public void Simple_BooleanOperation(){
Predicate predicate = cat.kittens.any().name.eq("Ruth123");
assertEquals("cat_kittens.name = Ruth123", serialize(predicate));
assertMatches("cat_kittens.*\\.name = Ruth123", serialize(predicate));
}
@Test
public void Simple_StringOperation(){
Predicate predicate = cat.kittens.any().name.substring(1).eq("uth123");
assertEquals("substring(cat_kittens.name,1) = uth123", serialize(predicate));
assertMatches("substring\\(cat_kittens.*\\.name,1\\) = uth123", serialize(predicate));
}
@Test
public void And_Operation(){
// TODO : the subqueries should be merged
Predicate predicate = cat.kittens.any().name.eq("Ruth123").and(cat.kittens.any().bodyWeight.gt(10.0));
assertEquals("cat_kittens.name = Ruth123 && cat_kittens.bodyWeight > 10.0", serialize(predicate));
assertMatches("cat_kittens.*\\.name = Ruth123 && cat_kittens.*\\.bodyWeight > 10.0", serialize(predicate));
}
@Test
public void Template(){
Expression<Boolean> templateExpr = TemplateExpressionImpl.create(Boolean.class, "{0} = {1}",
cat.kittens.any().name, ConstantImpl.create("Ruth123"));
assertEquals("cat_kittens.name = Ruth123", serialize(templateExpr));
assertMatches("cat_kittens.*\\.name = Ruth123", serialize(templateExpr));
}
private String serialize(Expression<?> expression){
@ -80,4 +80,7 @@ public class CollectionAnyVisitorTest {
return transformed.toString();
}
private static void assertMatches(String str1, String str2) {
assertTrue(str2, str2.matches(str1));
}
}

View File

@ -7,11 +7,19 @@ import org.junit.Test;
public class SerializerBaseTest {
@Test
public void Normalize() {
public void Normalize_Addition() {
assertEquals("3", SerializerBase.normalize("1+2"));
assertEquals("where 3 = 3", SerializerBase.normalize("where 1+2 = 3"));
assertEquals("where 3.3 = 3.3", SerializerBase.normalize("where 1.1+2.2 = 3.3"));
assertEquals("where 3.3 = 3.3", SerializerBase.normalize("where 1.1 + 2.2 = 3.3"));
}
@Test
public void Normalize_Subtraction() {
assertEquals("3", SerializerBase.normalize("5-2"));
assertEquals("where 3 = 3", SerializerBase.normalize("where 5-2 = 3"));
assertEquals("where 3.3 = 3.3", SerializerBase.normalize("where 5.5-2.2 = 3.3"));
assertEquals("where 3.3 = 3.3", SerializerBase.normalize("where 5.5 - 2.2 = 3.3"));
}
}

View File

@ -23,7 +23,7 @@ import com.mysema.query.types.PredicateOperation;
* @author tiwe
*
*/
public final class JPQLCollectionAnyVisitor extends CollectionAnyVisitor{
public final class JPQLCollectionAnyVisitor extends CollectionAnyVisitor {
public static final JPQLCollectionAnyVisitor DEFAULT = new JPQLCollectionAnyVisitor();

View File

@ -330,17 +330,6 @@ public class JPQLSerializer extends SerializerBase<JPQLSerializer> {
}
return null;
}
@SuppressWarnings("rawtypes")
private SingularAttribute<?,?> getIdProperty(EntityType entity) {
Set<SingularAttribute> singularAttributes = entity.getSingularAttributes();
for (SingularAttribute singularAttribute : singularAttributes) {
if (singularAttribute.isId()){
return singularAttribute;
}
}
return null;
}
@Override
@SuppressWarnings("unchecked")
@ -348,7 +337,6 @@ public class JPQLSerializer extends SerializerBase<JPQLSerializer> {
boolean old = wrapElements;
wrapElements = templates.wrapElements(operator);
// TODO : refactor each case into own method
if (operator.equals(Ops.IN)) {
if (args.get(1) instanceof Path) {
visitAnyInPath(type, args);
@ -359,24 +347,10 @@ public class JPQLSerializer extends SerializerBase<JPQLSerializer> {
}
} else if (operator.equals(Ops.INSTANCE_OF)) {
if (templates.isTypeAsString()) {
List<Expression<?>> newArgs = new ArrayList<Expression<?>>(args);
Class<?> cl = ((Class<?>) ((Constant<?>) newArgs.get(1)).getConstant());
// use discriminator value instead of fqnm
if (cl.getAnnotation(DiscriminatorValue.class) != null) {
newArgs.set(1, ConstantImpl.create(cl.getAnnotation(DiscriminatorValue.class).value()));
} else {
newArgs.set(1, ConstantImpl.create(cl.getName()));
}
super.visitOperation(type, operator, newArgs);
} else {
super.visitOperation(type, operator, args);
}
visitInstanceOf(type, operator, args);
} else if (operator.equals(Ops.NUMCAST)) {
Class<?> targetType = (Class<?>) ((Constant<?>) args.get(1)).getConstant();
String typeName = targetType.getSimpleName().toLowerCase(Locale.ENGLISH);
visitOperation(targetType, JPQLTemplates.CAST, Arrays.<Expression<?>>asList(args.get(0), ConstantImpl.create(typeName)));
visitNumCast(args);
} else if (operator.equals(Ops.EXISTS) && args.get(0) instanceof SubQueryExpression) {
SubQueryExpression subQuery = (SubQueryExpression) args.get(0);
@ -398,6 +372,29 @@ public class JPQLSerializer extends SerializerBase<JPQLSerializer> {
wrapElements = old;
}
private void visitNumCast(List<? extends Expression<?>> args) {
Class<?> targetType = (Class<?>) ((Constant<?>) args.get(1)).getConstant();
String typeName = targetType.getSimpleName().toLowerCase(Locale.ENGLISH);
visitOperation(targetType, JPQLTemplates.CAST, Arrays.<Expression<?>>asList(args.get(0), ConstantImpl.create(typeName)));
}
private void visitInstanceOf(Class<?> type, Operator<?> operator,
List<? extends Expression<?>> args) {
if (templates.isTypeAsString()) {
List<Expression<?>> newArgs = new ArrayList<Expression<?>>(args);
Class<?> cl = ((Class<?>) ((Constant<?>) newArgs.get(1)).getConstant());
// use discriminator value instead of fqnm
if (cl.getAnnotation(DiscriminatorValue.class) != null) {
newArgs.set(1, ConstantImpl.create(cl.getAnnotation(DiscriminatorValue.class).value()));
} else {
newArgs.set(1, ConstantImpl.create(cl.getName()));
}
super.visitOperation(type, operator, newArgs);
} else {
super.visitOperation(type, operator, args);
}
}
@SuppressWarnings({ "rawtypes", "unchecked" })
private void visitPathInCollection(Class<?> type, Operator<?> operator,
List<? extends Expression<?>> args) {
@ -410,7 +407,9 @@ public class JPQLSerializer extends SerializerBase<JPQLSerializer> {
EntityType<?> entityType = metamodel.entity(args.get(0).getType());
if (entityType.hasSingleIdAttribute()) {
SingularAttribute<?,?> id = getIdProperty(entityType);
// turn lhs into id path
lhs = new PathImpl(id.getJavaType(), lhs, id.getName());
// turn rhs into id collection
Set ids = new HashSet();
for (Object entity : (Collection<?>)rhs.getConstant()) {
ids.add(util.getIdentifier(entity));
@ -422,6 +421,17 @@ public class JPQLSerializer extends SerializerBase<JPQLSerializer> {
super.visitOperation(type, operator, args);
}
@SuppressWarnings("rawtypes")
private SingularAttribute<?,?> getIdProperty(EntityType entity) {
Set<SingularAttribute> singularAttributes = entity.getSingularAttributes();
for (SingularAttribute singularAttribute : singularAttributes) {
if (singularAttribute.isId()){
return singularAttribute;
}
}
return null;
}
@SuppressWarnings({ "rawtypes", "unchecked" })
private void visitAnyInPath(Class<?> type, List<? extends Expression<?>> args) {
if (!templates.isEnumInPathSupported() && args.get(0) instanceof Constant && Enum.class.isAssignableFrom(args.get(0).getType())) {

View File

@ -714,6 +714,13 @@ public abstract class AbstractStandardTest {
query().from(cat).where(sum(cat.kittens.size()).gt(0)).list(cat);
}
@Test
public void Substring() {
for (String str : query().from(cat).list(cat.name.substring(1,2))) {
assertEquals(1, str.length());
}
}
@Test
public void SubQuery(){
QShow show = QShow.show;

View File

@ -36,7 +36,7 @@ import com.mysema.testutil.JPATestRunner;
@JPAConfig("hsqldb")
public class JPAIntegrationTest extends ParsingTest {
private EntityManager entityManager;
private EntityManager em;
@Override
protected QueryHelper query() {
@ -47,7 +47,7 @@ public class JPAIntegrationTest extends ParsingTest {
System.out.println("query : " + toString().replace('\n', ' '));
// create Query and execute it
Query query = entityManager.createQuery(toString());
Query query = em.createQuery(toString());
JPAUtil.setConstants(query, getConstants(),getMetadata().getParams());
try {
query.getResultList();
@ -86,8 +86,8 @@ public class JPAIntegrationTest extends ParsingTest {
// NOTE : commented out, because HQLSDB doesn't support these queries
}
public void setEntityManager(EntityManager entityManager) {
this.entityManager = entityManager;
public void setEntityManager(EntityManager em) {
this.em = em;
}
}

View File

@ -13,7 +13,7 @@
*/
package com.mysema.query.jpa;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.*;
import org.junit.Test;
@ -31,48 +31,47 @@ public class JPQLCollectionAnyVisitorTest {
@Test
public void Path(){
assertEquals("cat_kittens", serialize(cat.kittens.any()));
assertMatches("cat_kittens.*", serialize(cat.kittens.any()));
}
@Test
public void Longer_Path(){
assertEquals("cat_kittens.name", serialize(cat.kittens.any().name));
assertMatches("cat_kittens.*\\.name", serialize(cat.kittens.any().name));
}
@Test
public void Simple_BooleanOperation(){
Predicate predicate = cat.kittens.any().name.eq("Ruth123");
assertEquals("exists (select 1\n" +
"from Cat cat_kittens\n" +
"where cat_kittens in elements(cat.kittens) and cat_kittens.name = ?1)", serialize(predicate));
assertMatches("exists \\(select 1\n" +
"from Cat cat_kittens.*\n" +
"where cat_kittens.* in elements\\(cat\\.kittens\\) and cat_kittens.*\\.name = \\?1\\)", serialize(predicate));
}
@Test
public void Simple_StringOperation(){
Predicate predicate = cat.kittens.any().name.substring(1).eq("uth123");
assertEquals("exists (select 1\n" +
"from Cat cat_kittens\n" +
"where cat_kittens in elements(cat.kittens) and substring(cat_kittens.name,2) = ?1)", serialize(predicate));
assertMatches("exists \\(select 1\n" +
"from Cat cat_kittens.*\n" +
"where cat_kittens.* in elements\\(cat.kittens\\) and substring\\(cat_kittens.*\\.name,2\\) = \\?1\\)", serialize(predicate));
}
@Test
public void And_Operation(){
// TODO : the subqueries should be merged
Predicate predicate = cat.kittens.any().name.eq("Ruth123").and(cat.kittens.any().bodyWeight.gt(10.0));
assertEquals("exists (select 1\n" +
"from Cat cat_kittens\n" +
"where cat_kittens in elements(cat.kittens) and cat_kittens.name = ?1) and exists (select 1\n" +
"from Cat cat_kittens\n" +
"where cat_kittens in elements(cat.kittens) and cat_kittens.bodyWeight > ?2)", serialize(predicate));
assertMatches("exists \\(select 1\n" +
"from Cat cat_kittens.*\n" +
"where cat_kittens.* in elements\\(cat.kittens\\) and cat_kittens.*\\.name = \\?1\\) and exists \\(select 1\n" +
"from Cat cat_kittens.*\n" +
"where cat_kittens.* in elements\\(cat.kittens\\) and cat_kittens.*\\.bodyWeight > \\?2\\)", serialize(predicate));
}
@Test
public void Template(){
Expression<Boolean> templateExpr = TemplateExpressionImpl.create(Boolean.class, "{0} = {1}",
cat.kittens.any().name, ConstantImpl.create("Ruth123"));
assertEquals("exists (select 1\n" +
"from Cat cat_kittens\n" +
"where cat_kittens in elements(cat.kittens) and cat_kittens.name = ?1)", serialize(templateExpr));
assertMatches("exists \\(select 1\n" +
"from Cat cat_kittens.*\n" +
"where cat_kittens.* in elements\\(cat\\.kittens\\) and cat_kittens.*\\.name = \\?1\\)", serialize(templateExpr));
}
private String serialize(Expression<?> expression){
@ -82,4 +81,7 @@ public class JPQLCollectionAnyVisitorTest {
return serializer.toString();
}
private static void assertMatches(String str1, String str2) {
assertTrue(str2, str2.matches(str1));
}
}