java - 为 Jinq 创建查询时是否可以使用 Predicate<T> ?

标签 java jpa lambda predicate jinq

我的问题是关于 Jinq 的,我使用的是 1.8.9 版本,目前是最新版本。

我正在尝试使用 Jinq 来实现通用的可重用 JPA(Java Persistence API)类型安全查询方法,并使用 Java 8 lambda(功能接口(interface))谓词作为方法参数。

不幸的是,我无法使其与 Java 8 谓词一起使用,但我可以使用类似的谓词类型(由 Jinq 提供)作为方法参数,但希望避免方法签名中对 Jinq 的依赖,因此更喜欢Java 8 谓词(如果可能)?

Jinq提供了功能接口(interface)“Where”:

package org.jinq.orm.stream;
public interface JinqStream<T> extends Stream<T> {
    @FunctionalInterface
    public static interface Where<U, E extends Exception> extends Serializable {
      public boolean where(U obj) throws E;
    }

我可以通过在方法签名中使用上述接口(interface)来实现我想要的查询方法(但具有不良耦合),如下所示:

public List<T> select(JinqStream.Where<T, Exception> wherePredicate)

我想使用如下标准谓词,而不是在方法签名中与 Jinq 进行上述耦合:

public List<T> select(java.util.function.Predicate<T> wherePredicate) {

标准谓词定义如下:

@FunctionalInterface
public interface Predicate<T> {
  public boolean test(T t);
}

因此,我认为可以使用以下代码来实现我想要的 select 方法,以创建 Jinq 接口(interface)的 lambda 实现:

public List<T> select(java.util.function.Predicate<T> predicate) {
    org.jinq.orm.stream.JinqStream.Where<T, Exception> wherePredicate = u -> predicate.test(u);
    ...

但是,它不起作用,但会导致 IllegalArgumentException(请参阅下面粘贴的堆栈跟踪)

下面是一些更多的代码,说明了我正在尝试做的事情。

我试图说明的问题是,我想在下面的方法“DataMapperBase.select2”中使用 Predicate 参数,而不是下面的方法“DataMapperBase.select”中的 Jinq 特定的Where 参数。

public abstract class DataMapperBase<T> {
    ...
    private EntityManagerFactory entityManagerFactory;
    private EntityManager entityManager;
    private final Class clazz;// initialized using below method getClazz()

    private Class getClazz() throws ClassNotFoundException {
        Type genericSuperclass = getClass().getGenericSuperclass();
        Type actualTypeArgument = ((ParameterizedType)genericSuperclass).getActualTypeArguments()[0];
        return Class.forName(actualTypeArgument.getTypeName());
    }

    // This method works but has an undesirable dependency to Jinq in method signature.
    public List<T> select(org.jinq.orm.stream.JinqStream.Where<T, RuntimeException> wherePredicate) {
        JinqJPAStreamProvider streams = new JinqJPAStreamProvider(entityManagerFactory);
        List<T> result = streams
          .streamAll(entityManager, clazz)
          .where( wherePredicate )
          .toList();
        return result;
    }    

    // Instead of the above select method I want to use the below method (currently named "select2")

    // The code below compiles but does not work in runtime.This is the method signature I would like to use.
    public List<T> select2(java.util.function.Predicate<T> predicate) {
        org.jinq.orm.stream.JinqStream.Where<T, RuntimeException> wherePredicate = u -> predicate.test(u);    
        JinqJPAStreamProvider streams = new JinqJPAStreamProvider(entityManagerFactory);
        List<T> result = streams
          .streamAll(entityManager, clazz)
          .where( wherePredicate )
          .toList();
        return result;
    }
...
public class PersonDataMapper extends DataMapperBase<Person> { ...
...
@Entity
@Access(AccessType.PROPERTY)
@Table(name="person")
public class Person implements Serializable {
    ...
    private int age;
    @Column(name = "Age")
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    ...

...
// The invocations below can be used e.g. from a test class
List<Person> persons  = personDataMapper.select( p -> p.getAge() > 20 );
List<Person> persons2 = personDataMapper.select2( p -> p.getAge() > 20 );

上述两种方法(select 和 select2)都可以编译,但第二个方法在运行时失败,并出现以下异常;

java.lang.IllegalArgumentException: Could not extract code from lambda. This error sometimes occurs because your lambda references objects that aren't Serializable.
    at org.jinq.jpa.transform.LambdaInfo.analyze(LambdaInfo.java:33)
    at org.jinq.jpa.transform.LambdaAnalysisFactory.extractSurfaceInfo(LambdaAnalysisFactory.java:7)
    at org.jinq.jpa.JPAQueryComposer.applyTransformWithLambda(JPAQueryComposer.java:269)
    at org.jinq.jpa.JPAQueryComposer.where(JPAQueryComposer.java:365)
    at org.jinq.jpa.JPAQueryComposer.where(JPAQueryComposer.java:1)
    at org.jinq.orm.stream.QueryJinqStream.where(QueryJinqStream.java:45)
    at org.jinq.jpa.QueryJPAJinqStream.where(QueryJPAJinqStream.java:86)

错误消息表明可能是 java.util.function.Predicate 未实现 Serialized 的问题。 (因为我的示例中的 Person 实现了可序列化)

不过,然后我尝试了另一个这样的界面:

public interface Predicate2<T> extends java.util.function.Predicate<T> , Serializable {}

当我使用它时,出现以下异常:

java.lang.IllegalArgumentException: Could not analyze lambda code
    at org.jinq.jpa.transform.LambdaAnalysis.fullyAnalyzeLambda(LambdaAnalysis.java:197)
    at org.jinq.jpa.transform.LambdaInfo.fullyAnalyze(LambdaInfo.java:116)
    at org.jinq.jpa.JPAQueryComposer.applyTransformWithLambda(JPAQueryComposer.java:278)
    at org.jinq.jpa.JPAQueryComposer.where(JPAQueryComposer.java:365)
    at org.jinq.jpa.JPAQueryComposer.where(JPAQueryComposer.java:1)
    at org.jinq.orm.stream.QueryJinqStream.where(QueryJinqStream.java:45)
    at org.jinq.jpa.QueryJPAJinqStream.where(QueryJPAJinqStream.java:86)    

所以,我的问题是是否有人可以提供上述方法“DataMapperBase.select2”的工作实现,即使用参数 java.util.function.Predicate 的方法?

最佳答案

由于 Java 8 lambda 实现的限制,Jinq 需要使用 Serialized lambda。不幸的是,Java 8 中默认的 Predicate 是不可序列化的,因此无法被 Jinq 分析。

我去年在 JVM 语言峰会上发表了关于 Jinq 工作原理的演讲。关于为什么 Jinq 无法使用默认 Java 8 Predicate 的部分在 17:16 左右进行了讨论:

https://youtu.be/JqCnZFzTR2I?t=17m16s

当前的 Jinq 字节码分析目前主要设计用于 Jinq 查询样式,因此如果您尝试填充任意 lambda,Jinq 的分析可能会失败。视频其他地方也对此进行了讨论。

关于java - 为 Jinq 创建查询时是否可以使用 Predicate<T> ?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34822975/

相关文章:

java - 使用 Java 和 Bouncy CaSTLe 进行 Rijndael 256 加密

java - 类 JavaLaunchHelper 在两个地方实现

jpa - Play [2.11] jpa事务

java - 在 JPA 中,如何知道持久性异常是否是由特定数据库触发器引起的?

intellij-idea - 在 lambda 中进行转换,在 IntelliJ 中标记为冗余

java - libgdx 调整带边框的图像大小

java - @Stateless webservice with JPA+JTA : How to commit changes of managed entity?

python - lambda 中的 bool 值求值

c# - 优化 linq 查询以对行进行排序?

java - 使用 Maven 和 ParameterSupplier 组织项目