gpt4 book ai didi

JPA Dynamic Order By with Criteria API

转载 作者:行者123 更新时间:2023-12-04 21:26:16 26 4
gpt4 key购买 nike

我有以下使用 JPA Criteria API 进行动态排序的代码片段

Root<Employee> root = criteriaQuery.from(Employee);
Join<Employee, Project> joinProject =
root.join(Employee_.projectList, JoinType.LEFT);

if (sortDirection.equals("asc")) {
criteriaQuery.orderBy(cb.asc(root.get(sortField)));

如果我传递一个属性 Employee实体按语句排序,它可以顺利工作,但是如果 Project 的属性实体被传递给 order by 语句,抛出异常说明
The attribute [projectName] is not present in the managed type

因为 projectNameProject 的一个属性使用 joinProject 与 Employee 连接的实体.为了按声明我使用 root.get(sortField) .如果是 joinProject.get(sortField) , 当 Project 的属性时它会正常工作正在被传递给 order by 语句。

我的问题是

我该如何修改我的 Order By语句以迎合传递的所有属性?

我是否需要有条件地检查哪个属性并相应地使用 if 条件还是有更好的方法来做到这一点?

欣赏这方面的洞察力。

最佳答案

一个简单场景(预先确定的仅一级连接)的特定方法可能是这样的:

    Root<Employee> root = criteriaQuery.from(Employee.class);
Join<Employee, Project> joinProject = root.join(Employee_.projectList, JoinType.LEFT);

Class<?> baseClass = fieldTypeMap.get(sortField);
From<?, ?> from;
if(baseClass == Employee.class)
{
from = root;
}
else if(baseClass == Project.class)
{
from = joinTeam;
}
else ...

Expression<?> expr = from.get(sortField);

if(sortDirection.equals("asc"))
{
criteriaQuery.orderBy(cb.asc(expr));
}

...

哪里 fieldTypeMap是这样的:
private final static Map<String, Class<?>> fieldTypeMap = new HashMap<>();
static {
fieldTypeMap.put("employeeName", Employee.class);
fieldTypeMap.put("projectName", Project.class);
...
}

然而,这又快又脏,又丑又难维护。

如果你想要一个通用的方法,事情可能会变得有点复杂。

就我个人而言,我使用的是建立在 EntityManager 之上的我自己的类。 , CriteriaBuilder和 Metamodel,它提供动态过滤和多排序功能。
但是这样的事情应该足够有意义:
protected static List<Order> buildOrderBy(CriteriaBuilder builder, Root<?> root, List<SortMeta> sortList)
{
List<Order> orderList = new LinkedList<>();

for(SortMeta sortMeta : sortList)
{
String sortField = sortMeta.getSortField();
SortOrder sortOrder = sortMeta.getSortOrder();

if(sortField == null || sortField.isEmpty() || sortOrder == null)
{
continue;
}

Expression<?> expr = getExpression(root, sortField);

if(sortOrder == SortOrder.ASCENDING)
{
orderList.add(builder.asc(expr));
}
else if(sortOrder == SortOrder.DESCENDING)
{
orderList.add(builder.desc(expr));
}
}

return orderList;
}

protected static Expression<?> getExpression(Root<?> root, String sortField)
{
ManagedType<?> managedType = root.getModel();
From<?, Object> from = (From<?, Object>) root;

String[] elements = sortField.split("\\.");
for(String element : elements)
{
Attribute<?, ?> attribute = managedType.getAttribute(element);
if(attribute.getPersistentAttributeType() == PersistentAttributeType.BASIC)
{
return from.get(element);
}

from = from.join(element, JoinType.LEFT);
managedType = EntityUtils.getManagedType(from.getJavaType());
}

return from;
}

这样,连接基于排序字段,它现在是一个点状路径表达式,如“projectList.name”或“office.responsible.age”
public static <X> ManagedType<X> getManagedType(Class<X> clazz)
{
try
{
return getMetamodel().managedType(clazz);
}
catch(IllegalArgumentException e)
{
return null;
}
}

public static Metamodel getMetamodel()
{
return getEntityManagerFactory().getMetamodel();
}

public static EntityManagerFactory getEntityManagerFactory()
{
try
{
return InitialContext.doLookup("java:module/persistence/EntityManagerFactory");
}
catch(NamingException e)
{
throw new RuntimeException(e.getMessage(), e);
}
}

为了让它在 webapp 上工作,你必须在 web.xml 上声明上下文引用:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
version="3.1">

<display-name>my_app_name</display-name>

...

<persistence-context-ref>
<persistence-context-ref-name>java:module/persistence/EntityManager</persistence-context-ref-name>
<persistence-unit-name>my_pu_name</persistence-unit-name>
</persistence-context-ref>

<persistence-unit-ref>
<persistence-unit-ref-name>java:module/persistence/EntityManagerFactory</persistence-unit-ref-name>
<persistence-unit-name>my_pu_name</persistence-unit-name>
</persistence-unit-ref>
</web-app>

更新

我不知道 EclipseLink 如何处理分组,但在某些情况下,Hibernate 也无法正确执行连接(实际上是连接条件)。

例如,当满足所有这些条件时:
  • 基于 SINGLE_TABLE
  • 查询属于类层次结构(而不​​是叶子)的实体(作为 Root/From)
  • 加入/获取子类的属性
  • 通过属性名称(字符串)而不是属性加入/获取

  • 为了解决这个问题,我总是将属性名称解析为一个 Attribute 并重用已经“走过”的连接(我是说事情可能会变得复杂吗?):
    public class MetaDescriptor extends BusinessObject implements Serializable, MemberEx, ColumnDescriptor
    {
    private static final long serialVersionUID = 1L;

    @BusinessKey
    protected final Attribute<?, ?> attribute;

    @BusinessKey
    protected final MetaDescriptor parent;

    protected List<MetaDescriptor> childList;

    protected final Type<?> elementType;

    ...

    protected MetaDescriptor(Attribute<?, ?> attribute, MetaDescriptor parent)
    {
    this.attribute = attribute;
    this.parent = parent;

    if(attribute instanceof SingularAttribute)
    {
    SingularAttribute<?, ?> singularAttribute = (SingularAttribute<?, ?>) attribute;
    elementType = singularAttribute.getType();
    }
    else if(attribute instanceof PluralAttribute)
    {
    PluralAttribute<?, ?, ?> pluralAttribute = (PluralAttribute<?, ?, ?>) attribute;
    elementType = pluralAttribute.getElementType();
    }
    else
    {
    elementType = null;
    }
    }

    public static MetaDescriptor getDescriptor(ManagedType<?> managedType, String path)
    {
    return getDescriptor(managedType, path, null);
    }

    public static MetaDescriptor getDescriptor(From<?, ?> from, String path)
    {
    if(from instanceof Root)
    {
    return getDescriptor(((Root<?>) from).getModel(), path);
    }

    return getDescriptor(from.getJavaType(), path);
    }

    public static MetaDescriptor getDescriptor(Class<?> clazz, String path)
    {
    ManagedType<?> managedType = EntityUtils.getManagedType(clazz);
    if(managedType == null)
    {
    return null;
    }

    return getDescriptor(managedType, path);
    }

    private static MetaDescriptor getDescriptor(ManagedType<?> managedType, String path, MetaDescriptor parent)
    {
    if(path == null)
    {
    return null;
    }

    Entry<String, String> slice = StringUtilsEx.sliceBefore(path, '.');
    String attributeName = slice.getKey();

    Attribute<?, ?> attribute;
    if("class".equals(attributeName))
    {
    attribute = new ClassAttribute<>(managedType);
    }
    else
    {
    try
    {
    attribute = managedType.getAttribute(attributeName);
    }
    catch(IllegalArgumentException e)
    {
    Class<?> managedClass = managedType.getJavaType();

    // take only if it is unique
    attribute = StreamEx.of(EntityUtils.getMetamodel().getManagedTypes())
    .filter(x -> managedClass.isAssignableFrom(x.getJavaType()))
    .flatCollection(ManagedType::getDeclaredAttributes)
    .filterBy(Attribute::getName, attributeName)
    .limit(2)
    .collect(Collectors.reducing((a, b) -> null))
    .orElse(null);

    if(attribute == null)
    {
    return null;
    }
    }
    }

    MetaDescriptor descriptor = new MetaDescriptor(attribute, parent);

    String remainingPath = slice.getValue();
    if(remainingPath.isEmpty())
    {
    return descriptor;
    }

    Type<?> elementType = descriptor.getElementType();
    if(elementType instanceof ManagedType)
    {
    return getDescriptor((ManagedType<?>) elementType, remainingPath, descriptor);
    }

    throw new IllegalArgumentException();
    }

    @Override
    public <T> Expression<T> getExpression(CriteriaBuilder builder, From<?, ?> from)
    {
    From<?, Object> parentFrom = getParentFrom(from);

    if(attribute instanceof ClassAttribute)
    {
    return (Expression<T>) parentFrom.type();
    }

    if(isSingular())
    {
    return parentFrom.get((SingularAttribute<Object, T>) attribute);
    }

    return getJoin(parentFrom, JoinType.LEFT);
    }

    private <X, T> From<X, T> getParentFrom(From<?, ?> from)
    {
    return OptionalEx.of(parent)
    .map(x -> x.getJoin(from, JoinType.LEFT))
    .select(From.class)
    .orElse(from);
    }

    public <X, T> Join<X, T> getJoin(From<?, ?> from, JoinType joinType)
    {
    From<?, X> parentFrom = getParentFrom(from);

    Join<X, T> join = (Join<X, T>) StreamEx.of(parentFrom.getJoins())
    .findAny(x -> Objects.equals(x.getAttribute(), attribute))
    .orElseGet(() -> buildJoin(parentFrom, joinType));

    return join;
    }

    private <X, T> Join<X, T> buildJoin(From<?, X> from, JoinType joinType)
    {
    if(isSingular())
    {
    return from.join((SingularAttribute<X, T>) attribute, joinType);
    }

    if(isMap())
    {
    return from.join((MapAttribute<X, ?, T>) attribute, joinType);
    }

    if(isSet())
    {
    return from.join((SetAttribute<X, T>) attribute, joinType);
    }

    if(isList())
    {
    return from.join((ListAttribute<X, T>) attribute, joinType);
    }

    if(isCollection())
    {
    return from.join((CollectionAttribute<X, T>) attribute, joinType);
    }

    throw new ImpossibleException();
    }

    public Order buildOrder(CriteriaBuilderEx builder, From<?, ?> from, SortOrder direction)
    {
    if(direction == null)
    {
    return null;
    }

    Expression<?> expr = getExpression(builder, from);

    return direction == SortOrder.ASCENDING ? builder.asc(expr) : builder.desc(expr);
    }
    }

    有了这个构造,我现在可以安全地:
    public static List<Order> buildOrderList(CriteriaBuilderEx builder, From<?, ? extends Object> from, List<SortMeta> list)
    {
    return StreamEx.of(list)
    .nonNull()
    .map(x -> buildOrder(builder, from, x.getSortField(), x.getSortOrder()))
    .nonNull()
    .toList();
    }

    public static Order buildOrder(CriteriaBuilderEx builder, From<?, ? extends Object> from, String path, SortOrder direction)
    {
    if(path == null || path.isEmpty() || direction == null)
    {
    return null;
    }

    MetaDescriptor descriptor = MetaDescriptor.getDescriptor(from, path);
    if(descriptor == null)
    {
    return null;
    }

    return descriptor.buildOrder(builder, from, direction);
    }

    关于JPA Dynamic Order By with Criteria API,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34452229/

    26 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com