My question is about Jinq , and I am using version 1.8.9 which currently is the latest release.
I am trying to use Jinq for implementing a general reusable JPA (Java Persistence API) typesafe query method with the Java 8 lambda (functional interface) Predicate as method parameter.
Unfortunately, I can not make it work with the Java 8 Predicate but instead I can use a similar predicate type (provided by Jinq) as method parameter, but would like to avoid dependencies to Jinq in method signatures, and therefore would prefer the Java 8 Predicate if possible?
Jinq provides the functional 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;
}
I can implement the query method I want (but with the undesirable coupling) by using the above interface in the method signature like this:
public List<T> select(JinqStream.Where<T, Exception> wherePredicate)
Instead of the above coupling to Jinq in the method signature I would like to use the standard Predicate as below:
public List<T> select(java.util.function.Predicate<T> wherePredicate) {
The standard Predicate is defined like this:
@FunctionalInterface
public interface Predicate<T> {
public boolean test(T t);
}
Therefore I thought it might have been possible to implement my desired select method using the following code for creating a lambda implementation of the Jinq interface:
public List<T> select(java.util.function.Predicate<T> predicate) {
org.jinq.orm.stream.JinqStream.Where<T, Exception> wherePredicate = u -> predicate.test(u);
...
However, it does not work but results in an IllegalArgumentException (see stacktrace pasted further down below)
Below is some more code illustrating what I am trying to do.
The problem I am trying to illustrate is that I want to use the Predicate parameter in the below method "DataMapperBase.select2" instead of the Jinq specific Where parameter as in below method "DataMapperBase.select".
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 );
Both above methods (select and select2) compiles but the second fails in runtime with the following exception;
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)
The error message indicates it might be a problem with java.util.function.Predicate not implementing Serializable. (since the Person in my example implements Serializable)
Though, then I experimented with another interface like this:
public interface Predicate2<T> extends java.util.function.Predicate<T> , Serializable {}
When I used it instead, I got the following exception:
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)
So, my question is if someone can provide a working implementation of the above method "DataMapperBase.select2" i.e. the method using parameter java.util.function.Predicate ?