From 41b73ea8166ea21e7063e8cb1dcc98c97b43dffd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20Westk=C3=A4mper?= Date: Tue, 18 Nov 2014 23:28:06 +0200 Subject: [PATCH] Serialize constants in case constructs as literals for Hibernate --- .../com/mysema/query/jpa/HQLTemplates.java | 5 ++ .../com/mysema/query/jpa/JPQLSerializer.java | 74 +++++++++++++++---- .../com/mysema/query/jpa/JPQLTemplates.java | 4 + .../com/mysema/query/AbstractJPATest.java | 8 -- .../mysema/query/jpa/JPQLSerializerTest.java | 29 ++++++++ 5 files changed, 98 insertions(+), 22 deletions(-) diff --git a/querydsl-jpa/src/main/java/com/mysema/query/jpa/HQLTemplates.java b/querydsl-jpa/src/main/java/com/mysema/query/jpa/HQLTemplates.java index 77b255616..78725da19 100644 --- a/querydsl-jpa/src/main/java/com/mysema/query/jpa/HQLTemplates.java +++ b/querydsl-jpa/src/main/java/com/mysema/query/jpa/HQLTemplates.java @@ -141,4 +141,9 @@ public class HQLTemplates extends JPQLTemplates { return true; } + @Override + public boolean isCaseWithLiterals() { + return true; + } + } 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 915138d4e..6e04cca72 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 @@ -79,6 +79,8 @@ public class JPQLSerializer extends SerializerBase { private boolean inProjection = false; + private boolean inCaseOperation = false; + static{ joinTypes.put(JoinType.DEFAULT, COMMA); joinTypes.put(JoinType.FULLJOIN, "\n full join "); @@ -279,23 +281,64 @@ public class JPQLSerializer extends SerializerBase { @Override public void visitConstant(Object constant) { - boolean wrap = templates.wrapConstant(constant); - if (wrap) { - append("("); - } - append("?"); - if (!getConstantToLabel().containsKey(constant)) { - final String constLabel = String.valueOf(getConstantToLabel().size()+1); - getConstantToLabel().put(constant, constLabel); - append(constLabel); + if (inCaseOperation && templates.isCaseWithLiterals()) { + visitLiteral(constant); } else { - append(getConstantToLabel().get(constant)); + boolean wrap = templates.wrapConstant(constant); + if (wrap) { + append("("); + } + append("?"); + if (!getConstantToLabel().containsKey(constant)) { + final String constLabel = String.valueOf(getConstantToLabel().size()+1); + getConstantToLabel().put(constant, constLabel); + append(constLabel); + } else { + append(getConstantToLabel().get(constant)); + } + if (wrap) { + append(")"); + } } - if (wrap) { - append(")"); + } + + public void visitLiteral(Object constant) { + if (constant instanceof Boolean) { + append(constant.toString()); + } else if (constant instanceof Number) { + append(constant.toString()); + } else if (constant instanceof String) { + append("'"); + append(escapeLiteral(constant.toString())); + append("'"); + } else if (constant instanceof Enum) { + append(constant.getClass().getName()); + append("."); + append(((Enum) constant).name()); + } else { + // TODO date time literals + throw new IllegalArgumentException("Unsupported constant " + constant); } } + private String escapeLiteral(String str) { + StringBuilder builder = new StringBuilder(); + for (char ch : str.toCharArray()) { + if (ch == '\n') { + builder.append("\\n"); + continue; + } else if (ch == '\r') { + builder.append("\\r"); + continue; + } else if (ch == '\'') { + builder.append("''"); + continue; + } + builder.append(ch); + } + return builder.toString(); + } + @Override public Void visit(ParamExpression param, Void context) { append("?"); @@ -336,7 +379,9 @@ public class JPQLSerializer extends SerializerBase { @Override @SuppressWarnings("unchecked") protected void visitOperation(Class type, Operator operator, List> args) { - boolean old = wrapElements; + boolean oldInCaseOperation = inCaseOperation; + inCaseOperation = inCaseOperation || operator.equals(Ops.CASE) || operator.equals(Ops.CASE_EQ); + boolean oldWrapElements = wrapElements; wrapElements = templates.wrapElements(operator); if (operator == Ops.EQ && args.get(1) instanceof Operation && @@ -387,7 +432,8 @@ public class JPQLSerializer extends SerializerBase { super.visitOperation(type, operator, args); } - wrapElements = old; + inCaseOperation = oldInCaseOperation; + wrapElements = oldWrapElements; } private void visitNumCast(List> args) { diff --git a/querydsl-jpa/src/main/java/com/mysema/query/jpa/JPQLTemplates.java b/querydsl-jpa/src/main/java/com/mysema/query/jpa/JPQLTemplates.java index a2179d238..db636c13d 100644 --- a/querydsl-jpa/src/main/java/com/mysema/query/jpa/JPQLTemplates.java +++ b/querydsl-jpa/src/main/java/com/mysema/query/jpa/JPQLTemplates.java @@ -183,4 +183,8 @@ public class JPQLTemplates extends Templates { return queryHandler; } + public boolean isCaseWithLiterals() { + return false; + } + } 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 e27e770fa..8200dab06 100644 --- a/querydsl-jpa/src/test/java/com/mysema/query/AbstractJPATest.java +++ b/querydsl-jpa/src/test/java/com/mysema/query/AbstractJPATest.java @@ -303,19 +303,11 @@ public abstract class AbstractJPATest { } @Test - @NoHibernate // https://hibernate.atlassian.net/browse/HHH-4700 @NoBatooJPA public void Case() { query().from(cat).list(cat.name.when("Bob").then(1).otherwise(2)); } - @Test(expected=ClassCastException.class) - @NoEclipseLink - @NoBatooJPA - public void Case_Hibernate() { - query().from(cat).list(cat.name.when("Bob").then(1).otherwise(2)); - } - @Test public void Case2() { query().from(cat) diff --git a/querydsl-jpa/src/test/java/com/mysema/query/jpa/JPQLSerializerTest.java b/querydsl-jpa/src/test/java/com/mysema/query/jpa/JPQLSerializerTest.java index b2068f9d1..22104dd5d 100644 --- a/querydsl-jpa/src/test/java/com/mysema/query/jpa/JPQLSerializerTest.java +++ b/querydsl-jpa/src/test/java/com/mysema/query/jpa/JPQLSerializerTest.java @@ -19,6 +19,7 @@ import com.mysema.query.DefaultQueryMetadata; import com.mysema.query.JoinType; import com.mysema.query.QueryMetadata; import com.mysema.query.domain.QCat; +import com.mysema.query.jpa.domain.JobFunction; import com.mysema.query.jpa.domain.Location; import com.mysema.query.jpa.domain.QDomesticCat; import com.mysema.query.jpa.domain.QEmployee; @@ -223,4 +224,32 @@ public class JPQLSerializerTest { assertEquals("select cat_\nfrom Cat cat_\n inner join cat_.mate on cat_.mate.alive", serializer.toString()); } + + @Test + public void visitLiteral_boolean() { + JPQLSerializer serializer = new JPQLSerializer(HQLTemplates.DEFAULT); + serializer.visitLiteral(Boolean.TRUE); + assertEquals("true", serializer.toString()); + } + + @Test + public void visitLiteral_number() { + JPQLSerializer serializer = new JPQLSerializer(HQLTemplates.DEFAULT); + serializer.visitLiteral(1.543); + assertEquals("1.543", serializer.toString()); + } + + @Test + public void visitLiteral_string() { + JPQLSerializer serializer = new JPQLSerializer(HQLTemplates.DEFAULT); + serializer.visitLiteral("abc''def"); + assertEquals("'abc''''def'", serializer.toString()); + } + + @Test + public void visitLiteral_enum() { + JPQLSerializer serializer = new JPQLSerializer(HQLTemplates.DEFAULT); + serializer.visitLiteral(JobFunction.MANAGER); + assertEquals("com.mysema.query.jpa.domain.JobFunction.MANAGER", serializer.toString()); + } }