Just a quick one. If I'm using an EJBQL query with named parameters, can I use the same parameter name twice in a single query to avoid having to set the value twice when I actually want to run the query? For instance, I'd like to be able to do something like this:
SELECT g FROM Group g WHERE g = :group OR g.parent = :group
...so that doing:
query.setParameter("group", theGroup);
will populate both fields. Is this possible?
I realize I could just run this and see, but I figured that asking first might save me (and anyone else who happens to find this question) a bit of time and frustration.
Yes, it's part of the spec. Makes no sense for a spec to insist on passing in an extra param name with dup value
Related
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!
I would like to get objects via Hibernate from database with concrete order. This order is something like that:
as the first I would like to get objects with column titled for example first_column not null,
as the second I would like to get objects with column second_column not null,
as the last I would like to get objects which third_column is the id for another object/table, and this another object has a field with concrete value for example: "something".
I have created criteria in this way:
criteria.addOrder(Order.asc("firstColumn"));
criteria.addOrder(Order.asc("secondColumn"));
but how can I meet the last requirement?
With the restriction I can do something like that:
criteria.createAlias("thirdColumn", "t");
criteria.add(Restrictions.eq("t.field", "something"));
But I have to use order, not restriction with three separate Criteria results, because I am using also setFirstResult() and setMaxResults() of the Criteria to implement pagination in my frontend.
If you can write the statement in SQL then you can probably get away with the approach mentioned in this post which is to create a custom subclass of Order.
I think you can simply use the "." separator and write your code as follow
criteria.createAlias("thirdColumn", "t");
criteria.addOrder(Order.asc("t.field"));
I have a requirement to use a reporting-friendly query from HQL. That is, each column is represented by a standard Java type (Long, Integer, String, List) rather than a mapped class. The values will be used by a third party library, so I have very limited control over post-processing.
My example object tree looks like this:
a.x
a.y (a collection of z)
a.y.z[0].v
a.y.z[1].v
a.y.z[2].v
I would like query to retrieve two columns. The first column is the plain "a.x" field, and the second is a String of a comma separated list of all of the a.y.z.v values. If this is not possible, then having the second column return as a Java list of the a.y.z.v values would be satisfactory.
In short, I would like to flatten the a.y.z.v collection to a csv String or to a List object from inside the query.
I have already attempted the following:
Using the "new" keyword in a list subselect. ie, "select a.x, (select new list(a.y.z.v)) from a". If necessary I could have transformed the contents of the list into a csv, but this caused a syntax error.
Using the "new" keyword with a custom object in a subselect. ie, "select a.x, (select new custom.package.ListToCsvObject(a.y.z)) from a". This caused the same error as the first attempt
Using the "elements()" keyword in the select. Unfortunately, this keyword only seems to work inside "in", "exists" clauses (etc), not as the actual returned value.
The only solution we've been able to find was to create a stored procedure in the database and use that, but such a solution is painfully slow through HQL (it turns a sub-second query into a 30 second query) and therefore is not something we want to continue to do.
I am able to make some limited changes to the Hibernate mapping (so I can add #formula, etc) but I would prefer not to have to make major changes to the database schema to support it. (So no, I don't want to create a denormalized "csv_value" column in the database!)
Could anyone suggest some code or, failing that, an alternative approach to solving this problem?
Try something like this should work. Flattening of list to comma separated string is done in the constructor of your VO class. You can also take a look at resultTransformer, you can create a custom resultTransformer and attach it to the query.
class ResultVO {
String x;
String y;
public ResultVO(String x,List<Z> y) {
this.x = x;
this.y = createCSV(y);
}
}
then in HQL
select new ResultVO(a.x,a.y) from a
A warning - this is not a good way to use JPA. If most of your use cases are like this you should seriously reconsider using some other persistence approach (ibastis, spring jdbc template + sql etc).
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)
i had a situation where i had to use this setDataVector function. I was puzzled to see there was an extra second argument(Vector columnIdentifiers) in the function. I'm just resetting the data. Why do i need to send the column identifiers?? And it doesn't take the old column identifiers by default if i don't pass the second argument. Irritating to add initialize a vector with column identifiers only for this purpose. Any idea why it's been done like that?
From the actual code, it looks to me like the method could have been better named. Something like setDataAndColumns() makes more sense. The internal code looks like this:
this.dataVector = nonNullVector(dataVector);
this.columnIdentifiers = nonNullVector(columnIdentifiers);
Passing in null for columnIdentifiers will simply remove all the columns in the table. I guess your controller class needs to keep a copy of the columnIdentifiers to pass in as required.
The setDataVector(...) method is invoked by all the constructor methods which require you to include both parameters.