在为 Jinq 创建查询时是否可以使用 Predicate<T>?
Is it possible to use Predicate<T> when creating queries for Jinq?
我的问题是关于 Jinq 的,我使用的是 1.8.9 版本,目前是最新版本。
我正在尝试使用 Jinq 实现一个通用的可重用 JPA(Java 持久性 API)类型安全查询方法,使用 Java 8 lambda(功能接口)谓词作为方法参数.
不幸的是,我不能让它与 Java 8 谓词一起工作,但我可以使用类似的谓词类型(由 Jinq 提供)作为方法参数,但我想避免在方法中依赖于 Jinq签名,因此如果可能的话更喜欢 Java 8 Predicate?
Jinq提供功能接口[=52=]:
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;
}
我可以通过在方法签名中使用上述接口来实现我想要的查询方法(但具有不希望的耦合):
public List<T> select(JinqStream.Where<T, Exception> wherePredicate)
我想在方法签名中使用标准 Predicate,而不是上述与 Jinq 的耦合:
public List<T> select(java.util.function.Predicate<T> wherePredicate) {
标准谓词定义如下:
@FunctionalInterface
public interface Predicate<T> {
public boolean test(T t);
}
因此我认为使用以下代码创建 Jinq 接口的 lambda 实现可能已经可以实现我想要的 select 方法:
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".[=20 中的 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 未实现 Serializable 可能是一个问题。
(因为我示例中的 Person 实现了 Serializable)
虽然,然后我尝试了另一个这样的界面:
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 需要使用 Serializable
lambda。可惜Java8中默认的Predicate
是不可Serializable的,所以Jinq无法解析。
我去年在 JVM 语言峰会上发表了关于 Jinq 工作原理的演讲。关于为什么 Jinq 不能使用默认 Java 8 Predicate
的部分在 17:16 标记周围讨论:
https://youtu.be/JqCnZFzTR2I?t=17m16s
当前的 Jinq 字节码分析当前主要设计用于 Jinq 查询样式,因此如果您尝试填充任意 lambda,Jinq 的分析很可能会失败。这也在视频的其他地方讨论过。
我的问题是关于 Jinq 的,我使用的是 1.8.9 版本,目前是最新版本。
我正在尝试使用 Jinq 实现一个通用的可重用 JPA(Java 持久性 API)类型安全查询方法,使用 Java 8 lambda(功能接口)谓词作为方法参数.
不幸的是,我不能让它与 Java 8 谓词一起工作,但我可以使用类似的谓词类型(由 Jinq 提供)作为方法参数,但我想避免在方法中依赖于 Jinq签名,因此如果可能的话更喜欢 Java 8 Predicate?
Jinq提供功能接口[=52=]:
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;
}
我可以通过在方法签名中使用上述接口来实现我想要的查询方法(但具有不希望的耦合):
public List<T> select(JinqStream.Where<T, Exception> wherePredicate)
我想在方法签名中使用标准 Predicate,而不是上述与 Jinq 的耦合:
public List<T> select(java.util.function.Predicate<T> wherePredicate) {
标准谓词定义如下:
@FunctionalInterface
public interface Predicate<T> {
public boolean test(T t);
}
因此我认为使用以下代码创建 Jinq 接口的 lambda 实现可能已经可以实现我想要的 select 方法:
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".[=20 中的 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 未实现 Serializable 可能是一个问题。 (因为我示例中的 Person 实现了 Serializable)
虽然,然后我尝试了另一个这样的界面:
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 需要使用 Serializable
lambda。可惜Java8中默认的Predicate
是不可Serializable的,所以Jinq无法解析。
我去年在 JVM 语言峰会上发表了关于 Jinq 工作原理的演讲。关于为什么 Jinq 不能使用默认 Java 8 Predicate
的部分在 17:16 标记周围讨论:
https://youtu.be/JqCnZFzTR2I?t=17m16s
当前的 Jinq 字节码分析当前主要设计用于 Jinq 查询样式,因此如果您尝试填充任意 lambda,Jinq 的分析很可能会失败。这也在视频的其他地方讨论过。