I have some query like
SELECT * FROM JobTable
WHERE isnull(retryCount,0)<3
AND updatedOn < dateadd(MI,-5,getdate())
How to convert it to Criteria api calls? Point to use Criteria is to allow refactoring if fields names will be changed.
For simple things this is looks like
criteria.add(Restrictions.lt(JobTable.RETRYCOUNT_FULL, 3));
But what about my case?
criteria.add(Restrictions.lt(JobTable.UPDATEON_FULL, <???>);
criteria.add(Restrictions.lt( <someexpression(JobTable.RETRYCOUNT_FULL)> , 3));
I'm not sure of the correct name but hibernate has some standard functions which are mapped to the dialects functions. They can be used like this
Projections.sqlFunction("dateadd", Projections.property("property"), Hibernate.dateTime);
Hibernate Criteria API has Restrictions.sqlRestriction functions. In these functions you write your SQL the way you like and Hibernate embeds it in to your SQL.
criteria.add(Restrictions.sqlRestriction("isnull({alias}" + JobTable.RETRYCOUNT_FULL + ", 0) < ?", 3, new IntegerType());
Related
The problem I'm trying to solve here is, filtering the table using dynamic queries supplied by the user.
Entities needed to describe the problem:
Table: run_events
Columns: user_id, distance, time, speed, date, temperature, latitude, longitude
The problem statement is to get the run_events for a user, based on a filterQuery.
Query is of the format,
((date = '2018-06-01') AND ((distance < 20) OR (distance > 10))
And this query can combine multiple fields and multiple AND/OR operations.
One approach to solving this is using hibernate and concatenating the filterQuery with your query.
"select * from run_events where user_id=:userId and "+filterQuery;
This needs you to write the entire implementation and use sessions, i.e.
String q = select * from run_events where user_id=:userId and "+filterQuery;
Query query = getSession().createQuery(q);
query.setParameter("userId", userId);
List<Object[]> result = query.list();
List<RunEvent> runEvents = new ArrayList<>();
for(Object[] obj: result){
RunEvent datum = new RunEvent();
int index = -1;
datum.setId((long) obj[++index]);
datum.setDate((Timestamp) obj[++index]);
datum.setDistance((Long) obj[++index]);
datum.setTime((Long) obj[++index]);
datum.setSpeed((Double) obj[++index]);
datum.setLatitude((Double) obj[++index]);
datum.setLongitude((Double) obj[++index]);
datum.setTemperature((Double) obj[++index]);
runEvents.add(datum);
}
This just doesn't seem very elegant and I want to use the #Query annotation to do this i.e.
#Query(value = "select run_event from RunEvent where user_id = :userId and :query order by date asc")
List<RunEvent> getRunningData(#Param("userId") Long userId,
#Param("query") String query,
);
But this doesn't work because query as a parameter cannot be supplied that way in the query.
Is there a better, elegant approach to getting this done using JPA?
Using Specifications and Predicates seems very complicated for this sort of a query.
To answer the plain question: This is not possible with #Query.
It is also in at least 99% of the cases a bad design decision because constructing SQL queries by string concatenation using strings provided by a user (or any source not under tight control) opens you up for SQL injection attacks.
Instead you should encode the query in some kind of API (Criteria, Querydsl, Query By Example) and use that to create your query. There are plenty of questions and answers about this on SO so I won't repeat them here. See for example Dynamic spring data jpa repository query with arbitrary AND clauses
If you insist on using a SQL or JPQL snippet as input a custom implementation using String concatenation is the way to go.
This opens up attack for SQL injection. Maybe that’s why this feature is not possible.
It is generally a bad idea to construct query by appending random filters at the end and running them.
What if the queryString does something awkward like
Select * from Foo where ID=1234 or true;
thereby returning all the rows and bringing a heavy load on DB possibly ceasing your whole application?
Solution: You could use multiple Criteria for filtering it dynamically in JPA, but you’ll need to parse the queryString yourself and add the necessary criteria.
You can use kolobok and ignore fields with null values.
For example create one method like bellow
findByUserIdAndDistanceaLessThanAndDistancebGreaterThan....(String userid,...)
and call that method only with the filter parameters while other parameters are null
How can I use find_in_set in jpa?
I need to achieve like this
SQL - select * from teacher where find_in_set("5", deptIds) and id = 101
where deptIds have comma separated ids (I know it's bad idea but legacy.)
To do so I had been tried using Criteria but not found any Restrictions that can fulfill find_in_set.
Note - need possible solution with Criteria and Restrictions
criteriaBuilder.function("find_in_set", Boolean.class,
criteriaBuilder.literal(s),
root.get("field"))
Here is an example:
Here is java code snippet.
list.add(cb.greaterThan(cb.function("FIND_IN_SET", Integer.class,
cb.literal(val.toString()), root.get(attributeName)), 0));
select t from Teacher t where find_in_set("5", t.deptIds) = 0 and t.id = 101
I am have a problem where i need to join two tables using the LEAST and GREATEST functions, but using JPA CriteriaQuery. Here is the SQL that i am trying to duplicate...
select * from TABLE_A a
inner join TABLE_X x on
(
a.COL_1 = least(x.COL_Y, x.COL_Z)
and
a.COL_2 = greatest(x.COL_Y, x.COL_Z)
);
I have looked at CriteriaBuilder.least(..) and greatest(..), but am having a difficult time trying to understand how to create the Expression<T> to pass to either function.
The simplest way to compare two columns and get the least/greatest value is to use the CASE statement.
In JPQL, the query would look like
select a from EntityA a join a.entityXList x
where a.numValueA=CASE WHEN x.numValueY <= x.numValueZ THEN x.numValueY ELSE x.numValueZ END
and a.numValueB=CASE WHEN x.numValueY >= x.numValueZ THEN x.numValueY ELSE x.numValueZ END
You can code the equivalent using CriteriaBuilder.selectCase() but I've never been a big fan of CriteriaBuilder. If requirements forces you to use CriteriaBuilder then please let me know and I can try to code the equivalent.
CriteriaBuilder least/greatest is meant to get the min/max value of all the entries in one column. Let's say you want to get the Entity that had the alphabetically greatest String name. The code would look like
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery query = cb.createQuery(EntityX.class);
Root<EntityX> root = query.from(EntityX.class);
Subquery<String> maxSubQuery = query.subquery(String.class);
Root<EntityX> fromEntityX = maxSubQuery.from(EntityX.class);
maxSubQuery.select(cb.greatest(fromEntityX.get(EntityX_.nameX)));
query.where(cb.equal(root.get(EntityX_.nameX), maxSubQuery));
I created a sample Spring Data JPA app that demonstrates these JPA examples at
https://github.com/juttayaya/stackoverflow/tree/master/JpaQueryTest
It turns out that CriteriaBuilder does support calling LEAST and GREATEST as non-aggregate functions, and can be accessed by using the CriteriaBuilder.function(..), as shown here:
Predicate greatestPred = cb.equal(pathA.get(TableA_.col2),
cb.function("greatest", String.class,
pathX.get(TableX_.colY), pathX.get(TableX_.colZ)));
In SQL Server i am using this query
select *
from Unit c
ORDER BY CONVERT(INT, LEFT(name, PATINDEX('%[^0-9]%', name + 'z')-1)) desc;
I want this query to use in Hibernate. when I use this in Hibernate I got error
java.lang.IllegalArgumentException: org.hibernate.hql.ast.QuerySyntaxException:unexpected token: LEFT near line 1, column 122
[SELECT c FROM models.entities.UnitEntity c WHERE c.expSetId = :expSetId AND isWaitArea=:isWaitArea ORDER BY CONVERT(INT, LEFT(name, PATINDEX('%[^0-9]%', name + 'z')-1)) asc]
some of that is not in the hibernate dialect, you can change left with substring, convert with cast. and as for patindex i couldn't find the substitution. you either can add pathindex to the constructor of the dialect that you use
registerFunction( "patindex", new StandardSQLFunction("patindex") );
or create patindex() to a stored procedure.
then you can use something like this:
from Unit c order by cast(substring(name, 0, PATINDEX('%[^0-9]%', name + 'z')-1) as integer);
or you can use locate() instead of patindex(), but i think it doesn't support regular expression.
I am pretty sure that you can use SQL query with hibernate as well. When you create your hibernate session, you can use something like
session.createSQLQuery("Your Query Here")
Hope this helps.
Is there a way to have named parameters in Java MySQL query?
Like this:
SELECT * FROM table WHERE col1 = :val1 AND col2 = :val2
instead of this:
SELECT * FROM table WHERE col1 = ? AND col2 = ?
UPDATE: Im' using java.sql.*, however would be interested in alternatives capable of this.
Maybe Hibernate is good choice for you. It provided the query style as your description, and it's so powerful and convenient to do persistence work that you'll feel cool.
e.g
Query query = sesion.createQuery("from Student s where s.age = :age");
query.setProperties(student);
see the doc:http://docs.jboss.org/hibernate/orm/3.2/api/org/hibernate/Query.html#setParameter(java.lang.String, java.lang.Object)
The excellent JDBI library lets you do this and much more.
Yes, there are alternatives. By example, with javax.persistence.* you can achieve that quite easily.
http://docs.oracle.com/javaee/6/tutorial/doc/bnbrg.html
An entity manager will allow you to create dynamic (parametrized) queries with the methods EntityManager.createQuery, and EntityManager.createNamedQuery.