spring data rest update produce cross join sql error - spring-data

I want to use spring data rest to update rows of certain user , but at run time this query has strange "cross join" added to the query .
spring data rest method
#Modifying
#Transactional
#Query("Update Notification n SET n.noticed = true Where n.notificationPost.owner.userId = 1 ")
public void postNoticed();
run time created query
Hibernate: update notification cross join set noticed=true where owner_id=?
My only concern is why "cross join" added as it gives sql error
org.postgresql.util.PSQLException: ERROR: syntax error at or near "cross"
I call this method directly by rest invoke , and also from mvc controller, both ways produce the same error
Thanks in advance.

Found solution as stated in http://forum.spring.io/forum/spring-projects/data/114271-spring-data-jpa-modifying-query-failure
"No joins, either implicit or explicit, can be specified in a bulk HQL query. Sub-queries can be used in the where-clause, where the subqueries themselves may contain joins. "(Hibernate doc reference: http://docs.jboss.org/hibernate/core.../#batch-direct)."
So I edited my code to use sub query
#Modifying
#Transactional
#Query("Update Notification n SET n.noticed = true Where n.notificationPost.postId in (SELECT n2.notificationPost.postId FROM Notification n2 where n2.notificationPost.owner.userId =:#{#security.principal.user.userId}) ")
public int postNoticed();

Related

Spring Data: Getting NonUniqueResult Problem for the query

Hello experts of the world. Need some help concerning executing a query with SpringData.
The expectation is to execute the Query below in the Spring Data annotation by combining with the repository method name (Automated Query Construction) to get a unique result. Apparently it fails from time to time by saying the result is not Unique.
The question here is if the method name is still considered in Query Construction while also executing the query in the annotation.
#Query("SELECT r from Revision r WHERE r.revisionBid = ?1 AND r.revisionStatusId = ?2 ORDER BY r.lastModifiedDate DESC")
Optional<Revision> findFirst(Integer revisionBid, Integer revisionStatusId);
Thanks in advance!
The query creation for limiting to 1 result is defined here with FIRST & TOP included in the method name.
https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#repositories.query-methods.query-creation
I don't think "findFirst" will work when you're using an #Query, as the query will be constructed from HQL expression in the #Query rather than the fluent API passing over the method name. Because of this, when the query returns multiple results, it will throw the exception as the Optional is told to wrap a single returned object, not a collection. Add a LIMIT clause to the HQL query and you should be good.

Spring JPA repository and QueryDsl how to force left join

Let's say I have two entities User and Task, each user can have one task.
The issue that I'm facing is if I have one record in the user table whose email starts with a and there are no records at all in the task table.
This snippet below will return no records although I would expect users that have mail starting with a.
UserRepository in example extends QuerydslPredicateExecutor.
userRepository.findAll(
QUser.user.email.startsWith("a")
.or(QUser.user.task.text.contains("something"))
)
If I check logs, Hibernate is creating cross join with user.task_id=task.id as a part of where clauses. This type of join automatically discards users whose mails are starting with a if they don't have a task assigned.
Is there a way to force usage of left join instead of a cross join in findAll method of the repository?
I know I can do it by using JPAQuery but then I would have to reimplement paging functionality...
JPAQuery query = new JPAQuery(entityManager);
query
.from(QUser.user)
.leftJoin(QTask.task)
// ...
I am not sure if we can do that since the findAll implementation is generated for us. However we can pass a predicate in the findAll method which will help deal with issue you are encountering.
You can try to do something like this:
QUser qUser = QUser.user;
QTask qTask = QTask.task;
JPQL<UserEntity> userJpqlQuery = JPAExpressions.selectFrom(qUser)
.leftjoin(qUser.task, qTask)
.where(qUser.email...., qTask.text...);
userRepository.findAll(qUser.in(userJpqlQuery));
In the code above I have used Querydsl, which is an alternative to CriteriaBuilder and is type safe. Then I have created a subquery to make the selection I want and return the all users matching the subquery.
In the end , hibernate should generate something like this:
select * from User qUser0 where qUser0.id.in(
select qUser1.id from User qUser1
left join Task qTask0 on
qUser1.taskId = qTask0.id
where ...
);

Query annotation not working for max(id)

I have a domain object GenJournal and it has an "id" member (Long) that's auto-generated. I also have a JPA repository that I've added ...
#Query("select coalesce(max(u.id), 0) from GenJournal u")
Long getMaxId();
The method getMaxId() returns zero or null before I added coalesce. I have two rows in my database with ids 1 and 2. Can anyone help me determine why this doesn't work?
I'm trying to get the latest or max id so that I can use the find method after to return the most recent GenJournal object in my service class.
I'm stumped and really need some options or strategy to determine why this doesn't work.
You could use "Native Query" feature by passing nativeQuery = true param into #Query annotation like this
#Query("select coalesce(max(u.id), 0) from Gen_Journal_Table u", NativeQuery = true)
Long getMaxId();
My issue was two-fold. First I was getting null without the use of "coalesce". That caused me to think that this didn't work. When I adopted the use of "coalesce" I didn't realize that my table had no records and was returning the zero (0). My table in the production profile did have two records and I was expecting an id of 2.
I was manually checking the wrong database and setting expectations that were incorrect.

Spring Data - JPA Repository - Too many parameters in a Query

I'm working with Spring Data Repositories manipulating data from a PostreSQL database.
In one of my repositories, I have a very simple query:
#Query(value = "FROM MyEntity entity WHERE entity.entityId IN (:entityIds)")
Collection<MyEntity> getEntitiesByIds(#Param("entityIds") Collection<Long> entityIds);
The problem is, when I pass in a collection that has more than 32k~ ids this query throws the following error:
java.io.IOException: Tried to send an out-of-range integer as a 2-byte value: 70324
I did some research and found out that this error happens because postgresql jdbc driver has a limit of 32k~ parameters you can pass in to a query. So I have two options here:
Do the query in batches. At this point I think this is my best shot but I was hoping to find a solution so I can pull my rows in one database call.
Build a temporary table with the ids, then join it against MyEntity table, but I'm running again into the same original problem. For this approach I was thinking to pass a String with all the concatenated ids and then use a native query to pull the information but it its not working as I expected:
#Query(value = ";WITH cteAllEntityIds(entity_id) AS( " +
"VALUES (?1)) " +
"SELECT p.* FROM my_entity e " +
"JOIN cteAllEntityIds cte ON e.entity_id = cte.entity_id", nativeQuery = true)
Collection<ProfileViewEntity> getProfilesByIds(String profileIds);
Of course, the parameter I'm passing in is a String whereas the entity_id is a number in the data base.
Do you know a way to accomplish approach #2? How to build queries dynamically and make jpa to interpret it that way?
Do you know a better solution to this problem?
Any suggestions are welcome.
Thanks.

Spring CRUD repository: is there findOneByMaxXYZColumn()?

My requirement:
fetch ONE object (e.g RetainInfo ) from table RETAIN_INFO if VERSION column has max value
Does CRUD repository support for an interface method like
findOneByMaxRetVersionAndCountry("DEFAULT")
Equivalent db2 sql:
select RET_ID, max(ri.RET_VERSION) from RETAIN_INFO ri where ri. COUNTRY='DEFAULT' group by RET_ID fetch first 1 rows only;
This query selects an ID, but I would actually want the RetainInfo object corresponding the SINGLE row returned by the query.
I prefer to get that without using custom query, i.e using findBy or some other method/interface supported by Spring CRUD.
You could use limiting in combination with sorting (spring data reference:limit query results). Declare a method similar to the following in your CrudRepository interface :
RetainInfo findTopByCountryOrderByRetVersionDesc(String country);
You can also use findFirst to get the first result. Before getting the result, make sure to use Orderby and then the ascending(Asc) or descending(Desc). As an example if you want to order by version and retrieve based on productName
RetainInfo findFirstByProductNameOrderByVersionDesc(String productName);
Spring Data doesn't provide an expression to select a max value. All supported query parts could be found in the Spring 1.2.0.RELEASE docs: Appendix A. Namespace reference or line 182 of org.springframework.data.repository.query.parser.Part.
Also feel free to create a feature request at Spring's Jira page.