How to use a dynamic parameter in a IN clause of a JPA named query? - sql

my problem is about this kind of query :
select * from SOMETABLE where SOMEFIELD in ('STRING1','STRING2');
the previous code works fine within Sql Developer.
The same static query also works fine and returns me a few results;
Query nativeQuery = em.createNativeQuery(thePreviousQuery,new someResultSet());
return nativeQuery.getResultList();
But when I try to parameterize this, I encounter a problem.
final String parameterizedQuery = "select * from SOMETABLE where SOMEFIELD in (?selectedValues)";
Query nativeQuery = em.createNativeQuery(parameterizedQuery ,new someResultSet());
nativeQuery.setParameter("selectedValues","'STRING1','STRING2'");
return nativeQuery.getResultList();
I got no result (but no error in console).
And when I look at the log, I see such a thing :
select * from SOMETABLE where SOMEFIELD in (?)
bind => [STRING1,STRING2]
I also tried to use no quotes (with similar result), or non ordered parameter (:selectedValues), which leads to such an error :
SQL Error: Missing IN or OUT parameter at index:: 1
I enventually tried to had the parentheses set directly in the parameter, instead of the query, but this didn't work either...
I could build my query at runtime, to match the first (working) case, but I'd rather do it the proper way; thus, if anyone has an idea, I'll read them with great interest!
FYI :
JPA version 1.0
Oracle 11G

JPA support the use of a collection as a list literal parameter only in JPQL queries, not in native queries. Some JPA providers support it as a proprietary feature, but it's not part of the JPA specification (see https://stackoverflow.com/a/3145275/1285097).
Named parameters in native queries also aren't part of the JPA specification. Their behavior depends on the persistence provider and/or the JDBC driver.
Hibernate with the JDBC driver for Oracle support both of these features.
List<String> selectedValues = Arrays.asList("STRING1", "STRING2");
final String parameterizedQuery = "select * from SOMETABLE where SOMEFIELD in (:selectedValues)";
return em.createNativeQuery(parameterizedQuery)
.setParameter("selectedValues", selectedValues)
.getResultList();

Instead of:
nativeQuery.setParameter("selectedValues", params);
I had to use:
nativeQuery.setParameterList("selectedValues", params);

This worked for me in derby. parameter without "()".
List<String> selectedValues = Arrays.asList("STRING1", "STRING2");
final String parameterizedQuery = "select * from SOMETABLE where SOMEFIELD in
:selectedValues";
return em.createNativeQuery(parameterizedQuery)
.setParameter("selectedValues", selectedValues)
.getResultList();

Replace this:
nativeQuery.setParameter("selectedValues","'STRING1','STRING2'");
with
List<String> params;
nativeQuery.setParameter("selectedValues",params);

I also faced the same issue.
This is what I did:
List<String> sample = new ArrayList<String>();
sample.add("sample1");
sample.add("sample2");
And now you, can set the sample in params.

Related

JPA native query gives incorrect output

I know this may sound silly but I've been stuck on this problem for too long!
I'm querying a PostgreSQL repository through JPA using native SQL queries. One of my queries looks like this:
#Query(value = "select * from gs where ?1 = ?2", nativeQuery = true)
public List<GsJsonStore> matchJson(String term, String value);
I'm testing the function using :
List<GsJsonStore> list = repo.matchJson("subject", "'Sub'");
The list is empty on running the query, however when I run the same query through PSQL command line using:
select * from gs where subject = 'Sub';
I get the correct output, records contatining the key-value pair are returned.
Where am I making the mistake?
You can't use parameter for column name. Your query resolves to
select * from gs where 'subject' = '''Sub'''
EDIT: just saw #pozs already posted the same in comment

Convert SQL request to Hibernate Criteria

I would like to convert this SQL into either Criteria. I am sorry I don't know which one to use since I'm new to Hibernate. I've done some research, and it looks like both are needed to achieve what I wanted.
My Sql Request
select *
from change.pade pade, change.pade_etat rdp, par.safsit safsit, par.saf saf
where pade.sir = "1245454"
and pade.id_safsit = "1"
and pade.id_safsit = safsit.id
and safsit.cd_s in ("12", "45")
and safsit.fk_saf = saf.id
and saf.cd_ur in "124"
and rdp.fk_pade = pade.id
and rdp.id_etat in "444"
You can use the Hibernate properties instead of writing HQL like this
session.createSQLQuery(String sqlQuery)
see an example here

Blank FROM clause in JPQL?

I'm using a JPQL select to access a Oracle DB function: select FUNCTION('function_name', 'foo', 1234) from com_mycompany_bar obj
This works as expected and the function is indeed called. The thing is I actually don't need the FROM clause and would rather have it empty instead of having to access an entity just to conform to the syntax.
What is the best option I have here?
I see several possible answers:
Within standard JPA there are two options, both involving Oracle DUAL "table"
Map DUAL to an entity and use it in the FROM clause. It is the more "JPQL" way, but it is more involved:
In Java code:
#Entity(name = "Dual")
#Table(name = "SYS.DUAL")
public class Dual {
#Id
#Column(name = "DUMMY")
private String dummy;
}
In orm.xml:
<named-query name="yourQuery">
<query><![CDATA[
SELECT FUNCTION('function_name', 'foo', 1234) FROM Dual d
]]>
</query>
</named-query>
In client code:
Query q = entityManager.createNamedQuery("yourQuery");
Just use a native query that uses DUAL
In orm.xml:
<named-native-query name="yourQuery">
<query><![CDATA[
SELECT function_name('foo', 1234) from sys.dual
]]>
</query>
</named-native-query>
In client code:
Query q = entityManager.createNativeQuery("yourQuery");
As a last option, you can use some JPA implementation extension to JPA and avoid DUAL:
Eclipselink:
http://wiki.eclipse.org/EclipseLink/Examples/JPA/StoredProcedures
http://www.eclipse.org/eclipselink/documentation/2.4/jpa/extensions/a_namedstoredprocedurequery.htm
http://ronaldoblanc.blogspot.com.br/2012/05/jpa-eclipselink-and-complex-parameters.html
Hibernate:
http://objectopia.com/2009/06/26/calling-stored-procedures-in-jpa/
jpa calling stored procedure with output cursor
How to call Oracle Function or Procedure using Hibernate 4 (EntityManager) or JPA 2
In short: I don't think it's possible to skip FROM clause of JPQL query.
JPQL grammar requires FROM clause to be present (see e.g. here or here).
Calling stored procedures/functions is typically done via native queries (see: createNativeQuery). So this should work:
em.createNativeQuery("select function_name('foo', 1234)").getSingleResult()
UPDATE (from comments):
That's true that JPA native queries don't support named parameters. So you must decide which is the lesser of two evils for you: either using JPQL with unnecessary entity reference or native query with positional parameters instead of named.
But be aware that FUNCTION is EclipseLink extension so it won't work should you decide to change a vendor.

How to change sql generated by linq-to-entities?

I am querying a MS SQL database using Linq and Entity Framework Code First. The requirement is to be able to run a WHERE SomeColumn LIKE '%sometext'clause against the table.
This, on the surface, is a simple requirement that could be accomplished using a simple Linq query like this:
var results = new List<MyTable>();
using(var context = new MyContext())
{
results = context.MyTableQueryable
.Where(x => x.SomeColumn.EndsWith("sometext"))
.ToList();
}
// use results
However, this was not effective in practice. The problem seems to be that the column SomeColumn is not varchar, rather it's a char(31). This means that if a string is saved in the column that is less than 31 characters then there will be spaces added on the end of the string to ensure a length of 31 characters, and that fouls up the .EndsWith() query.
I used SQL Profiler to lookup the exact sql that was generated from the .EndsWith() method. Here is what I found:
--previous query code removed for brevity
WHERE [Extent1].[SomeColumn] LIKE N'%sometext'
So that is interesting. I'm not sure what the N means before '%sometext'. (I'll Google it later.) But I do know that if I take the same query and run it in SSMS without the N like this:
--previous query code removed for brevity
WHERE [Extent1].[SomeColumn] LIKE '%sometext'
Then the query works fine. Is there a way to get Linq and Entity Framework to drop that N from the query?
Please try this...
.Where(x => x.SomeColumn.Trim().EndsWith("sometext"))
Just spoke to my colleague who had a similar issue, see if the following works for you:
[Column(TypeName = "varchar")]
public string SomeColumn
{
get;
set;
}
Apparently setting the type on the column mapping will force the query to recognise it as a VARCHAR, where a string is normally interpreted as an NVARCHAR.

Using CDBExpression With CDBCriteria In findAll

I am trying to generate and execute the following sql through CActiveRecord:
SELECT * FROM `bucket` `t`
WHERE bkt_user = unhex('A4FF2131E00C4696837689FCAAAC7DD2');
I came up with this:
$uuid = 'A4FF2131E00C4696837689FCAAAC7DD2';
$criteria = new CDbCriteria();
$expression = new CDbExpression(
'unhex(:value)',
array(':value'=>$uuid,));
$criteria->addCondition("bkt_user = :exp");
$criteria->params = array(':exp' => $expression);
$buckets = Bucket::model()->findAll($criteria);
The code executes without error or exception but does not return results as expected. A little debugging revealed that the snippet above generates the following sql:
SELECT * FROM `xpg_bucket` `t` WHERE bkt_user = :exp
and AFAIK only one CDbCommandBuilder::bindValue where :exp is bound to unhex(:value). There is no parameter binding done for :value. This is happening in CDbCommandBuilder::createFindCommand. CDbCommandBuilder::createInsertCommand, on the other hand seems to take care of a value itself being a CDbExpression and generated the appropriate bindValue's.
CDbExpression's documentation contains:
* CDbExpression is mainly used in {#link CActiveRecord} as attribute values.
* When inserting or updating a {#link CActiveRecord}, attribute values of
* type CDbExpression will be directly put into the corresponding SQL statement
* without escaping.
Does this mean CDbExpression is not meant to be used with CDbCriteria in findAll()? If yes, what is the alternative?
Actually, it turns out all that circus is not required. The following works with findAll:
$criteria->addCondition('bkt_user = unhex(:value)');
$criteria->params = array(':value'=>'665730BDEDA7489383E2519DB5DE6D60');
$buckets = Bucket::model()->findAll($criteria);
Be aware that the same will not work with add or update operations.