Spring hibernate template list as a parameter - java

i'm trying to execute this query :
Code:
this.getHibernateTemplate()
find("select distinct ci.customer " +
"from CustomerInvoice ci " +
"where ci.id in (?) " , ids);
with ids as a List, id is of type Long
when executing i get exception
Code:
java.lang.ClassCastException: java.util.ArrayList cannot be cast to java.lang.Long
at org.hibernate.type.LongType.set(LongType.java:42)
at org.hibernate.type.NullableType.nullSafeSet(NullableType.java:136)
at org.hibernate.type.NullableType.nullSafeSet(NullableType.java:116)
at org.hibernate.param.PositionalParameterSpecification.bind(PositionalParameterSpecification.java:39)
at org.hibernate.loader.hql.QueryLoader.bindParameterValues(QueryLoader.java:491)
at org.hibernate.loader.Loader.prepareQueryStatement(Loader.java:1563)
at org.hibernate.loader.Loader.doQuery(Loader.java:673)
at org.hibernate.loader.Loader.doQueryAndInitializeNonLazyCollections(Loader.java:236)
at org.hibernate.loader.Loader.doList(Loader.java:2220)
at org.hibernate.loader.Loader.listIgnoreQueryCache(Loader.java:2104)
at org.hibernate.loader.Loader.list(Loader.java:2099)
at org.hibernate.loader.hql.QueryLoader.list(QueryLoader.java:378)
at org.hibernate.hql.ast.QueryTranslatorImpl.list(QueryTranslatorImpl.java:338)
at org.hibernate.engine.query.HQLQueryPlan.performList(HQLQueryPlan.java:172)
at org.hibernate.impl.SessionImpl.list(SessionImpl.java:1121)
at org.hibernate.impl.QueryImpl.list(QueryImpl.java:79)
at org.springframework.orm.hibernate3.HibernateTemplate$29.doInHibernate(HibernateTemplate.java:849)
at org.springframework.orm.hibernate3.HibernateTemplate.execute(HibernateTemplate.java:372)
at org.springframework.orm.hibernate3.HibernateTemplate.find(HibernateTemplate.java:840)
at org.springframework.orm.hibernate3.HibernateTemplate.find(HibernateTemplate.java:836)
at

If you want to add a list to an in clause it is best to use a named parameter. This is done like so.
Query q = this.getHibernateTemplate().getSession().createQuery("select distinct ci.customer " +
"from CustomerInvoice ci " +
"where ci.id in (:idsParam) ");
q.setParameter("idsParam", ids);
List<Customer> = q.getResultList();

In addition to mR_fr0g's answer, this one also works:
this.getHibernateTemplate()
findByNamedParam("select distinct ci.customer " +
"from CustomerInvoice ci " +
"where ci.id in (:ids) ", "ids", ids);

You could use the Hibernate Criteria API, it has a so called "in" Restriction.
Reference:
Restrictions.in(String, Collection)
Btw. be aware of cases where the ids collection is empty! (not only if you use the criteria API)

You can use parameter list to inlcude in your query with 'IN' and 'setParameterList'
List<Long> ids= new ArrayList<Long>();
Query query = getSession().createQuery("select distinct ci.customer from CustomerInvoice ci where ci.id in (:ids) ");
query.setParameterList("ids", ids);
query.executeUpdate();

ProjectionList projList =
Projections.projectionList().add("customer","customer");
List<Long> ids = ids;
Criteria criteria = hibernateTemplate.getSessionFactory().getCurrentSession()
.createCriteria(CustomerInvoice.class)
.add(Restrictions.in("id",ids))
.setProjection(projList); List<Long> listOfIds = criteria.list();

Related

How can you set a list as a query paramter? [duplicate]

I have built a NamedQuery that looks like this:
#NamedQuery(name = "EventLog.viewDatesInclude",
query = "SELECT el FROM EventLog el WHERE el.timeMark >= :dateFrom AND "
+ "el.timeMark <= :dateTo AND "
+ "el.name IN (:inclList)")
What I want to do is fill in the parameter :inclList with a list of items instead of one item. For example if I have a new List<String>() { "a", "b", "c" } how do I get that in the :inclList parameter? It only lets me codify one string. For example:
setParameter("inclList", "a") // works
setParameter("inclList", "a, b") // does not work
setParameter("inclList", "'a', 'b'") // does not work
setParameter("inclList", list) // throws an exception
I know I could just build a string and build the whole Query from that, but I wanted to avoid the overhead. Is there a better way of doing this?
Related question: if the List is very large, is there any good way of building query like that?
When using IN with a collection-valued parameter you don't need (...):
#NamedQuery(name = "EventLog.viewDatesInclude",
query = "SELECT el FROM EventLog el WHERE el.timeMark >= :dateFrom AND "
+ "el.timeMark <= :dateTo AND "
+ "el.name IN :inclList")
The proper JPA query format would be:
el.name IN :inclList
If you're using an older version of Hibernate as your provider you have to write:
el.name IN (:inclList)
but that is a bug (HHH-5126) (EDIT: which has been resolved by now).
public List<DealInfo> getDealInfos(List<String> dealIds) {
String queryStr = "SELECT NEW com.admin.entity.DealInfo(deal.url, deal.url, deal.url, deal.url, deal.price, deal.value) " + "FROM Deal AS deal where deal.id in :inclList";
TypedQuery<DealInfo> query = em.createQuery(queryStr, DealInfo.class);
query.setParameter("inclList", dealIds);
return query.getResultList();
}
Works for me with JPA 2, Jboss 7.0.2
You must convert to List as shown below:
String[] valores = hierarquia.split(".");
List<String> lista = Arrays.asList(valores);
String jpqlQuery = "SELECT a " +
"FROM AcessoScr a " +
"WHERE a.scr IN :param ";
Query query = getEntityManager().createQuery(jpqlQuery, AcessoScr.class);
query.setParameter("param", lista);
List<AcessoScr> acessos = query.getResultList();

Oracle - How to use START WITH in loop (hibernate select)

I am using hibernate for this select:
NativeQuery sqlQuery = session.createSQLQuery("select :id, min(a.time) " +
"from table1 a, (" +
" select parentid" +
" from (" +
" select LEVEL, parentid" +
" from table2 " +
" START WITH id = :id" +
" CONNECT BY NOCYCLE PRIOR parentid = id" +
" order by level desc)" +
" where rownum = 1" +
" ) b " +
"where a.id = b.parentid");
sqlQuery.setParameter("id", id);
List<Object[]> list = sqlQuery.list();
I need to use this for a lot of ids. Ideally I would pass a list of ids, run that in oracle and return the result. I cannot do that because of the START WITH clause.
This way I send a query to database for each id, which is really slow.
Is there any way to put a list of ids to the query, do this in loop and return back to the application with a list of results?
NativeQuery sqlQuery = session.createSQLQuery("...?...");
sqlQuery.setListParameter("ids", ids);
List<Object[]> list = sqlQuery.list();
Edit: I cannot use recursive with, because we use Oracle 10.
I don't know about the Oracle specific syntax, but with the SQL standard WITH RECURSIVE syntax, you can of course list multiple parameters. Just use the IN predicate e.g. for batches of 5 id IN (:id1, :id2, :id3, :id4, :id5) and then bind the values with setParameter("id1", ...). If you are interested, you can also formulate this with Blaze-Persistence on top of Hibernate by using the JPA model: https://persistence.blazebit.com/documentation/1.6/core/manual/en_US/#recursive-ctes
In your case this would look like the following:
#CTE
#Entity
public class ResultCte {
#Id
Long parentId;
Long rootId;
int level;
}
CriteriaBuilder<Tuple> cb = criteriaBuilderFactory.create(entityManager, Tuple.class);
cb.withRecursive(ResultCte.class)
.from(Entity2.class, "e2")
.where("e2.id").in(idList)
.bind("parentId").select("e2.parent.id")
.bind("rootId").select("e2.id")
.bind("level").select("1")
.unionAll()
.from(Entity2.class, "e2")
.from(ResultCte.class, "cte")
.where("e2.id").eqExpression("cte.parentId")
.bind("parentId").select("e2.parent.id")
.bind("rootId").select("cte.rootId")
.bind("level").select("cte.level + 1")
.end()
.from(Entity1.class "a")
.from(ResultCte.class, "cte")
.where("a.id").eqExpression("cte.parentId")
.select("cte.rootId")
.select("min(a.time)")

Hibernate createNativeQuery - get more than one Entity

I am using following code to execute native SQL query with hibernate:
Query zonesQuery = session.createNativeQuery(
"Select * \n" +
"FROM dbo.Structure AS S\n" +
"JOIN dbo.StructureLocationType AS SLT ON SLT.StructureId = S.Id\n" +
"WHERE SLT.LocationTypeId = :lc").addEntity(StructureEntity.class);
zonesQuery.setParameter("lc", locationTypeID);
List<StructureEntity> zones = zonesQuery.list();
So it works and it gets me list of StructureEntity
now, because my sql query "join" from StructureLocationType table, is there possibility to get whole StructureLocationType row as well, still using single query?
Thank you.
It can be achieved with the following (notice curly braces in SQL and entities aliases):
Query query = session
.createNativeQuery(
"SELECT {S.*}, {SLT.*} " +
"FROM dbo.Structure AS S " +
"JOIN dbo.StructureLocationType AS SLT ON SLT.StructureId = S.Id " +
"WHERE SLT.LocationTypeId = :lc")
.unwrap(SQLQuery.class)
.addEntity("S", StructureEntity.class)
.addEntity("SLT", StructureLocationTypeEntity.class)
.setParameter("lc", locationTypeID);
List<Pair<StructureEntity, StructureLocationTypeEntity>> result = ((List<Object[]>) query.list())
.stream()
.map(p -> Pair.of((StructureEntity) p[0], (StructureLocationTypeEntity) p[1]))
.collect(Collectors.toList());
You can't get multiple Objects from one query.
But you could either select which columns you want and then iterate the returned Object array:
The query:
SELECT s.id, s.someColumn, slt.id, slt.structureId
FROM dbo.Structure AS s
JOIN dbo.StructureLocationType AS slt on slt.structureId = s.id
WHERE slt.locationTypeId = :lc
Then iterate over the Object array:
List<Object[]> result = query.getResultList();
Or you could create a view on your database and map it to a java entity using the Table annotation like it was a normal table:
#Entity
#Table(name = "STRUCTURE_LOCATION_TYPE_VIEW")
public class StructureAndLocationType {
// ...
}
I thought there is a way to map from a query to an Object without creating a DB view but couldn't find it right now.

hql in-clause with multiple hits in child collection

I have a table/entity called Recipe with a child collection of type Tag.
I want to be able to find a Recipe searching by two or more tags. Something like:
SELECT re FROM Recipe re JOIN re.tags t WHERE t in :tagsIds
but I only want those hits where the Tag collection contains all tagIds.
Is it possible in HQL/SQL? (Maybe using Criteria?)
Thanks in advance.
I am assuming you have two different entities Recipe and Tag, it can be done as below.
Criteria criteria = getSession().createCriteria("Recipe.class");
criteria.createAlias("tags", "tag");
criteria.add(Restrictions.in("tag.id", Arrays.asList(1,2,3)));
return (List<Recipe>) criteria.list();
I believe you may be missing parentheses in the HQL. It should be:
... WHERE t in (:tagsIds)
Ok, so this did it. Thanks for the replies.
String hql = "select r from Recipe r " +
"join r.tags t " +
"where t.id in (:tags) " +
"group by r " +
"having count(t)=:tag_count";
Query query = session.createQuery(hql);
query.setParameterList("tags", tagIds);
query.setInteger("tag_count", tagIds.size());
List<Recipe> recipes = query.list();

Adding IN clause List to a JPA Query

I have built a NamedQuery that looks like this:
#NamedQuery(name = "EventLog.viewDatesInclude",
query = "SELECT el FROM EventLog el WHERE el.timeMark >= :dateFrom AND "
+ "el.timeMark <= :dateTo AND "
+ "el.name IN (:inclList)")
What I want to do is fill in the parameter :inclList with a list of items instead of one item. For example if I have a new List<String>() { "a", "b", "c" } how do I get that in the :inclList parameter? It only lets me codify one string. For example:
setParameter("inclList", "a") // works
setParameter("inclList", "a, b") // does not work
setParameter("inclList", "'a', 'b'") // does not work
setParameter("inclList", list) // throws an exception
I know I could just build a string and build the whole Query from that, but I wanted to avoid the overhead. Is there a better way of doing this?
Related question: if the List is very large, is there any good way of building query like that?
When using IN with a collection-valued parameter you don't need (...):
#NamedQuery(name = "EventLog.viewDatesInclude",
query = "SELECT el FROM EventLog el WHERE el.timeMark >= :dateFrom AND "
+ "el.timeMark <= :dateTo AND "
+ "el.name IN :inclList")
The proper JPA query format would be:
el.name IN :inclList
If you're using an older version of Hibernate as your provider you have to write:
el.name IN (:inclList)
but that is a bug (HHH-5126) (EDIT: which has been resolved by now).
public List<DealInfo> getDealInfos(List<String> dealIds) {
String queryStr = "SELECT NEW com.admin.entity.DealInfo(deal.url, deal.url, deal.url, deal.url, deal.price, deal.value) " + "FROM Deal AS deal where deal.id in :inclList";
TypedQuery<DealInfo> query = em.createQuery(queryStr, DealInfo.class);
query.setParameter("inclList", dealIds);
return query.getResultList();
}
Works for me with JPA 2, Jboss 7.0.2
You must convert to List as shown below:
String[] valores = hierarquia.split(".");
List<String> lista = Arrays.asList(valores);
String jpqlQuery = "SELECT a " +
"FROM AcessoScr a " +
"WHERE a.scr IN :param ";
Query query = getEntityManager().createQuery(jpqlQuery, AcessoScr.class);
query.setParameter("param", lista);
List<AcessoScr> acessos = query.getResultList();

Categories