Is it possible to have multiple columns in IN clause?
#Query(nativeQuery = true, value = "select * from table where (column1, column2) in (:column1, :column2)")
List<Table> findByColumn1Column2In(#Param("column1") List<BigDecimal> column1, #Param("column2") List<BigDecimal> column2);`
Expecting a query like this:
select * from table where (column1, column2) in ((1,2), (3,4), (5,6))
Since JPA doesn't support multi-columns IN clauses I overcome this by using SQL CONCAT function on the DB values, and of course, on the supplied range values, as follows:
first, create the range we looking for:
List<String> columns;
for (i=0, i<column1.size(), i++){
columns.add(column1.get(i) + '-' + column2.get(i));
}
Modify the query:
#Query(nativeQuery = true,
value = "select * from table where CONCAT(column1, '-', column2) in (:columns)")
List<Table> findByColumn1Column2In(#Param("columns") List<String> columns);
And there you nail that :-)
Multiple column with IN clause in not yet supported by Spring data. You can use #Query annotation for custom query as below:
#Query( "select o from MyObject o where id in :ids" )
List findByIds(#Param("ids") List inventoryIdList);
Yes, It is possible to have multiple "In" clause in a method.
Using spring data jpa and spring boot we can achieve this as below:
For your case you can just write the below method in your repository and it would work fine.
List<Table> findByColumn1InAndColumn2In(List<BigDecimal> column1,List<BigDecimal> column2);
I know I can pass a list to named query in JPA, but how about NamedNativeQuery? I have tried many ways but still can't just pass the list to a NamedNativeQuery. Anyone know how to pass a list to the in clause in NamedNativeQuery? Thank you very much!
The NamedNativeQuery is as below:
#NamedNativeQuery(
name="User.findByUserIdList",
query="select u.user_id, u.dob, u.name, u.sex, u.address from user u "+
"where u.user_id in (?userIdList)"
)
and it is called like this:
List<Object[]> userList = em.createNamedQuery("User.findByUserIdList").setParameter("userIdList", list).getResultList();
However the result is not as I expected.
System.out.println(userList.size()); //output 1
Object[] user = userList.get(0);
System.out.println(user.length); //expected 5 but result is 3
System.out.println(user[0]); //output MDAVERSION which is not a user_id
System.out.println(user[1]); //output 5
System.out.println(user[2]); //output 7
The above accepted answer is not correct and led me off track for many days !!
JPA and Hibernate both accept collections in native query using Query.
You just need to do
String nativeQuery = "Select * from A where name in :names"; //use (:names) for older versions of hibernate
Query q = em.createNativeQuery(nativeQuery);
q.setParameter("names", l);
Also refer the answers here which suggest the same (I picked the above example from one of them)
Reference 1
Reference 2 which mentioned which cases paranthesis works which giving the list as a parameter
*note that these references are about jpql queries, nevertheless the usage of collections is working with native queries too.
A list is not a valid parameter for a native SQL query, as it cannot be bound in JDBC. You need to have a parameter for each argument in the list.
where u.user_id in (?id1, ?id2)
This is supported through JPQL, but not SQL, so you could use JPQL instead of a native query.
Some JPA providers may support this, so you may want to log a bug with your provider.
Depending on your database/provider/driver/etc., you can, in fact, pass a list in as a bound parameter to a JPA native query.
For example, with Postgres and EclipseLink, the following works (returning true), demonstrating multidimensional arrays and how to get an array of double precision. (Do SELECT pg_type.* FROM pg_catalog.pg_type for other types; probably the ones with _, but strip it off before using it.)
Array test = entityManager.unwrap(Connection.class).createArrayOf("float8", new Double[][] { { 1.0, 2.5 }, { 4.1, 5.0 } });
Object result = entityManager.createNativeQuery("SELECT ARRAY[[CAST(1.0 as double precision), 2.5],[4.1, 5.0]] = ?").setParameter(1, test).getSingleResult();
The cast is there so the literal array is of doubles rather than numeric.
More to the point of the question - I don't know how or if you can do named queries; I think it depends, maybe. But I think following would work for the Array stuff.
Array list = entityManager.unwrap(Connection.class).createArrayOf("int8", arrayOfUserIds);
List<Object[]> userList = entityManager.createNativeQuery("select u.* from user u "+
"where u.user_id = ANY(?)")
.setParameter(1, list)
.getResultList();
I don't have the same schema as OP, so I haven't checked this exactly, but I think it should work - again, at least on Postgres & EclipseLink.
Also, the key was found in: http://tonaconsulting.com/postgres-and-multi-dimensions-arrays-in-jdbc/
Using hibernate, JPA 2.1 and deltaspike data I could pass a list as parameter in query that contains IN clause. my query is below.
#Query(value = "SELECT DISTINCT r.* FROM EVENT AS r JOIN EVENT AS t on r.COR_UUID = t.COR_UUID where " +
"r.eventType='Creation' and t.eventType = 'Reception' and r.EVENT_UUID in ?1", isNative = true)
public List<EventT> findDeliveredCreatedEvents(List<String> eventIds);
can be as simple as:
#Query(nativeQuery =true,value = "SELECT * FROM Employee as e WHERE e.employeeName IN (:names)")
List<Employee> findByEmployeeName(#Param("names") List<String> names);
currently I use JPA 2.1 with Hibernate
I also use IN condition with native query. Example of my query
SELECT ... WHERE table_name.id IN (?1)
I noticed that it's impossible to pass String like "id_1, id_2, id_3" because of limitations described by James
But when you use jpa 2.1 + hibernate it's possible to pass List of string values. For my case next code is valid:
List<String> idList = new ArrayList<>();
idList.add("344710");
idList.add("574477");
idList.add("508290");
query.setParameter(1, idList);
In my case ( EclipseLink , PostGreSQL ) this works :
ServerSession serverSession = this.entityManager.unwrap(ServerSession.class);
Accessor accessor = serverSession.getAccessor();
accessor.reestablishConnection(serverSession);
BigDecimal result;
try {
Array jiraIssues = accessor.getConnection().createArrayOf("numeric", mandayWorkLogQueryModel.getJiraIssues().toArray());
Query nativeQuery = this.entityManager.createNativeQuery(projectMandayWorkLogQueryProvider.provide(mandayWorkLogQueryModel));
nativeQuery.setParameter(1,mandayWorkLogQueryModel.getPsymbol());
nativeQuery.setParameter(2,jiraIssues);
nativeQuery.setParameter(3,mandayWorkLogQueryModel.getFrom());
nativeQuery.setParameter(4,mandayWorkLogQueryModel.getTo());
result = (BigDecimal) nativeQuery.getSingleResult();
} catch (Exception e) {
throw new DataAccessException(e);
}
return result;
Also in query cannot use IN(?) because you will get error like :
Caused by: org.postgresql.util.PSQLException: ERROR: operator does not exist: numeric = numeric[]
'IN(?)' must be swapped to '= ANY(?)'
My solution was based on Erhannis concept.
In jpa, it worked for me
#Query(nativeQuery =true,value = "SELECT * FROM Employee as e WHERE e.employeeName IN (:names)")
List<Employee> findByEmployeeName(#Param("names") List<String> names);
Tried in JPA2 with Hibernate as provider and it seems hibernate does support taking in a list for "IN" and it works. (At least for named queries and I believe it will be similar with named NATIVE queries)
What hibernate does internally is generate dynamic parameters, inside the IN same as the number of elements in the passed in list.
So in you example above
List<Object[]> userList = em.createNamedQuery("User.findByUserIdList").setParameter("userIdList", list).getResultList();
If list has 2 elements the query will look like
select u.user_id, u.dob, u.name, u.sex, u.address from user u "+
"where u.user_id in (?, ?)
and if it has 3 elements it looks like
select u.user_id, u.dob, u.name, u.sex, u.address from user u "+
"where u.user_id in (?, ?, ?)
you should do this:
String userIds ="1,2,3,4,5";
List<String> userIdList= Stream.of(userIds.split(",")).collect(Collectors.toList());
Then, passes like parameter inside your query, like this:
#NamedNativeQuery(name="User.findByUserIdList", query="select u.user_id, u.dob, u.name, u.sex, u.address from user u where u.user_id in (?userIdList)")
It's not possible with standard JPA. Hibernate offers the proprietary method setParameterList(), but it only works with Hibernate sessions and is not available in JPA's EntityManager.
I came up with the following workaround for Hibernate, which is not ideal but almost standard JPA code and has some nice properties to it.
For starters you can keep the named native query nicely separated in a orm.xml file:
<named-native-query name="Item.FIND_BY_COLORS" result-class="com.example.Item">
<query>
SELECT i.*
FROM item i
WHERE i.color IN ('blue',':colors')
AND i.shape = :shape
</query>
</named-native-query>
The placeholder is wrapped in single quotes, so it's a valid native JPA query. It runs without setting a parameter list and would still return correct results when other matching color parameters are set around it.
Set the parameter list in your DAO or repository class:
#SuppressWarnings("unchecked")
public List<Item> findByColors(List<String> colors) {
String sql = getQueryString(Item.FIND_BY_COLORS, Item.class);
sql = setParameterList(sql, "colors", colors);
return entityManager
.createNativeQuery(sql, Item.class)
.setParameter("shape", 'BOX')
.getResultList();
}
No manual construction of query strings. You can set any other parameter as you normally would.
Helper methods:
String setParameterList(String sql, String name, Collection<String> values) {
return sql.replaceFirst(":" + name, String.join("','", values));
}
String getQueryString(String queryName, Class<?> resultClass) {
return entityManager
.createNamedQuery(queryName, resultClass)
.unwrap(org.hibernate.query.Query.class) // Provider specific
.getQueryString();
}
So basically we're reading a query string from orm.xml, manually set a parameter list and then create the native JPA query. Unfortunately, createNativeQuery().getResultList() returns an untyped query and untyped list even though we passed a result class to it. Hence the #SuppressWarnings("unchecked").
Downside: Unwrapping a query without executing it may be more complicated or impossible for JPA providers other than Hibernate. For example, the following might work for EclipseLink (untested, taken from Can I get the SQL string from a JPA query object?):
Session session = em.unwrap(JpaEntityManager.class).getActiveSession();
DatabaseQuery databaseQuery = query.unwrap(EJBQueryImpl.class).getDatabaseQuery();
databaseQuery.prepareCall(session, new DatabaseRecord());
Record r = databaseQuery.getTranslationRow();
String bound = databaseQuery.getTranslatedSQLString(session, r);
String sqlString = databaseQuery.getSQLString();
An alternative might be to store the query in a text file and add code to read it from there.
You can pass in a list as a parameter, but:
if you create a #NamedNativeQuery and use .createNamedQuery(), you don't use named param, you used ?1(positional parameter). It starts with 1, not 0.
if you use .createNativeQuery(String), you can use named param.
You can try this :userIdList instead of (?userIdList)
#NamedNativeQuery(
name="User.findByUserIdList",
query="select u.user_id, u.dob, u.name, u.sex, u.address from user u "+
"where u.user_id in :userIdList"
)
I'm trying to make a new calculated col with criteria query like this SQL:
SELECT *, SUM(quantity) + SUM(pledged) as total, SUM(pledged) as pledged FROM stock_position GROUP BY partner_id, product_id
I need to get this result and set to another entity, so I need to use the alias total and map to entity. But I don't find a way to do this. I tried the code below.
final CriteriaQuery<Stock> query = getCriteriaBuilder().createQuery(Stock.class);
final Root<StockPosition> root = query.from(StockPosition.class);
Expression<Double> quantity = getCriteriaBuilder().sum(root.<Double> get("quantity"));
Expression<Double> pledged = getCriteriaBuilder().sum(root.<Double>get("pledged"));
Expression<Double> total = getCriteriaBuilder().sum(quantity, pledged);
query.select(total); //THIS GIVES ME ERROR
query.groupBy(root.get("partner"), root.get("product"));
Of course the types are different, but I don't know how to map. Anyone can give a hand?
Thanks
Simple: You want to sum up some values, but the query expects an entity of the class Stock.
The correct declaration would be as follows:
final CriteriaQuery<Double> query = getCriteriaBuilder().createQuery(Double.class);
I need to fetch the result of the following query but i am getting a typecast exception. Kindly help out!
SELECT COUNT(*) FROM ( SELECT DISTINCT a.PROPSTAT_CODE,a.PROPSTAT_DESC,a.PROPSTAT_TYPE FROM CNFGTR_PROPSTAT_MSTR a WHERE 1 = 1 )
My code is given below,
Query query = session.createSQLQuery(sqlQuery);
listRes = query.list();
int ans = ((Integer)listRes.get(0)).intValue();
Thanks in advance
Since you say that you are wrapping the above query in another query that returns the count, then this will give you want, without having to convert to any other data types.
Integer count = (Integer) session.createSQLQuery("select count(*) as num_results from (SELECT DISTINCT a.PROPSTAT_CODE,a.PROPSTAT_DESC,a.PROPSTAT_TYPE FROM CNFGTR_PROPSTAT_MSTR a WHERE 1 = 1)")
.addScalar("num_results", new IntegerType())
.uniqueResult();
System.err.println(count);
The trick is the call to "addScalar". This tells Hibernate you want the data type of "num_results" pre-converted to an Integer, regardless of what your specific DB implementation or JDBC driver prefers. Without this, Hibernate will use the type preferred by the JDBC driver, which explains why different answers here have different casts. Setting the desired result type specifically removes all guesswork about your returned data type, gives you the correct results, and has the added bonus of being more portable, should you ever wish to run your application against a different relational database. If you make the call to "list" instead of "uniqueResult" then you can assign the results directly to a List
Use long instead of int. Hibernate returns count(*) as long not int.
Query query = session.createSQLQuery(sqlQuery);
listRes = query.list();
long ans = (long)listRes.get(0);
Well.. I suppose this should work:
Query query = session.createSQLQuery(sqlQuery);
List listRes = query.list();
int ans = ((BigDecimal) listRes.get(0)).intValue();
Note: you need to import java.math.BigDecimal
List number=session.createSQLQuery("SELECT COUNT(*) FROM devicemaster WHERE ClientId="+id).list();
session.getTransaction().commit();
int ans = ((java.math.BigInteger) number.get(0)).intValue();
I am using java/hibernate/Oracle. i have a list with more than 3000 entries. if i pass whole list i get below exception.
Caused by: java.sql.SQLException: ORA-01795: maximum number of expressions in a list is 1000
to solve the issue i am splitting the list into sublists, each sublist will have 1000 entries. for every thousand entries i am firing a query. it is working fine.
Please clarify me, is there any better solution?
Thanks!
It's an Oracle limitation, which is why it's got an Oracle error code... although you could argue that it's a limitation of Hibernate that it doesn't transparently work around it :)
You should probably put the list into a temporary table and join on that, assuming Oracle doesn't have anything like SQL Server's table-valued parameters. (Or you could break your query up into multiple queries, potentailly - it depends on what you're doing.)
The Exception-Text lets me believe that this is coming from the Oracle-Database - look at the error-number...
You cant have a list with more than 1000 elements in a single "where" condition if you are working with Oracle DB. So you can chop down your "where" condition in multiple "where" conditions and join them with "or" clause.
If you are using hibernate Criteria, you can use below Java method to do this.
Just replace your code where ever you used
criteria.add(Restrictions.in(propertyName, mainList));
with
addCriteriaIn(propertyName, mainList, criteria);
which the method is :
private void addCriteriaIn (String propertyName, List<?> list,Criteria criteria)
{
Disjunction or = Restrictions.disjunction();
if(list.size()>1000)
{
while(list.size()>1000)
{
List<?> subList = list.subList(0, 1000);
or.add(Restrictions.in(propertyName, subList));
list.subList(0, 1000).clear();
}
}
or.add(Restrictions.in(propertyName, list));
criteria.add(or);
}
there is also another way to resolve this issue. lets say you have two tables Table1 and Table2. and it is required to fetch all entries of Table1 not referred/present in Table2 using Criteria query.
So go ahead like this...
List list=new ArrayList();
Criteria cr=session.createCriteria(Table1.class);
cr.add(Restrictions.sqlRestriction("this_.id not in (select t2.t1_id from Table2 t2 )"));
.
.
List list=new ArrayList();
Criteria cr=session.createCriteria(Table1.class);
cr.add(Restrictions.sqlRestriction("this_.id not in (select t2.t1_id from Table2 t2 )"));
list=cr.list();
.
.
.
It will perform all the subquery finction directly in SQL without including 1000 or more parameters in SQL converted by Hibernate framework. It worked for me.
Note: You may need to change SQL portion as per your requirement.
i have used below and it works perfectly for me
if (myArgList.size() > 1000) {
while(myArgList.size()>0)
{
tempList = myArgList;
def listSize= tempList.size()
if(listSize>1000){
def subList = tempList.subList(0, 1000);
def tcriteria = tableA.createCriteria()
result.addAll(tcriteria.listDistinct {
'in'('id', subList.collect{it.id})
fetchMode('tableBObj', FetchMode.JOIN)
})
myArgList.subList(0, 1000).clear();
}else{
def subList = myArgList.subList(0, listSize);
def ncriteria = tableA.createCriteria()
result.addAll(ncriteria.listDistinct {
'in'('id', subList.collect{it.id})
fetchMode('tableBObj', FetchMode.JOIN)
})
myArgList.clear();
}
}
} else {
def criteria = tableA.createCriteria()
result = criteria.listDistinct {
'in'('id', myArgList.collect{it.id})
fetchMode('tableBObj', FetchMode.JOIN)
}
}