Merge pull request #2530 from jwgmeligmeyling/jpa-treated-path

[#658] Support JPQL Treated paths for WHERE clause
This commit is contained in:
Jan-Willem Gmelig Meyling 2020-12-31 19:45:05 +01:00 committed by GitHub
commit c75ef4cafd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 75 additions and 1 deletions

View File

@ -291,6 +291,9 @@
<exclude>com/querydsl/sql/types/LocalDateTimeType</exclude>
<exclude>com/querydsl/sql/types/LocalDateType</exclude>
<exclude>com/querydsl/sql/types/LocalTimeType</exclude>
<exclude>com/querydsl/jpa/Hibernate5Templates</exclude>
<exclude>com/querydsl/jpa/HibernateHandler</exclude>
<exclude>com/querydsl/jpa/JPAExpressions</exclude>
</excludes>
<compatibilityType>BACKWARD_COMPATIBLE_USER</compatibilityType>
<dumpDetails>true</dumpDetails>

View File

@ -65,7 +65,12 @@ public enum PathType implements Operator {
/**
* Root path
*/
VARIABLE;
VARIABLE,
/**
* Treated path
*/
TREATED_PATH;
@Override
public Class<?> getType() {

View File

@ -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 <U> the subtype class
* @param <T> the expression type
* @return subtype instance with the same identity
*/
public static <U extends BeanPath<? extends T>, T> U treat(BeanPath<? extends T> path, Class<U> 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 <U extends BeanPath<? extends T>, T> Class<? extends T> getConcreteEntitySubType(Class<U> subtype) {
return (Class<? extends T>) ((ParameterizedType) subtype.getGenericSuperclass()).getActualTypeArguments()[0];
}
private JPAExpressions() { }
}

View File

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

View File

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