Compare Date entities in JPA Criteria API - java

Using JPA 2 with EclipseLink implementation.
I'm trying to build a dynamic query which should bring me some records persisted after a given date.
CriteriaBuilder builder = em.getCriteriaBuilder();
CriteriaQuery<Event> criteria = builder.createQuery(Event.class);
Root<Event> root = criteria.from(Event.class);
criteria.select(root);
criteria.distinct(true);
List<Predicate> predicates = new ArrayList<Predicate>();
//...
if (dateLimit != null){
ParameterExpression<Date> param = builder.parameter(Date.class, "dateLimit");
predicates.add(builder.lessThanOrEqualTo(root.get("dateCreated"), param));
}
lessThanOrEqualTo() and le() are the only two methods in the API which look like may help me in this case. This warning is thrown by the eclipse though:
Bound mismatch: The generic method lessThanOrEqualTo(Expression<? extends Y>, Expression<? extends Y>)
of type CriteriaBuilder is not applicable for the arguments (Path<Object>, ParameterExpression<Date>).
The inferred type Object is not a valid substitute for the bounded parameter
<Y extends Comparable<? super Y>>
I can imagine that I'm not taking the correct approach for this problem but I can't find anywhere some tips or pointers for a possible solution.

The problem is that with the string-based API it cannot infer the type for the result value of the get-Operation. This is explained for example in Javadoc for Path.
If you use
predicates.add(builder.lessThanOrEqualTo(root.<Date>get("dateCreated"), param));
instead, it will work fine, because it can figure out the return type from the type argument and will find out that it is comparable. Note, the use of a parameterised method invocation root.<Date>get(...) (see, e.g., When is a parameterized method call useful?).
Another (in my opinion better) solution is to use the metamodel based API instead of the string-based one. A simple example about canonical metamodel is given for example here. If you have more time to invest, this is a good article about static metamodel: Dynamic, typesafe queries in JPA 2.0

You need to use the generated metamodel to access the attributes is a really safe way. If you use Strings to refer to your attributes, types can only be deduced from the explicit generic type used when calling the method, or by a type cast, or by the automatic type inference done by the compiler:
Path<Date> dateCreatedPath = root.get("dateCreated");
predicates.add(builder.lessThanOrEqualTo(dateCreatedPath, dateLimit));

I was getting a similar error but with the syntax predicates.add(cb.greaterThan(article.get(Article_.created), since)); and found this page. The cause for me, turned out to be that I had upgraded my project from Java 1.7 to 1.8, and in the process had configured Maven to compile for Java 1.8 as well. I simply had to change Maven compiles back to 1.7, while keeping the rest of the project at 1.8, to fix the error.

I had the same problem, when I have worked with predicates. It worked great with all types except Date type. I tried all method and most simple way for me was:
predicates.add(builder.greaterThanOrEqualTo(root.get(criteria.getKey()), (Date)criteria.getValue()));
I added (Date) before criteria.getValue(), what help to recognize my value Object as Date type.

Related

How to create 'update' using multiple 'set' methods

Synopsis: I'm trying to create an SQL update using jOOQ
DSL.using(connection)
.update(DSL.table("dogs"))
.set(DSL.field("age"), DSL.field("age").add(1))
.set(DSL.field("rabies"), "true")
.where(DSL.field("id").eq("Kujo"))
.execute();
Issue:
The method set(Field<Object>, Object) is ambiguous for the type UpdateSetFirstStep<Record>
Question: How do I create this update using jOOQ?
You ran into this problem: Reference is ambiguous with generics
Fixing your query
It's always a good idea to attach data types with your jOOQ expressions. In your particular case, you can work around the problem by specifying things like:
DSL.field("age", SQLDataType.INTEGER)
Or, shorter, with the usual static imports:
field("age", INTEGER)
Using the code generator
However, jOOQ is best used with its code generator, see also this article here. Not only will you avoid problems like these, but you also get compile time type safety (of data types and meta data), advanced features like implicit joins and much more.
Your query would then look like this:
DSL.using(connection)
.update(DOGS)
.set(DOGS.AGE, DOGS.AGE.add(1))
.set(DOGS.RABIES, true)
.where(DOGS.ID.eq("Kujo"))
.execute();

JOOQ - convert fetchOne().into() to fetchValue(query)

In this answer, the author mentions that to avoid NPE the fetchValue(query) method can be used. The problem is that how exactly can the OP's code be converted into a query? I have similar code, pasted below, and would like to turn it into a query also.
return jooqDSLContext.select()
.from(CL_LOGIN)
.join(CL_USERS)
.on(CL_LOGIN.CL_USER_ID.eq(CL_USERS.CL_USER_ID))
.where(CL_USERS.EMAIL1.eq(email))
.fetchOne().into(CL_LOGIN);
JOOQ is very powerful and has many capabilities, but unfortunately everything I have tried to make a standalone query object with a join does not even compile.
EDIT: The answer provided did help me side-step the need to have a query object. But for those that want to know how to get a query object you can use the getQuery() method... see example below.
SelectQuery<Record1<String>> query = jooqDSLContext.select(USER_LOGIN.ACCOUNT_STATUS)
.from(USER_LOGIN)
.where(USER_LOGIN.USER_ID.eq(userId))
.getQuery();
Observe the signature of the method DSLContext.fetchValue(ResultQuery<R>), where R extends Record1<T>. This means that the expected row type of the query is Record1<T> with any arbitrary <T> type. In other words, you must project exactly one column in your SELECT clause.
You seem to want to project the entire record of type CL_LOGIN, so fetchValue() is not applicable to your use-case.
But note, there's also ResultQuery.fetchOneInto(Table), which is a convenience method wrapping that null check and the into() call. So, just write:
return jooqDSLContext.select()
.from(CL_LOGIN)
.join(CL_USERS)
.on(CL_LOGIN.CL_USER_ID.eq(CL_USERS.CL_USER_ID))
.where(CL_USERS.EMAIL1.eq(email))
.fetchOneInto(CL_LOGIN);

How to add generic type?

I have used below code in hibernate stuts2 java.
String hql = "from PostDetails";
Query postDetails = session.createQuery(hql); // Got warning from here
List<PostDetails> result = postDetails.list();
Warning is : Type safety: The expression of type List needs unchecked conversion to conform to List<PostDetails>
If I used this annotation #SuppressWarnings("unchecked") then warning is solved. But I don't want to any annotation.
How to fixed this warning?
I don't believe you can. Even the documentation and examples for Hibernate use a raw-type List as opposed to a typed one. However, it is very unlikely that you will run into an error with that list; Hibernate wouldn't return a list that isn't valid in that context.
The newer, standard version of the JPA API has typed queries which would avoid those warnings, and allow you to get back a properly typed list.
That would mean you could execute this to get the list that you want:
List<Student> = em.createQuery("select s from School s", Student.class).getResultList();

Converter affects routine auto generated code

I am using custom type converters with JOOQ, which work fine for tables but create uncompilable code for routines.
For example I have a public class DateConverter implements Converter<Date, LocalDate> but when auto-generating code some functions that accept a date as a parameter lead to the following code being generated:
public static final org.jooq.Parameter<java.time.LocalDate> VALUE_DATE = createParameter("value_date", org.jooq.impl.SQLDataType.DATE);
I understand that a related feature is planned for 3.5.0 that will probably solve the issue but I am on 3.4.4 and wonder if there is a workaround.
From your comments, I take that you're looking for a way to prevent jOOQ's code generator from erroneously applying converted data types to stored function parameters.
One workaround I can think of doing this right now is to ensure that none of the procedures / functions is accidentally matched in the code generator configuration. The <expression/> only matches either the fully qualified object name (e.g. the parameter) or the unqualified object name.
Another workaround would be to have two distinct code generation configurations: One for tables, the other for routines. That way, it would be much easier to configure converters...

What wrong here in addOrderBy() method of Jooq?

I am using Jooq and using below code
SelectQuery<Record> selectQuery = transaction.selectQuery();
Now Jooq telling it have a method Check Here where we can pass Collection and i am doing same, check below
List<SortField<T>> orderByValue1;
and then doing this
selectQuery.addOrderBy(orderByValue1);
but now in the above line i am getting compile time exception
The method addOrderBy(Field<?>...) in the type SelectQuery<Record> is not applicable for the arguments (List<SortField<T>>)
What i am doing wrong here?
There's a flaw in the jOOQ API, which is described in issue #2719. For the time being, the type of your orderByValue1 list must be adapted:
// Correct type:
List<SortField<?>> orderByValue1;
// Wrong type
List<SortField<T>> orderByValue1;
Note that the above types are not the same. For more info, consider reading the Oracle tutorial documentation on generics.

Categories