JPQL Create new object using a query with dynamic order by and limit - postgresql

I am able to create my custom DTO by using this JPQL query:
#Query("SELECT new com.mycompany.dto.UserDetailsDTO(vu.id, ru.active, ru.firstname, ru.lastname, ru.username, vu.logins, ru.email, COUNT(li.creator)) "
+ "FROM User vu inner join RemoteUser ru on vu.remoteUser = ru.username "
+ "inner join Item li on li.creator = vu.id "
+ "group by li.creator, ru.active, ru.firstname, ru.lastname, ru.username, vu.logins, vu.id, ru.email")
List<UserDetailsDTO> getAllUsers();
Now I want to add order by, ASC/DESC, limit, offset to the above query to get the result based on dynamic params something like this:
#Query("SELECT new com.mycompany.dto.UserDetailsDTO(vu.id, ru.active, ru.firstname, ru.lastname, ru.username, vu.logins, ru.email, COUNT(li.creator)) "
+ "FROM User vu inner join RemoteUser ru on vu.remoteUser = ru.username "
+ "inner join Item li on li.creator=vu.id "
+ "group by li.creator, ru.active, ru.firstname, ru.lastname, ru.username, vu.logins, vu.id, ru.email "
+ "order by = :orderBy :orderDir and offset = :pageNo and limit = :pageSize")
List<UserDetailsDTO> getAllUsers(#Param("pageNo") int pageNo,
#Param("pageSize") int pageSize,
#Param("orderDir") String orderDir,
#Param("orderBy") String orderBy);
But it is not working and the error is:
Caused by: org.hibernate.hql.internal.ast.QuerySyntaxException: unexpected token: = near line 1, column 427
Already tried with passing pageable as a param:
Page<UserDetailsDTO> getAllUsers(Pageable pageable)
and preparing page request like:
PageRequest pageRequest = PageRequest.of(pageNo, pageSize, Sort.by(Sort.Direction.valueOf(orderDir), orderFieldUid));
Also, I tried with
+ "order by ?4 ?3 and offset ?1 and limit ?2")
Is there any way to add dynamic params as order by and offset to it?
I want similar to something like this in JPQL.

Finally, I fixed it by using Page<UserDetailsDTO> getAllUsers(Pageable pageable) No need to pass params in the query. Hibernate will take the params internally from pageable

Related

JPA - Find items from a list that don't exist in a table

Given a list of emails, I need to find which ones don't exist in a table. Using SQL, I can do the following:
SELECT e.email
FROM
(
VALUES('email1'),('email2'),('email3'),('email4')
) AS e(email)
EXCEPT
SELECT username FROM dbo.UsersTbl;
How can I write equivalent JPQL? In the application, values email1, email2... need be dynamically built (not hardcoded) based on passed in list. Using a Spring Data JPA native query, I can do the following:
#Query( value =
"SELECT e.email " +
" FROM " +
"( " +
" VALUES('email1'),('email2'),('email3'),('email4') " +
" ) AS e(email) " +
" EXCEPT " +
" SELECT username FROM dbo.UsersTbl ",
nativeQuery=true)
List<String> findMissingEmails(List<String> emails);
But how can I pass in the list of emails to the query?
For fixed number of email arguments, this could work:
#Query( value =
"SELECT e.email " +
" FROM " +
"( " +
" VALUES(:email1),(:email2),(:email3),(:email4) " +
" ) AS e(email) " +
" EXCEPT " +
" SELECT username FROM dbo.UsersTbl ",
nativeQuery=true)
List<String> findMissingEmails(String email1, String email2, String email3, String email4);
For high and/or dynamic number of emails, a better approach could be to use NativeQuery constructed at runtime.
Old answer - more or less exactly the opposite of what was asked for, but I'll keep it here as reference.
Using of named parameter:
#Query("SELECT u.email FROM User AS u WHERE u.email NOT IN (:emails)")
List<String> findMissingEmails(#Param("emails")Collection<String> emails);
Alternatively, you could use a JPA query method:
#Repository
public interface UserRepository extends JpaRepository<User, Long> {
List<User> findAllByEmailNotIn(Collection<String> emails);
}
Unfortunately that method would fetch and return a list of Users instead of list of their emails.
To fetch just emails you could use a JPA projection.
Assuming that User entity has a field of type String named email, the following projection could be used:
public interface UserEmail {
String getEmail();
}
And this is the repository method:
#Repository
public interface UserRepository extends JpaRepository<User, Long> {
List<UserEmail> findAllByEmailNotIn(Collection<String> emails);
}

ERROR: operator does not exist: timestamp without time zone >= boolean Hint: No operator matches the given name and argument type(s)

Am trying to execute a Named Query using JPA. Query has to search and pull the records based on criteria of the query. Criteria is query should pull the records between specific given times(Records between From and To Dates provided) and name of the Application.
Query works fine, when executed in Postgresql. But, through JPA it gives error while executing the query
Here is my Query in PostgreSQL :
SELECT auditLog.busn_sys_id
, sourceSystem.busn_sys_full_nm
, auditLog.purge_ts
, auditLog.rec_purge_cnt
FROM rec_ret.rec_rtn_purge_adt auditLog
LEFT JOIN gbl_dm.gbl_busn_sys_dm sourceSystem on (auditLog.busn_sys_id = sourceSystem.busn_sys_id)
WHERE (auditLog.purge_ts BETWEEN '2019-08-19' AND '2019-08-25')
and sourceSystem.busn_sys_full_nm like 'PROFILE'
order by auditLog.busn_sys_id ;
Here is my JPA implementation to get Results List
try{
Query query = entityManager.createNativeQuery("SELECT auditLog.busn_sys_id, sourceSystem.busn_sys_full_nm, auditLog.purge_ts, auditLog.rec_purge_cnt " +
"FROM rec_ret.rec_rtn_purge_adt auditLog " +
"LEFT JOIN gbl_dm.gbl_busn_sys_dm sourceSystem on (auditLog.busn_sys_id = sourceSystem.busn_sys_id) " +
"WHERE (auditLog.purge_ts BETWEEN auditLog.purge_ts = :requestedFrom AND auditLog.purge_ts = :requestedTo) " +
//"WHERE (auditLog.purge_ts BETWEEN requestedFrom = (?) AND requestedTo = (?)) " +
"and sourceSystem.busn_sys_full_nm = :sourceSystem " +
"order by auditLog.busn_sys_id ");
query.setParameter("requestedFrom",auditLogCriteria.getFromDate());
query.setParameter("requestedTo",auditLogCriteria.getToDate());
query.setParameter("sourceSystem",auditLogCriteria.getSourceSystem());;
query.executeUpdate();
return query.getResultList();
}catch (Exception e){
logger.error("Fetching of logs failed with message : " + e.getMessage());
throw e;
}
Here is my implementation of Entity/Model class code
#NotNull (message = "Name of the Source System should be entered")
private String sourceSystem;
#NotNull (message = "Specify FromDate to filter records")
#JsonFormat(shape = JsonFormat.Shape.STRING, pattern="yyyy-MM-dd HH:mm:ss[.SSS]")
#Convert(converter = LocalDateTimeConverter.class)
private LocalDateTime fromDate;
#NotNull (message = "Specify ToDate to filter records")
#JsonFormat(shape = JsonFormat.Shape.STRING, pattern="yyyy-MM-dd HH:mm:ss[.SSS]")
#Convert(converter = LocalDateTimeConverter.class)
private LocalDateTime toDate;
\\ Getters and Setters
Here is the Error :
Hibernate: SELECT auditLog.busn_sys_id, sourceSystem.busn_sys_full_nm, auditLog.purge_ts, auditLog.rec_purge_cnt FROM rec_ret.rec_rtn_purge_adt auditLog LEFT JOIN gbl_dm.gbl_busn_sys_dm sourceSystem on (auditLog.busn_sys_id = sourceSystem.busn_sys_id) WHERE (auditLog.purge_ts BETWEEN auditLog.purge_ts = ? AND auditLog.purge_ts = ?) and sourceSystem.busn_sys_full_nm = ? order by auditLog.busn_sys_id
[ERROR] 2019-08-29 10:02:00.558 [http-nio-8080-exec-2] SqlExceptionHelper - ERROR: operator does not exist: timestamp without time zone >= boolean
Hint: No operator matches the given name and argument type(s). You might need to add explicit type casts.
Position: 267
javax.persistence.PersistenceException: org.hibernate.exception.SQLGrammarException: could not execute statement
at org.hibernate.internal.ExceptionConverterImpl.convert(ExceptionConverterImpl.java:154)
at org.hibernate.internal.ExceptionConverterImpl.convert(ExceptionConverterImpl.java:181)
at org.hibernate.query.internal.AbstractProducedQuery.executeUpdate(AbstractProducedQuery.java:1593)
So, result should be all the records matching with the criteria has to be returned as a response to JSON request
This part of your JPA query looks wrong:
BETWEEN auditLog.purge_ts = :requestedFrom AND auditLog.purge_ts = :requestedTo
I think it should be:
BETWEEN :requestedFrom AND :requestedTo

"Column count does not match" for h2 database Select query using projection in Spring-Boot #DataJpaTest

I have a Spring-Boot app that has some native queries that use content projection. It runs Postgres in production and works fine. I'm trying to set up integration tests for the repositories using #DataJpaTest and a h2 in-memory database, but my queries that are using content projection are failing with a JdbcSQLException out of the driver:
org.h2.jdbc.JdbcSQLException: Column count does not match
I successfully save to the TestEntityManager, so there are records in the database, but I am unable to invoke the SELECT via the repository method. It works properly in production on Postgres -- is this a limitation to h2 and is there a workaround I could apply so I can properly test this?
The repository method looks like this (one inner join, two params in the where clause, table names and columns changed to protect the guilty):
public interface OrderRepository extends PagingAndSortingRepository<Order, Long> {
#Query(nativeQuery = true,
value = "SELECT order.id, order.total, pizza.name " +
"FROM example.order " +
"INNER JOIN example.pizza USING (pizza_id) " +
"WHERE order.customer_id = :custId " +
"AND order.order_date = :orderDate ",
countQuery = "SELECT count(order.id) " +
"FROM example.order " +
"INNER JOIN example.pizza USING (pizza_id) " +
"WHERE order.customer_id = :custId " +
"AND order.order_date = :orderDate")
<T> Page<T> findAllByCustIdAndOrderDate(String custId, OffsetDateTime orderDate, Pageable paging, Class<T> type);
}
And the projection looks like this:
public interface PizzaOrderProjection {
Long getId();
Double getTotal();
String getName();
}
The exception triggers when I call findAllByCustIdAndOrderDate, and the SQL statement that it prints is causing it is the SELECT. The SELECT it prints looks perfectly normal:
Hibernate:
/* dynamic native SQL query */ SELECT
order.id,
order.total,
pizza.name
FROM
example.order
INNER JOIN
example.pizza USING (pizza_id)
WHERE
order.customer_id = ?
AND order.order_date = ? limit ?
2019-04-09 12:42:18.704 WARN 17568 --- [ main] o.h.engine.jdbc.spi.SqlExceptionHelper : SQL Error: 21002, SQLState: 21S02
2019-04-09 12:42:18.708 ERROR 17568 --- [ main] o.h.engine.jdbc.spi.SqlExceptionHelper : Column count does not match; SQL statement:
It turns out that the error message actually has nothing to do with the underlying issue.
The H2 database does not support the using keyword on the inner join clause, only the on keyword.
The issue was resolved by changing the inner join to use on instead, like this:
public interface OrderRepository extends PagingAndSortingRepository<Order, Long> {
#Query(nativeQuery = true,
value = "SELECT order.id, order.total, pizza.name " +
"FROM example.order " +
"INNER JOIN example.pizza ON order.pizza_id = pizza.pizza_id " +
"WHERE order.customer_id = :custId " +
"AND order.order_date = :orderDate ",
countQuery = "SELECT count(order.id) " +
"FROM example.order " +
"INNER JOIN example.pizza ON order.pizza_id = pizza.pizza_id " +
"WHERE order.customer_id = :custId " +
"AND order.order_date = :orderDate")
<T> Page<T> findAllByCustIdAndOrderDate(String custId, OffsetDateTime orderDate, Pageable paging, Class<T> type);
}
This change makes the queries valid in both postgres and h2.

JPQL, How to execute a query without a matched entity type?

Say I get an entity from database, employee, and it has 3 attributes :
1.name(String)
2.id(String)
3.department(String).
Now I want to do a group-function query like: select e.department, count(e) from employee e group by e.department. I'm using EntityManager to create an executable query but question is no such entity fits the result, what should I do to do such a query and obtain the result?
Your query will return results like this:
Department Count
Dept1 10
Dept2 20
Assuming you have a ResultSet object:
while(rs.next()){
String dept = rs.getString(1); //You can use column name if its defined
int count = rs.getInt(2);
}
select e.department, count(e) from employee e group by e.department
This jpql will give result as Object[] .
Then cast the result to required object.
for (Object[] o : resultList) {
String department = (String) o[0];
int e = (Integer) o[1];
System.out.println(department + " : " + e);
}
I think this will help you.

How to write JPA Query to the equivalent mysql query

I want to write equivalent JPA query to the following mysql query
select active_package,sum(duration),sum(charge),count(*)
from User
where call_type="MO"
and start_date between '2012-02-01' and '2012-02-09'
group by active_package;
For JPA Query the corresponding Attributes are below.
activePackage,callType,duration,charge,startDate
Entity class is User.
I want to use the createQuery() of JPA.
Can any one tell me or give me the link where can i find the solution for this.
Try this one, it should work, if not, please comment, we will get it work :).
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<Tuple> cq = cb.createQuery(Tuple.class);
Root<User> entity = cq.from(User.class);
cq.multiselect(entity.get("activePackage"),
cb.sum(entity.get("duration").as(Long.class)),
cb.sum(entity.get("charge").as(Double.class),
cb.count(entity).as(Long.class)));
cq.where(cb.equal(entity.get("callType"), "MO"),
cb.between(entity.get("startDate").as(Date.class),
new Date(), new Date()));
cq.groupBy(entity.get("activePackage"));
List<Tuple> resultList = entityManager.createQuery(cq).getResultList();
for (Tuple result : resultList) {
System.out.println(result.get(0) + " " + result.get(1)
+ " " + result.get(2) + " " + result.get(3));
}
Also if you want to filter only by date, but have timestamp in your model, you can check this Compare date's date part only with Timestamp in Hibernate answer.
Also JPA provides constructing result classes as return values, so you can group your columns. Read more.