Getting Invalid parameter index 1 from simple HQL query - java

I have the following HQL query and for simplicity sake lets assume the mappings and table names are correct.
String queryString = "from entity as vv inner join vv.childentity as vis with childentityid=?";
Query query = session.createQuery(queryString);
query.setParameter(0, someVarId);
List<entity> entities = query.list();
I get the following error when attempting to execute this:
ERROR: could not bind value '12' to parameter: 1; Invalid parameter index 1.
I suspect this might be because HQL implicitly does not support binding parameters in the WITH clause. I cannot find any documentation saying that this is not supported and I RTFM.
Can anybody confirm this is true or that this is a known Hibernate bug, or a good workaround would be nice too.
EDIT: I forgot to mention that I get the same error even if using a named parameter.

I guess you need to use full name in with clause:
from entity as vv inner join vv.childentity as vis with vis.childentityid=?"

Thanks for your help but I figured out the weirdness.
When I am joining two objects in HQL it should be done this way.
from entity as vv where childentityid=?
I found out that I don't actually need to join them, I wasn't giving HQL enough credit to look at the object mappings and determine that entity has a property called childentity and thus childentityid is the unique identifier of it.
Thank you for all of your help.

Not directly related to your exact problem but I came to this thread by search engine.
Had same error 'Invalid parameter index 1' and have two hints for it:
For all coming from simple java.sql. zach is right - you have to start counting by 1. For JBoss/HBL you have to begin to count by 0.
My actual mistake was that I used quotation around the placeholder. (e.g. "SELECT foo FROM bar WHERE foobar like '?';")
As already mentioned - my answer is to clarify this Thread in case you come from simple java.sql.

query.setParameter(0, someVarId) needs to be:
query.setParameter(1, someVarId)

Related

Could any one tell me the real reason of spring-data projection in my case?

The case is:
I have a repository and two methods, one of them looks like:
//There is no warning about: Activity domain type or valid projection interface expected here...
#Query("select distinct a.creatorId from Activity a")
Optional<List<String>> findAllCreatorIds();
The second method looks like:
//There i have warning about: Activity domain type or valid projection interface expected here
#Query("select distinct a.creatorId from Activity a join a.categories c where c.name in ?1")
Optional<List<String>> findAllCreatorIdsByCategoryNames(Set<String> categoryNames);
It looks workable and tests are passed and the generated query is the following:
SELECT DISTINCT activity0_.created_by AS col_0_0_
FROM activities activity0_
INNER JOIN category_item categories1_ ON
activity0_.id=categories1_.activity_id
INNER JOIN categories category2_ ON
categories1_.category_id=category2_.id
WHERE category2_.name IN (?)
I have made the changes to use projection interface. The simple projection interface is:
public interface CreatorIdProjection {
String getCreatorId();
}
The query was changed:
#Query("select a from Activity a join a.categories c where c.name in ?1")
Optional<List<CreatorIdProjection>> findAllCreatorIdsByCategoryNames(Set<String> categoryNames);
Works too. And here is the generated query:
SELECT activity0_.id AS id1_0_,
activity0_.price AS price2_0_,
activity0_.created_by AS created_3_0_,
activity0_.expiration_date AS expirati4_0_,
activity0_.insertts AS insertts5_0_,
activity0_.name AS name6_0_,
activity0_.start_date AS start_da7_0_,
activity0_.updatets AS updatets8_0_
FROM activities activity0_
INNER JOIN category_item categories1_ ON activity0_.id=categories1_.activity_id
INNER JOIN categories category2_ ON categories1_.category_id=category2_.id
WHERE category2_.name IN (?)
I have a few questions related to this case:
Why there is no warning for the first method ?
Why the query have a lot of fields after SELECT when we use
projection? (to be more precise - why we cannot use "select
a.creatorId from Activity a...")
What is the reason of the warning "...domain type or valid
projection interface expected here" if as a result we have a query
that querying the table data instead of what we need.
The warning comes because you're using 'By' in your method name which triggers Spring Data to use (magic) Query methods.
Remove 'By' or replace it with 'Using' or something else and the warning will disappear.
See also https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#repositories.query-methods.details
About question #1:
Why there is no warning for the first method?
There is no Spring Data Projection involved here. You just execute a query returning a list of scalar values, that then is past on (and wrapped in an Optional) by Spring Data. Therefore you should not get such a warning. As a matter of fact I couldn't reproduce the warning at all, nor could I find it in the source code and in any case, the same argument holds for the second method. If it actually produces such a warning please submit a bug.
For question #2a
Why the query have a lot of fields after SELECT when we use projection?
You specify the query exactly using the #Query annotation, and it gets executed as such. Spring Data does not parse your query and removes unnecessary parts, which would be a huge amount of effort for little effect since you provided the query in the first place you might as well provide one that fits your needs.
For question #2b
why we cannot use select a.creatorId from Activity a...?
You can (almost). You just have to specify aliases because JPA will otherwise mangle the column names and Spring Data wouldn't know what columns it is looking at. It actually tells you so when you don't specify aliases. You should get an exception No aliases found in result tuple! Make sure your query defines aliases!. So this should work:
select a.creatorId as creatorId from Activity a...
An alternative is to actually invoke the constructor of a class in the statement:
#Query("select new my.super.cool.projection.CreatorId(a.creatorId)")
Both variants will only query the columns specified.
For question #3
What is the reason for the warning ...domain type or valid projection interface expected here if as a result we have a query that querying the table data instead of what we need.
I was not able to reproduce the warning. I also searched the source code and couldn't find it, so I can't answer this one.

Spring Data JPA difference between findBy / findAllBy

Is there any difference when using Spring Data JPA keywords between:
List<SomeEntity> findBySomeCondition();
and
List<SomeEntity> findAllBySomeCondition();
No, there is no difference between them, they will execute exactly the same query, the All part is ignored by Spring Data when deriving the query from the method name. The only important bit is the By keyword, anything following it is treated as a field name (with the exception of other keywords like OrderBy which incidentially can lead to some strange looking method names like findAllByOrderByIdAsc).
This means something like this is perfectly valid:
List<SomeEntity> findAnythingYouWantToPutHereBySomeCondition();
And will execute exactly the same SQL query as:
List<SomeEntity> findBySomeCondition();
or
List<SomeEntity> findAllBySomeCondition();
The documentation for the 2.3.6 release of Spring Data discusses this feature:
Any text between find (or other introducing keywords) and By is considered to be descriptive unless using one of the result-limiting keywords such as a Distinct to set a distinct flag on the query to be created or Top/First to limit query results.
The purpose of feature was explained in a blog post about the then-upcoming 2.0 release of Spring Data:
Spring Data’s method parsing uses prefix keywords like find, exists, count, and delete and a terminating By keyword. Everything you put in between find and By makes your method name more expressive and does not affect query derivation.
To illustrate the difference lets look at the two functions:
1. Set<Policy> findAllByRoleIn(Iterable<Role> role);
2. Set<Policy> findByRoleIn(Iterable<Role> role);
The query generated by 1st function:
1. select policy.id, policy.role from policy where (policy.role in (? , ? , ? , ?))
The query generated by 2nd function:
2. select policy.id, policy.role from policy where (policy.role in (? , ? , ? , ?))
Conclusion: Clearly, if we look at the queries generated by both functions. We can clearly see, there is no difference between the two function definitions, they execute exactly the same query.
one difference is that with findAllBy Hibernate filters (#Filters from org.hibernate.annotations) are applied and so a different sql.
Actually, the difference between findallBy and findby, is that :
findAllBy returns a Collection but
findBy returns Optional.
so it's preferable to write List findAllBy instead of writing List findBy (but it will work also :p).
and to write Optional findBy instead of Optional findAllBy.
check this doc https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#repositories.core-concepts
findBy method is used if we want to find by name or some other criteria like findByFirstName(String firstName);
findAll methods generally finds by providing specification
List<T> findAll(Specification<T> spec);
Please see docs below for more clarity:
http://docs.spring.io/spring-data/jpa/docs/1.4.3.RELEASE/reference/html/jpa.repositories.html

Why does Hibernate throw a QuerySyntaxException for this HQL?

While building a query using Hibernate, I noticed something rather odd. If I use sequential named parameters for the ORDER BY clause, Hibernate throws a QuerySyntaxException (the colon prefix being an unexpected token):
createQuery("FROM MyEntity ORDER BY :orderProperty :orderDirection");
However, when this is done with a plain SQL query the query is created without a problem:
createSQLQuery("SELECT * FROM my_entity_table ORDER BY :orderProperty :orderDirection");
I know Hibernate is doing more String evaluation for the HQL query, which is probably why the SQL query is created without an error. I am just wondering why Hibernate would care that there are two sequential named parameters.
This isn't a huge issue since it is simple to work around (can just append the asc or desc String value to the HQL instead of using a named paramater for it), but it struck my curiosity why Hibernate is preventing it (perhaps simply because 99% of the time sequential named parameters like this result in invalid SQL/HQL).
I've been testing this in my local, and I can't get your desired outcome to work with HQL.
Here is quote from the post I linked:
You can't bind a column name as a parameter. Only a column value. This name has to be known when the execution plan is computed, before binding parameter values and executing the query. If you really want to have such a dynamic query, use the Criteria API, or some other way of dynamically creating a query.
Criteria API looks to be the more useful tool for your purposes.
Here is an example:
Criteria criteria = session.createCriteria(MyEntity.class);
if (orderDirection.equals("desc")) {
criteria.addOrder(Order.desc(orderProperty));
}
else {
criteria.addOrder(Order.asc(orderProperty));
}
According to the answer accepted in this question, you can only define parameters in WHERE and HAVING clauses.
The same answer also gives you some ways to have a workaround for your problem, however I will add one more way to do this:
Use the CASE - WHEN clause in your ORDER BY, this would work by the following way:
SELECT u FROM User u
ORDER BY
CASE WHEN '**someinputhere**' = :orderProperty
AND '**someotherinput**' = :orderDirection
THEN yourColumn asc
ELSE yourColumn desc END
Please, note that in this approach would required you to write all the possible inputs for ordering. Not really beautiful but really useful, especially because you would not need to write multiple queries with different orderings, plus with this approach you can use NamedQueries, which would be possible by writing the query dinamically using string concats.
Hope this can solve your problem, good luck!

JPA - An identification variable must be provided for a range variable declaration

I wonder why I got "An identification variable must be provided for a range variable declaration." error when I don't have alias.
Seems like I solved by problem when I added alias to the table.
Code with problem:
List result = entityManager.createQuery( "from Rental", Rental.class ).getResultList();
Solution:
List result = entityManager.createQuery( "from Rental r", Rental.class ).getResultList();
You should make a distinction between HQL (Hibernate Query Language) and JPQL (Java Persistence Query Language). As long as your provider is Hibernate you will see no difference, but you have to remember that correct JPQL query consists of SELECT keyword. Here's explanation of this differences.
Going back to you question - it is JPQL requirement to define alias for each Entity table.

JPA Re executing same namedQuery with different parameters

I'm using openJPA as implementation, and i'm facing the following issue.
In one of our services, we use a namedQuery to select value in a range, so something like that:
query = "select xxx from xxx where xxx in (:param)"
This service / query is called by another process which is responsible of building/providing this parameter value. Sometime - for bad reasons - this process give us a list with a length greater than the maximum one authorized by DB2.
So i'm wondering if it's possible for us to implement a mecanism to automatically split this parameter into several list of vlaues, execute for each internal list this namedQuery and then aggregate results before returning.
The main question now is: can i re-use several times my built TypedQuery to re-execute same request but with a different parameter value ?
Code example (For the example, not a real nor existing code):
TypedQuery<MyClass> query = em.createNamedQuery("myQueryName", MyClass.class);
foreach (...) {
query.setParameter(...);
res = query.getResultList();
// Query re-executed and results are different ?
}
Is it a correct way to fix this kind of issue, if not is there any better (in term of performances) way to do this ?
No, you can't do that. You must recreate the TypedQuery for each execution.

Categories

Resources