假设我们有一个抽象@Entity Animal,以及几个扩展 Animal 的实体类,包括 Dog、Cat、Monkey 和 Bat。
如何根据扩展实体的类过滤结果?
示例: 有一些复选框,用户可以选择要检索的实体。
[ ] Dog
[X] Cat
[X] Monkey
[ ] Bat
现在我想使用
Animal
类中定义的(命名)查询来检索实体。我可以在查询中放入什么样的查询参数,以便只返回 Cat 和 Monkey 对象?
我不确定它是否受 JPA 支持,但是在 Hibernate 中执行此操作的方法,无论继承策略如何,因此即使您没有鉴别器(或没有将其映射为属性)也是如此使用隐式
class
属性 :
String jpql = "select a from Animal a where a.class in (:classes)";
Query q = em.createQuery(jpql).setParameter("classes",
Arrays.asList(Cat.class, Monkey.class));
编辑:
我刚刚发现在 JPA2 中可以使用 TYPE 运算符:
String jpql = "SELECT a FROM Animal a WHERE TYPE(a) IN :classes";
Query q = em.createQuery(jpql).setParameter("classes",
Arrays.asList(Cat.class, Monkey.class));
您可以使用鉴别器列和值来仅搜索给定类型的某些子类型。默认情况下,鉴别器列的名称是 JPA 中的 DTYPE,类型是 String,值是类的名称。但是,您可以通过添加类级别注释
@DiscriminatorColumn(name="KIND", discriminatorType=DiscriminatorType.INTEGER)
(对于鉴别器列的名称和类型)和 @DiscriminatorValue("1")
(对于特定类的特定鉴别器值)来覆盖它。然后,您可以在 yoru JPQL 查询的 WHERE 子句中使用它来仅获取某些子类型,例如:WHERE DTYPE="Dog" OR DTYPE="Cat"
这是我使用的:
/**
* Build an "instanceof" {@link Predicate} for an entity type.
*/
@SuppressWarnings("unchecked")
public static Predicate instanceOf(EntityManager entityManager, Path<?> path, Class<?> type) {
Preconditions.checkArgument(entityManager != null, "null entityManager");
Preconditions.checkArgument(path != null, "null path");
Preconditions.checkArgument(type != null, "null type");
final Set<Class<?>> subtypes = entityManager.getMetamodel().getManagedTypes().stream()
.map(ManagedType::getJavaType)
.filter(type::isAssignableFrom)
.collect(Collectors.toSet());
return JPAUtil.in(entityManager.getCriteriaBuilder(), (Expression<Class<?>>)path.type(), subtypes);
}
/**
* Build an IN predicate from a collection of possible values.
*/
public static <T> Predicate in(CriteriaBuilder builder, Expression<T> expr, Iterable<? extends T> values) {
Preconditions.checkArgument(builder != null, "null builder");
Preconditions.checkArgument(expr != null, "null expr");
Preconditions.checkArgument(values != null, "null values");
final CriteriaBuilder.In<T> in = builder.in(expr);
values.forEach(in::value);
return in;
}