diff --git a/pom.xml b/pom.xml
index cb3d988a8..c77280ac9 100644
--- a/pom.xml
+++ b/pom.xml
@@ -291,6 +291,9 @@
com/querydsl/sql/types/LocalDateTimeType
com/querydsl/sql/types/LocalDateType
com/querydsl/sql/types/LocalTimeType
+ com/querydsl/jpa/Hibernate5Templates
+ com/querydsl/jpa/HibernateHandler
+ com/querydsl/jpa/JPAExpressions
BACKWARD_COMPATIBLE_USER
true
diff --git a/querydsl-core/src/main/java/com/querydsl/core/types/PathType.java b/querydsl-core/src/main/java/com/querydsl/core/types/PathType.java
index 09b5cf9aa..14d7bf731 100644
--- a/querydsl-core/src/main/java/com/querydsl/core/types/PathType.java
+++ b/querydsl-core/src/main/java/com/querydsl/core/types/PathType.java
@@ -65,7 +65,12 @@ public enum PathType implements Operator {
/**
* Root path
*/
- VARIABLE;
+ VARIABLE,
+
+ /**
+ * Treated path
+ */
+ TREATED_PATH;
@Override
public Class> getType() {
diff --git a/querydsl-jpa/src/main/java/com/querydsl/jpa/JPAExpressions.java b/querydsl-jpa/src/main/java/com/querydsl/jpa/JPAExpressions.java
index f2a99fe1d..2044e2a7f 100644
--- a/querydsl-jpa/src/main/java/com/querydsl/jpa/JPAExpressions.java
+++ b/querydsl-jpa/src/main/java/com/querydsl/jpa/JPAExpressions.java
@@ -17,11 +17,19 @@ import com.querydsl.core.Tuple;
import com.querydsl.core.types.CollectionExpression;
import com.querydsl.core.types.EntityPath;
import com.querydsl.core.types.Expression;
+import com.querydsl.core.types.ExpressionException;
import com.querydsl.core.types.Ops;
+import com.querydsl.core.types.PathMetadata;
+import com.querydsl.core.types.PathType;
+import com.querydsl.core.types.dsl.BeanPath;
import com.querydsl.core.types.dsl.ComparableExpression;
import com.querydsl.core.types.dsl.Expressions;
import com.querydsl.core.types.dsl.StringExpression;
+import javax.persistence.Entity;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.ParameterizedType;
+
/**
* {@code JPAExpressions} provides factory methods for JPQL specific operations
* elements.
@@ -102,6 +110,31 @@ public final class JPAExpressions {
return select(expr).from(expr);
}
+ /**
+ * Create a JPA 2.1 treated path.
+ *
+ * @param path The path to apply the treat operation on
+ * @param subtype subtype class
+ * @param the subtype class
+ * @param the expression type
+ * @return subtype instance with the same identity
+ */
+ public static , T> U treat(BeanPath extends T> path, Class subtype) {
+ try {
+ Class extends T> entitySubType = getConcreteEntitySubType(subtype);
+ PathMetadata pathMetadata = new PathMetadata(path, getEntityName(entitySubType), PathType.TREATED_PATH);
+ return subtype.getConstructor(PathMetadata.class).newInstance(pathMetadata);
+ } catch (InstantiationException e) {
+ throw new ExpressionException(e.getMessage(), e);
+ } catch (IllegalAccessException e) {
+ throw new ExpressionException(e.getMessage(), e);
+ } catch (InvocationTargetException e) {
+ throw new ExpressionException(e.getMessage(), e);
+ } catch (NoSuchMethodException e) {
+ throw new ExpressionException(e.getMessage(), e);
+ }
+ }
+
/**
* Create a avg(col) expression
*
@@ -142,6 +175,22 @@ public final class JPAExpressions {
return Expressions.stringOperation(JPQLOps.TYPE, path);
}
+ private static String getEntityName(Class> clazz) {
+ final Entity entityAnnotation = clazz.getAnnotation(Entity.class);
+ if (entityAnnotation != null && entityAnnotation.name().length() > 0) {
+ return entityAnnotation.name();
+ } else if (clazz.getPackage() != null) {
+ String pn = clazz.getPackage().getName();
+ return clazz.getName().substring(pn.length() + 1);
+ } else {
+ return clazz.getName();
+ }
+ }
+
+ private static , T> Class extends T> getConcreteEntitySubType(Class subtype) {
+ return (Class extends T>) ((ParameterizedType) subtype.getGenericSuperclass()).getActualTypeArguments()[0];
+ }
+
private JPAExpressions() { }
}
diff --git a/querydsl-jpa/src/main/java/com/querydsl/jpa/JPQLTemplates.java b/querydsl-jpa/src/main/java/com/querydsl/jpa/JPQLTemplates.java
index f0e949690..8988a7d32 100644
--- a/querydsl-jpa/src/main/java/com/querydsl/jpa/JPQLTemplates.java
+++ b/querydsl-jpa/src/main/java/com/querydsl/jpa/JPQLTemplates.java
@@ -144,6 +144,7 @@ public class JPQLTemplates extends Templates {
// path types
add(PathType.PROPERTY, "{0}.{1s}");
add(PathType.VARIABLE, "{0s}");
+ add(PathType.TREATED_PATH, "treat({0} as {1s})");
// case for eq
add(Ops.CASE_EQ, "case {1} end");
diff --git a/querydsl-jpa/src/test/java/com/querydsl/jpa/JPQLSerializerTest.java b/querydsl-jpa/src/test/java/com/querydsl/jpa/JPQLSerializerTest.java
index 575dbb066..2cf3b002c 100644
--- a/querydsl-jpa/src/test/java/com/querydsl/jpa/JPQLSerializerTest.java
+++ b/querydsl-jpa/src/test/java/com/querydsl/jpa/JPQLSerializerTest.java
@@ -23,6 +23,7 @@ import org.junit.Test;
import com.querydsl.core.DefaultQueryMetadata;
import com.querydsl.core.JoinType;
import com.querydsl.core.QueryMetadata;
+import com.querydsl.core.domain.QAnimal;
import com.querydsl.core.domain.QCat;
import com.querydsl.core.types.EntityPath;
import com.querydsl.core.types.Expression;
@@ -253,6 +254,21 @@ public class JPQLSerializerTest {
" inner join treat(cat.mate as DomesticCat) as domesticCat", serializer.toString());
}
+ @Test
+ public void treated_path() {
+ QAnimal animal = QAnimal.animal;
+ JPQLSerializer serializer = new JPQLSerializer(HQLTemplates.DEFAULT);
+ QueryMetadata md = new DefaultQueryMetadata();
+ md.addJoin(JoinType.DEFAULT, animal);
+
+ md.addWhere(JPAExpressions.treat(animal, QCat.class).breed.eq(1));
+ md.setProjection(animal);
+ serializer.serialize(md, false, null);
+ assertEquals("select animal\n" +
+ "from Animal animal\n" +
+ "where treat(animal as Cat).breed = ?1", serializer.toString());
+ }
+
@Test
public void openJPA_variables() {
QCat cat = QCat.cat;