Spring Data JPQL No value specified for parameter - spring-data

I have two classes Post and Tag. These are in a many-to-many relationship and Post has an attribute Set< Tag > tags. I want to return the posts which have all the tags I provide as parameter.
Example: Post A has tags T1, T2 and T3, post B has tags T1. I provide a set with the tags T1 and T3. As a result, only post A should be returned.
In my repo interface extending CrudRepository, I have the following custom query build:
#Query(
value="SELECT p, count(p) AS noEntries
FROM Post p WHERE p.tags IN :tags
GROUP BY p.id HAVING noEntries = :noTags",
countQuery = "SELECT count(spe) AS noEntries
FROM Post spe WHERE spe.tags IN :tags
GROUP BY spe.id HAVING noEntries = :noTags")
Page<Post> findAllByTagsInCustom(Pageable pageable, #Param("tags") Set<Tag> tags,
#Param("noTags") int noTags);
I am calling the query with
posts = repo.findAllByTagsInCustom(pReq, tags, tags.size());
where the parameters are type PageRequest (pReq), Set (tags) and int (tags.size()).
When running the request I get
Servlet.service() for servlet [dispatcherServlet] in context with path [/v1] threw exception [Request processing failed; nested exception is org.springframework.dao.InvalidDataAccessResourceUsageException: could not extract ResultSet; SQL [n/a]; nested exception is org.hibernate.exception.SQLGrammarException: could not extract ResultSet] with root cause
java.sql.SQLException: No value specified for parameter 2
How do I have to specify the method parameters to make the query work?

Related

How to fix org.postgresql.util.PSQLException: ERROR: op ANY/ALL (array) requires array on right side

I have an SQL query
select t.id as id, t.color as color from test_data t where t.id = ANY(?1) and t.color=?2
how can I pass an array of values to ANY(?1)
i.e
em.createNamedQuery("Test.getTestData", Tuple.class)
.setParameter(1, arrayOfIds<----___can_I_pass_an_array___?____)
.setParameter(2, yellow)
.unwrap(NativeQuery.class)
.addScalar("id", LongType())
.addScalar("color", new StringType())
I get an error
Caused by: org.postgresql.util.PSQLException: ERROR: op ANY/ALL (array) requires array on right side
Position: 507
at org.postgresql.core.v3.QueryExecutorImpl.receiveErrorResponse(QueryExecutorImpl.java:2553) ~[postgresql-42.2.18.jar!/:42.2.18]
I see two possible ways:
You can transform = ANY(?1) statement to the in (?1). As it explained here, they have the same meaning. And then pass List<Long> instead of Long[]:
List<Long> ids = new ArrayList<>();
List<Object[]> result = em.createNativeQuery(
"select id, color from test_data where id in (:ids)")
.setParameter("ids", ids)
.getResultList();
You can add the following dependency:
<dependency>
<groupId>com.vladmihalcea</groupId>
<artifactId>hibernate-types-52</artifactId>
<version>2.10.1</version>
</dependency>
if you use hibernate 5.4, 5.3 or 5.2 and then rewrite your query in the following way:
import com.vladmihalcea.hibernate.type.array.LongArrayType;
Long[] ids = {1L, 3L};
List<Object[]> result = em.createNativeQuery(
"select id, color from test_data where id = ANY(:ids)")
.unwrap(org.hibernate.query.NativeQuery.class)
.setParameter("ids", ids, LongArrayType.INSTANCE)
.getResultList();
Instead of adding additional dependency you can also write your own hibernate custom basic type, but it can be quite problematically without appropriate experience.

JPA JPQL: select entities with List attribute

I have an entity which have a collection of tags
#ManyToMany
private Set<Tag> listeTag;
I want to write a query which returns a list of my entity which contains all tags passed in parameters and not just one like this.
select distinct entity from Entity entity where and entity.listeTag in :listeTag
if there are two tags in listeTag I want only entities which have at least the two tags.
select e from SomeEntity e where :numberOfTagsInSet =
(select count(tag.id) from SomeEntity e2
join e2.listeTag tag
where e.id = e2.id
and tag.id in :setOfTagIds)
That should do the trick.

JPQL query with WHERE on nested fields

I have a java entity class UserBean with a list of events:
#OneToMany
private List<EventBean> events;
EventBean has Date variable:
#Temporal(javax.persistence.TemporalType.TIMESTAMP)
private Date eventDate;
Now in UserBean I want to create a NamedQuery that returns all dates that fall within a specific range:
#NamedQuery(name="User.findEventsWithinDates",
query="SELECT u.events FROM UserBean u WHERE u.name = :name AND u.events.eventDate > :startDate AND u.events.eventDate < :endDate")
The above query does not compile though. I get this error:
The state field path 'u.events.eventDate' cannot be resolved to a valid type.
By the way, I use EclipseLink version 2.5.0.v20130507-3faac2b.
What can I do to make this query work? Thanks.
Path u.events.eventDate is an illegal construct in JPQL, because it is not allowed to navigate via a collection valued path expression. In this case u.events is a collection valued path expression. In JPA 2.0 specification this is told with following words:
It is syntactically illegal to compose a path expression from a path
expression that evaluates to a collection. For example, if o
designates Order, the path expression o.lineItems.product is illegal
since navigation to lineItems results in a collection. This case
should produce an error when the query string is verified. To handle
such a navigation, an identification variable must be declared in the
FROM clause to range over the elements of the lineItems collection.
This problem can be solved by using JOIN:
SELECT distinct(u)
FROM UserBean u JOIN u.events e
WHERE u.name = :someName
AND e.eventDate > :startDate
AND e.eventDate < :endDate

SpringData JPA hibernate collection for IN keyword

I am trying to fetch records using custome query in SpringData repository. I want to use IN keyword to fetch objects according to ids of a nested object. Following is my repo class.
public interface BusinessRepository extends JpaRepository<Business, Long> {
#Query("SELECT t from Business t where t.address.addressid IN ? AND (t.businessType.text like ? or t.businessname like ?)")
ArrayList<Business> findAllByAddressAddressidInAndBusinessTypeTextLikeOrBusinessnameLike(ArrayList<Long> ids, String businessType, String businessName);
My console shows following text.
Hibernate: select business0_.businessid as businessid0_, business0_.address_addressid as address9_0_, business0_.begdate as begdate0_, business0_.businessType_id as busines10_0_, business0_.businessname as business3_0_, business0_.contracttypeid as contract4_0_, business0_.multiuser as multiuser0_, business0_.statusid as statusid0_, business0_.urlreservos as urlreser7_0_, business0_.website as website0_, business0_1_.homePhone as homePhone2_, business0_1_.mobilePhone as mobilePh2_2_, business0_1_.primaryPhone as primaryP3_2_, business0_2_.businessdesc as business1_1_, business0_2_.pagetitle as pagetitle1_, business0_2_.tag as tag1_, business0_3_.userid as userid3_ from tbu1200 business0_ left outer join tbu1208 business0_1_ on business0_.businessid=business0_1_.id left outer join tbu1207 business0_2_ on business0_.businessid=business0_2_.id left outer join tbu1204 business0_3_ on business0_.businessid=business0_3_.businessid cross join vt1001 businessty1_ where business0_.businessType_id=businessty1_.id and (business0_.address_addressid in (?)) and (businessty1_.text like ? or business0_.businessname like ?)
TRACE: org.hibernate.type.descriptor.sql.BasicBinder - binding parameter [1] as [BIGINT] - [1, 2]
It seems correct to me. I am getting following exception.
java.lang.ClassCastException: java.util.ArrayList cannot be cast to java.lang.Long
at org.hibernate.type.descriptor.java.LongTypeDescriptor.unwrap(LongTypeDescriptor.java:36)
at org.hibernate.type.descriptor.sql.BigIntTypeDescriptor$1.doBind(BigIntTypeDescriptor.java:57)
at org.hibernate.type.descriptor.sql.BasicBinder.bind(BasicBinder.java:92)
My question is that why it is expecting Long while I have used IN statement. It should expect a Collection. If this is something not supported then what should I do?
Why are you adding #Query annotation here?
Without specifing query manually it should works as expected.
List<Company> findDistinctByUsers_loginIn(List<String> users)
I've checked and such code finishes without exception. The only problem is that SpringData is creating cartesian, and you need to add distinct to make it working properly.

DistinctResultList in OpenJPA

I am attempting to get a distinct list of id's back that would be longs... But I am getting back this DistinctResultList... How can one handle this so that they get back results they are expecting... Here is what I am doing...
#NamedQuery(name="getProvidersByResourceIds", query = "SELECT DISTINCT p.resourceId FROM Provider p WHERE p.resourceId `in :resourceIds")`
Out of that I try and get the resourceIds' by doing this...
List<Long> provIDs = (List<Long>) emf.createNamedQuery("getProvidersByResourceIds").setParameter("resourceIds", values).getResultList();
But like I said, I keep getting back a DistinctResultList... Looking through the debugger, I can see the values I am getting back. How can I translate this into something usefull?
javax.ejb.EJBException: See nested exception; nested exception is: java.lang.IllegalArgumentException: Parameter "Parameter<long>('resourceIds')" declared in "SELECT p FROM Provider p WHERE p.resourceId = :resourceIds" is set to value of "org.apache.openjpa.kernel.DistinctResultList#35fb35fb" of type "org.apache.openjpa.kernel.DistinctResultList", but this parameter is bound to a field of type "long".
java.lang.IllegalArgumentException: Parameter "Parameter<long>('resourceIds')" declared in "SELECT p FROM Provider p WHERE p.resourceId = :resourceIds" is set to value of "org.apache.openjpa.kernel.DistinctResultList#35fb35fb" of type "org.apache.openjpa.kernel.DistinctResultList", but this parameter is bound to a field of type "long"
Change your query to :
#NamedQuery(name="getProvidersByResourceIds",
query = "SELECT DISTINCT p.resourceId FROM Provider p WHERE p.resourceId in (:resourceIds)");