Spring Data JPA query with inner conditions - java

I want to be able to use inner conditions withing spring data jpa search query:
#Query("SELECT u FROM User u "
+ "WHERE (:isBlocked IS NULL OR (u.unblockDate IS NOT NULL OR u.unblockDate > CURRENT_DATE) = :isBlocked)" )
Page<User> filter(#Param("isBlocked") Boolean isBlocked,
Pageable pageable);
such logic works fine in sql but I can't make it work with spring-data-jpa. Is it correct syntax in spring data jpa?
UPDATE I'm getting the following exception:
Caused by: org.hibernate.hql.internal.ast.QuerySyntaxException: unexpected AST node: OR near line 1, column 107 [SELECT u FROM User u WHERE (:isBlocked IS NULL OR (u.unblockDate IS NOT NULL OR u.unblockDate > CURRENT_DATE) = :isBlocked)]

Related

syntax error when trying to call JPA function with custom query

Inside my Repository, i have following
public interface TenantRepository extends JpaRepository<Tenant, UUID> {
...
#Query("select substring(email from '#(.*)$') as domain from Tenant group by domain")
public List<String> findAllDomain();
}
When i tried to excute it, i got an error which says:
Syntax error in SQL statement "select substring(tenant0_.email, [*]from, '#(.*)ParseError: KaTeX parse error: Can't use function '\)' in math mode at position 2: '\̲)̲ as c…') as col_0_0_ from tenants tenant0_ group by col_0_0_
So i change the query to this:
"select substring(email from '#(.*)\\$') as domain from Tenant group by domain"
But it got another error:
org.h2.jdbc.JdbcSQLSyntaxErrorException: Syntax error in SQL statement "select substring(tenant0_.email, [*]from, '#(.*)\\$') as col_0_0_ from tenants tenant0_ group by col_0_0_"; expected "INTERSECTS (, NOT, EXISTS, UNIQUE, INTERSECTS"; SQL statement:
select substring(tenant0_.email, from, '#(.*)\$') as col_0_0_ from tenants tenant0_ group by col_0_0_
When i try the query on my PgAdmin4, it works.
Any suggestions?
thx.
I assume you are trying to get all the email domains.
In JPA you can use combination of locate and substring functions to achieve this
#Query("select substring(t.email, locate('#', t.email) + 1) as domain from Tenant t group by domain")
public List<String> findAllDomain();
emails in db
aaa#domain1.com
bbbb#domain2.com
ccccccc#domain2.com
query response
domain1.com, domain2.com

Spring Data JPA - SQL Grammar exception when trying to use IN clause with UPDATE Query

I am trying to update the entities in a certain list using Spring Data JPA. However, I'm getting SQL Grammar Exception.
This is the method:
#Modifying
#Query("UPDATE Call c set c.locationLocked = false, c.locationLockedBy = null, c.locationLockedOn = null WHERE c.callIdentifier IN :timedOutLockedCallsIdentifiers AND c.audit.retired = false")
int expireTimedOutLockedCalls(#Param("timedOutLockedCallsIdentifiers") List<String> timedOutLockedCallsIdentifiers);
And this is the root cause:
Caused by: org.postgresql.util.PSQLException: ERROR: syntax error at or near ")"
You are passing null or empty list in IN clause of the query.
Please check timedOutLockedCallsIdentifiers before calling above query.
It would probably be easier to do it more programatically then in a query
In your repository have the following
#Query("SELECT c FROM Call c WHERE c.callIdentifier IN :timedOutLockedCallsIdentifiers AND c.audit.retired = :retired")
List<Call> findByCallIdentifiers(List<String> timedOutLockedCallsIdentifiers, Boolean retired)
Then in your class
List<Call> updated = callRepo.findByCallIdentifiers(identifiers, false)
for(Call c : updated) {
c.setLocationLocked(false);
c.setLocationLockedBy(null);
c.setLocationLockedOn(null);
}
callRepo.saveAll(updated);
This will call the proper underlying updates as needed and cant let spring handle the query syntax properly

Spring Data JPA: Projection gets broken after adding pagination

I have a query that uses projections, it works well as long as the return type is List, but it stops working after adding pagination.
Here is the the working code:
#Query("SELECT DISTINCT \n" +
" new com.mycompany.dto.MyDto(me.property1, me.property2, ...) \n" +
"FROM MyEntiry me...")
List<MyDto> findEntities();
I need to extend it adding pagination, so I change it to:
#Query("SELECT DISTINCT \n" +
" new com.mycompany.dto.MyDto(me.property1, me.property2, ...) \n" +
"FROM MyEntiry me...")
Page<MyDto> findEntities(Pageable pageable);
Once I do that the context starts failing because while parsing it inserts select count(me) between SELECT and FROM statements so that the query become invalid:
SELECT DISTINCT
new com.mycompany.dto.MyDto(me.property1, me.property2, ...)
select count(me) FROM com.mycompany.MyEntiry me ...
The context fails with the following exception:
Caused by: org.hibernate.hql.internal.ast.QuerySyntaxException:
unexpected token: select near line 3, column 1 [SELECT DISTINCT new
com.mycompany.dto.MyDto(me.property1, me.property2, ...) select
count(me) FROM com.mycompany.MyEntiry me ...]
at org.hibernate.hql.internal.ast.QuerySyntaxException.convert(QuerySyntaxException.java:74)
at org.hibernate.hql.internal.ast.ErrorCounter.throwQueryException(ErrorCounter.java:91)
at org.hibernate.hql.internal.ast.QueryTranslatorImpl.parse(QueryTranslatorImpl.java:291)
at org.hibernate.hql.internal.ast.QueryTranslatorImpl.doCompile(QueryTranslatorImpl.java:186)
at org.hibernate.hql.internal.ast.QueryTranslatorImpl.compile(QueryTranslatorImpl.java:141)
at org.hibernate.engine.query.spi.HQLQueryPlan.(HQLQueryPlan.java:115)
at org.hibernate.engine.query.spi.HQLQueryPlan.(HQLQueryPlan.java:77)
at org.hibernate.engine.query.spi.QueryPlanCache.getHQLQueryPlan(QueryPlanCache.java:153)
at org.hibernate.internal.AbstractSharedSessionContract.getQueryPlan(AbstractSharedSessionContract.java:553)
at org.hibernate.internal.AbstractSharedSessionContract.createQuery(AbstractSharedSessionContract.java:662)
... 88 more
Question: How to make it work? Is it Spring Data bug?
Note:
The query I added is oversimplified, my real query in fact gathers different values from different tables and I can't implement it without projections
I'm using Spring Boot 1.5.8.RELEASE
Try to use 'native' Spring Data JPA projections.
This should work well:
public interface MyProjection {
String getProperty1();
//...
}
Page<MyProjection> getDistinctAllBy(Pageable pageable);
But, if your query joins many tables you cannot use the pagination without some pain (for example: 1, 2)
UPDATED
Try to add parameter countQuery to #Query annotation:
#Query(value = "select ...", countQuery = "select count(me) from MyEntiry me")
Page<MyDto> findEntities(Pageable pageable);

NoViableAltException: Unexpected token in #NamedQuery

I have the following query, which is working when I use it directly at the db:
#NamedQuery(name = "Sentitems.findWhereSendingDateTimeIsYesterdayByStatus",
query = "SELECT s FROM Sentitems s WHERE s.status = :status AND DATE_FORMAT(s.sendingDateTime, '%Y-%m-%d') = SUBDATE(CURDATE(),1)")
When running the application, a NoViableAltException is thrown:
Exception Description: Syntax error parsing the query [Sentitems.findWhereSendingDateTimeIsYesterdayByStatus: SELECT s FROM Sentitems s WHERE s.status = :status AND DATE_FORMAT(s.sendingDateTime, '%Y-%m-%d') = SUBDATE(CURDATE(),1)], line 1, column 66: unexpected token [(].
Internal Exception: NoViableAltException(83#[()* loopback of 383:9: (d= DOT right= attribute )*])
Try with #NamedNativeQuery. You seem to be using some DB specific syntax.
Like Balaji Krishnan said in the comments, the solution is to use #NamedNativeQuery instead of #NamedQuery.

Spring JPA - "in" word in package name of Entity Class - Results in JPQL Error

Issue observed with Spring-Data with Hibernate - Spring 4.1.5.RELEASE, Spring-Data - 1.8.0.RELEASE, Hibernate - 4.3.8.Final
Domain name of company ends with .in as it is in India.
Hence, my Java packages starts with "in.something....".
When using JPA Repository, if I have to use custom query on a method like below:
#Query(value = "SELECT o FROM UserEntity o, UserAttribute u where o.organization.organizationType.code in ?1 and o.status in ?2 and u.attrKey = 'SOL_ID' and u.attrValue in ?3 and u.userEntity = o")
Page<UserEntity> findByOrganizationAndStatusAndSolId(List<String> organizationTypes, List<StatusMaster> statusList, List<String> solId, Pageable pageable);
The application startup is failing because the JPA Query has fully qualified name for classes, and since package starts with "in", it thinks that there is a validation error.
Caused by: java.lang.IllegalArgumentException:
org.hibernate.hql.internal.ast.QuerySyntaxException: expecting OPEN,
found '.' near line 1, column 44 [SELECT o FROM
in.something.UserEntity o, in.something.UserAttribute u where
o.organization.organizationType.code in ?1 and o.status in ?2 and
u.attrKey = 'SOL_ID' and u.attrValue in ?3 and u.userEntity = o]
Caused by: java.lang.IllegalArgumentException: Validation failed for query for method public abstract org.springframework.data.domain.Page in.something.UserRepository.findByOrganizationAndStatusAndSolId(java.util.List,java.util.List,java.util.List,org.springframework.data.domain.Pageable)!
at org.springframework.data.jpa.repository.query.SimpleJpaQuery.validateQuery(SimpleJpaQuery.java:97)
at org.springframework.data.jpa.repository.query.SimpleJpaQuery.<init>(SimpleJpaQuery.java:66)
at org.springframework.data.jpa.repository.query.SimpleJpaQuery.fromQueryAnnotation(SimpleJpaQuery.java:169)
at org.springframework.data.jpa.repository.query.JpaQueryLookupStrategy$DeclaredQueryLookupStrategy.resolveQuery(JpaQueryLookupStrategy.java:114)
at org.springframework.data.jpa.repository.query.JpaQueryLookupStrategy$CreateIfNotFoundQueryLookupStrategy.resolveQuery(JpaQueryLookupStrategy.java:160)
at org.springframework.data.jpa.repository.query.JpaQueryLookupStrategy$AbstractQueryLookupStrategy.resolveQuery(JpaQueryLookupStrategy.java:68)
at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.<init>(RepositoryFactorySupport.java:290)
at org.springframework.data.repository.core.support.RepositoryFactorySupport.getRepository(RepositoryFactorySupport.java:158)
at org.springframework.data.repository.core.support.RepositoryFactoryBeanSupport.getObject(RepositoryFactoryBeanSupport.java:162)
at org.springframework.data.repository.core.support.RepositoryFactoryBeanSupport.getObject(RepositoryFactoryBeanSupport.java:44)
at org.springframework.beans.factory.support.FactoryBeanRegistrySupport.doGetObjectFromFactoryBean(FactoryBeanRegistrySupport.java:142)
... 37 more
Caused by: java.lang.IllegalArgumentException: org.hibernate.hql.internal.ast.QuerySyntaxException: expecting OPEN, found '.' near line 1, column 44 [SELECT o FROM in.something.UserEntity o, in.something.UserAttribute u where o.organization.organizationType.code in ?1 and o.status in ?2 and u.attrKey = 'SOL_ID' and u.attrValue in ?3 and u.userEntity = o]
at org.hibernate.ejb.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1364)
at org.hibernate.ejb.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1300)
at org.hibernate.ejb.AbstractEntityManagerImpl.createQuery(AbstractEntityManagerImpl.java:294)
I tried variety of options, but with no luck:
Using Suggestions from here to use escape table name in Entity
Use <delimited-identifiers/>
Specify a name in #Entity(name="otherName")
Any inputs on this will be highly appreciated.
UPDATE:
Code used to work fine when my package was starting with "com.something".
However, I re-factored code to fix package names, after that, issues started showing up
UPDATE 2
If the query is modified to use SELECT o FROM UserEntity o JOIN o.attributes u... then the error goes away.
UPDATE 3 - Issue found in UPDATE too
#Query(value="UPDATE WebSessionEntity o SET o.lastAccessedOn = ?2 WHERE o.authSessionToken = ?1")
public int updateLastAccessedOn(String authSessionToken, Date accessDate);
Assuming WebSessionEntity starts with package name "in.something...", then the application does not start. During start-up, I get Hibernate validation error:
2015-03-24 18:52:00,810 [main] ERROR org.hibernate.hql.internal.ast.ErrorCounter - line 1:8: unexpected token: in line 1:8: unexpected token: in
at org.hibernate.hql.internal.antlr.HqlBaseParser.updateStatement(HqlBaseParser.java:210)
...
...
Caused by: java.lang.IllegalArgumentException: node to traverse cannot be null!
at org.hibernate.hql.internal.ast.util.NodeTraverser.traverseDepthFirst(NodeTraverser.java:63)
at org.hibernate.hql.internal.ast.QueryTranslatorImpl.parse(QueryTranslatorImpl.java:272)
By convention, when a domain name is interfering with naming a package, an underscore is used.
Link - https://docs.oracle.com/javase/tutorial/java/package/namingpkgs.html
An excerpt from the link,
In some cases, the internet domain name may not be a valid package
name. This can occur if the domain name contains a hyphen or other
special character, if the package name begins with a digit or other
character that is illegal to use as the beginning of a Java name, or
if the package name contains a reserved Java keyword, such as "int".
In this event, the suggested convention is to add an underscore.

Categories

Resources