根据条件遍历路径,未知对象结构的根元素

4

我有一个搜索API,可以抽象地搜索持久化方案中定义的引用实体的属性。 例如,我有像这样的实体:

@Entity
public class EntityA {
@Column
private String someProperty;

@Column
private EntityB someReference;
}

@Entity
public class EntityB {
@Column
private String someProperty;

@Column
private Set<EntityC> someReferences;
}

@Entity
public class EntityC {
@Column
private String someProperty;
}

使用这些实体时,我可以遍历路径(例如,当我的根实体为EntityA且我确定用户在搜索字符串字段时):

private Expression<String> getExpression(Root<T> root, String fieldName) {
        String[] propertySplit = fieldName.split("\\.");
        Path<String> path = null;
        for (String property : propertySplit) {
            if (path == null) {
                path = root.get(property);
                continue;
            }
            path = path.get(property);
        }
        return path;
    }

假设当前 为 EntityA,我可以这样调用 getExpression 方法:

[...]
criteriaBuilder.equal(getExpression(entityARoot, "someProperty"), "myValue");
[...]

我也可以调用引用:

[...]
criteriaBuilder.equal(getExpression(entityARoot, "someReference.someProperty"), "myValue");
[...]

但是当路径遇到集合类型时,这种方法就不起作用了,但我想做类似于这样的事情:

[...]
criteriaBuilder.equal(getExpression(entityARoot, "someReference.someReferences.someProperty"), "myValue");
[...]

我遇到了以下异常:

java.lang.IllegalStateException: Illegal attempt to dereference path source [null.someReferences] of basic type

我知道一定有方法可以做到这一点,因为Spring Data也可以使用Repository中的方法名称实现。我的目标是创建一个函数,可以遍历任何未知对象,只要我检查的最终属性是我所知道的类型,那么每次我都知道该标准是应该比较字符串、整数、布尔值、日期等。

你尝试过在抛出异常的情况下使用 root.join(property)path.join(property) 吗?请参考 https://dev59.com/qlQJ5IYBdhLWcg3wdVkW。 - JMSilla
不幸的是,Path<>没有join函数.. Root有,但我认为我也需要path拥有它。但它没有.. - Noixes
也许你可以将get(property)返回的对象转换为一个包含join()方法的接口,并调用它。我认为最好的检查方法是通过调试并观察get(property)返回的实际类别来进行。join()方法在From<>接口中声明,而Root<>继承自它:https://docs.oracle.com/javaee/7/api/javax/persistence/criteria/From.html。 - JMSilla
2
每当root.property本身是@Entity时,怎么办?给定Root的Java类型:Class rootClass = ((RootImpl) root).getEntityType().getJavaType();,通过反射,您可以找到有关root.property字段的信息,并相应地采取行动:如果root.property是叶子,则只需创建谓词;而如果您处于root.property1.property2.property3的情况下,并且发现property1是@Entity,则加入实体并在路径joinProperty1.property2.property3的其余部分上应用相同的机制。 - tremendous7
2个回答

1

1
这看起来非常有前途!非常感谢你!我会在以后的时间尝试它,因为我今天不在办公室。无论如何,我还是会授予你奖励,因为你为我提供了解决此问题的途径。 - Noixes
1
我已经检查过了,决定使用rsql实现。非常感谢你! - Noixes

0

如果您想在查询中立即访问嵌套实体的集合,则需要急切地获取它们。

    @OneToMany(fetch = FetchType.EAGER) 
    @Column
    private Set<EntityC> someReferences;

请注意,如果"someReference"为空,则"someReference.someProperty"等等都不安全,会导致意外的结果。建议阅读上面的评论来学习如何使用Join来避免这种情况。


Spring 也可以通过命名规则来实现这一点。当你输入像这样的内容 findBySomeReferencesSomeReferenceName(String name); 即使属性是延迟加载的,它也能以某种方式创建查询。 - Noixes

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接