Is there a way to do a "in" statment in javax.persistence.Query [duplicate] - sql

I have the following parametrised JPA, or Hibernate, query:
SELECT entity FROM Entity entity WHERE name IN (?)
I want to pass the parameter as an ArrayList<String>, is this possible? Hibernate current tells me, that
java.lang.ClassCastException: java.util.ArrayList cannot be cast to java.lang.String
Is this possible at all?
ANSWER: Collections as parameters only work with named parameters like ":name", not with JDBC style parameters like "?".

Are you using Hibernate's Query object, or JPA? For JPA, it should work fine:
String jpql = "from A where name in (:names)";
Query q = em.createQuery(jpql);
q.setParameter("names", l);
For Hibernate's, you'll need to use the setParameterList:
String hql = "from A where name in (:names)";
Query q = s.createQuery(hql);
q.setParameterList("names", l);

in HQL you can use query parameter and set Collection with setParameterList method.
Query q = session.createQuery("SELECT entity FROM Entity entity WHERE name IN (:names)");
q.setParameterList("names", names);

Leaving out the parenthesis and simply calling 'setParameter' now works with at least Hibernate.
String jpql = "from A where name in :names";
Query q = em.createQuery(jpql);
q.setParameter("names", l);

Using pure JPA with Hibernate 5.0.2.Final as the actual provider the following seems to work with positional parameters as well:
Entity.java:
#Entity
#NamedQueries({
#NamedQuery(name = "byAttributes", query = "select e from Entity e where e.attribute in (?1)") })
public class Entity {
#Column(name = "attribute")
private String attribute;
}
Dao.java:
public class Dao {
public List<Entity> findByAttributes(Set<String> attributes) {
Query query = em.createNamedQuery("byAttributes");
query.setParameter(1, attributes);
List<Entity> entities = query.getResultList();
return entities;
}
}

query.setParameterList("name", new String[] { "Ron", "Som", "Roxi"}); fixed my issue

Related

Ignite SqlQuery for complex java objects

In my cache I have a complex java object as below -
class Person{
private Department d;
....
}
class Department {
private Department code;
....
}
I am using below SQLQuery to read it -
SqlQuery<Short, BinaryObject> query = new SqlQuery<>(Person.class, "d.code = ?");
String args="101"; // department code
QueryCursor<Cache.Entry<Short, BinaryObject>> resultSet = personCache.query(query.setArgs(args))
I am getting below error -
Caused by: class org.apache.ignite.internal.processors.query.IgniteSQLException: Failed to parse query: SELECT "PERSON_CACHE"."PERSONENTITY"._KEY, "TPERSON_CACHE"."PERSONENTITY"._VAL FROM "PERSON_CACHE"."PERSONENTITY" WHERE id.code = ?
Am I doing anything wrong here ?
You can access nested fields, but only if they were configured with QuerySqlField annotation in advance:
class Person{
private Department d;
...
}
class Department {
#QuerySqlField
private Department code;
....
}
SqlQuery<Short, BinaryObject> query = new SqlQuery<>(Person.class, "code = ?");
Destructuring is not supported by Ignite SQL and there are no solid plans to implement it.
This means you can't peek into fields that are rich objects, maps, lists, etc. You should introduce a departmentId numeric field here.
Theoretically you could also try putting #QuerySqlField annotation on Department's field code, and then access it as CODE = ?. Your mileage may vary. I for one would like to hear about the result of such experiment.
I resolved it by using predicate.
IgniteBiPredicate<Long, BinaryObject> predicate = new IgniteBiPredicate<Long, BinaryObject>() {
#Override
public boolean apply(Long e1, BinaryObject e2) {
Person p= e2.deserialize();
short s = (short) args[0];
return p.getId().getCode == s;
}
};

spring mvc hiberate, use CriteriaQuery to run select * from table where

what i need is to run a sql query something like :
select * from table where alpahbetcolumn="A" and numbercolumn="10" and shelfcolumn="upper";
i want to know how to do this query in hibernate using EntityManager
currently this is my own try out, but not working....
#PersistenceContext
EntityManager em;
#Transactional
public List<Item> listItems(String alpahbet, String number, String shelf) {
CriteriaQuery<Item> c = em.getCriteriaBuilder().createQuery(Item.class);
c.from(Item.class);
c..where( em.equal( alpahbet, "alpahbetcolumn" ) && em.equal( number, "numbercolumn" ) && em.equal( shelf, "shelfcolumn" ));
return em.createQuery(c).getResultList();
}
i only have a very vague understanding on spring hibernate topic..still learning...
can someone please point me out how to do this sql query properly, with code example. thanks
Try this
Query q = em.createNativeQuery("select * from table where alpahbetcolumn='A' and numbercolumn= 10 and shelfcolumn='upper'");
q.getResultList();
createNativeQuery() accepts plain SQL as parameter. If you expect Item as result, you can use this
em.createNativeQuery("select * from table where alpahbetcolumn='A' and numbercolumn= 10 and shelfcolumn='upper'", Item.class);
If you want to use JPQL (JPA Query Language), then we need your entity code to be sure, but it would be something like this
em.createQuery("select i from Item i where i.alphabetColumn = 'A' ");
For parameterized queries, use this
Query q = em.createNativeQuery("select * from table where alpahbetcolumn=? and numbercolumn=? and shelfcolumn=?");
q.setParameter(1, "A");
q.setParameter(2, 10);
q.setParameter(3, "upper");
HQL is like this:
StringBuilder queryBuilder = new StringBuilder();
queryBuilder.append(" FROM table");
queryBuilder.append(" WHERE alpahbetcolumn= :codeA");
queryBuilder.append(" AND numbercolumn= :numColumn");
queryBuilder.append(" AND shelfcolumn= :upper");
Query query = getSession().createQuery(queryBuilder.toString());
query = query.setParameter("codeA", "A");
query.setParameter("numColumn", "10");
query.setParameter("upper", "upper");
query.list(); to get your result ;)
and getSession() come from :
private SessionFactory sessionFactory;
#Required
#Autowired
public void setSessionFactory(SessionFactory sessionFactory) {
this.sessionFactory = sessionFactory;
}
protected Session getSession() {
return sessionFactory.getCurrentSession();
}
see spring configuration to configure your sessionfactory ;)
or if you use JPA try this link
get session from entityManager
i have worked out using criteriaquery to fullfill my needs, here is the code. it works
#Transactional
public List<Item> listItems(String alpahbet, String number, String shelf) {
CriteriaBuilder criteriaBuilder = em.getCriteriaBuilder();
CriteriaQuery<Item> criteriaQuery = criteriaBuilder.createQuery(Item.class);
Root<Item> itemRoot = criteriaQuery.from(Item.class);
criteriaQuery.select(itemRoot).where(criteriaBuilder.equal(itemRoot.get("alpahbetField"), alpahbet), criteriaBuilder.equal(itemRoot.get("numberField"), number), criteriaBuilder.equal(itemRoot.get("shelfField"), shelf));
return em.createQuery(criteriaQuery).getResultList();
}

Why JPQL returns only first and last field in query result using AS?

I have a model:
public class SomeModel extends Model {
private String name;
private String description;
private String comment;
... a lot of other fields
private String note;
}
I want to get the list of models from database as list of maps with only needed fields. I'm doing it following way:
EntityManager em = GuiceConfigSingleton.inject(EntityManager.class);
Query query = em.createQuery("SELECT
o.name AS ModelName,
o.description AS ModelDescription,
o.comment AS ModelComment,
o.note AS ModelNote
FROM SomeModel o");
query.setHint(QueryHints.RESULT_TYPE, ResultType.Map);
Resultlist of this query is a list of maps. And every map contains only first(ModelName) and last(ModelNote) field (other fields is not empty in database). When I'm doing it without "as" using just:
Query query = em.createQuery("SELECT
o.name,
o.description,
o.comment,
o.note
FROM SomeModel o");
I got all fields but without proper keys. What's wrong?
Screenshots with simplified example:
With "AS": http://imgur.com/bKZnqSx
Without "AS": http://imgur.com/PieCRzg
UPDATE:
It was a bug in EclipseLink 2.5.0. With 2.5.2 everything is ok!
there is an other way to fetch these fields
Query query = entityManager.createNativeQuery("your existing query");
final List<Object[]> rsList = (List<Object[]>) query.getResultList();
for (Object[] objects : rsList) {
String modelName = objects[0] == null ? ""
: objects[0].toString();
String modelDesc = objects[1] == null ? ""
: objects[1].toString();
String comment = objects[2] == null ? ""
: objects[2].toString();
String note = objects[3] == null ? ""
: objects[3].toString();
}
I hope it helps.
It is a bug in 2.5.0 version of EclipseLink. Since 2.5.1 everything is ok!
Lesson learned: always check for the last release.

Hibernate complains about double single quote

I am trying to select some data using Hibernate
The query ends up as follows
"select mystuff from mytable where upper(mystuff)=upper('a''c')"
( a'c is comming from user input escaped by StringEscapeUtils.escapeSql())
Hibernate is giving me a SQLGrammarException. Did I escape that single quote wrong?
I think '' to escape ' is good. I suppose that Hibernate + some RDBMS can have some mistakes with those quotes escaping. You should try to use preparedStatement or Query interface of Hibernate. To inject you data with method like this :
String selectSQL = "select mystuff from mytable where upper(mystuff)=upper(?)";
PreparedStatement preparedStatement = dbConnection.prepareStatement(selectSQL);
preparedStatement.setString(1, "a'c");
ResultSet rs = preparedStatement.executeQuery(selectSQL);
while (rs.next()) {
String mystuff = rs.getString("mystuff");
}
Or like this :
Query query = session.createQuery("from mytable where upper(mystuff)=upper(:mystuff)");
query.setString("mystuff", "a'c");
List<?> list = query.list();
So hibernate will be able to handle it.
Hope it helps :)

hibernate - createCriteria or createAlias?

If I want to search those students who take class "Math" and "John" is his group:
shoud I use createCriteria or createAlias?
Criteria:
Criteria criteria = session.createCriteria(Student.class);
Criteria subquery1 = criteria.createCriteria("courses", course).add(Restrictions.eq(course.name, "Math"));
Criteria subquery2 = criteria.createCriteria("group", student).add(Restrictions.eq(student.name, "John"));
how to put subquery1 and subquery2 together with initial criteria?
Alias:
Criteria criteria = session.createCriteria(Student.class).
createAlias("courses", course).add(Restrictions.eq(course.name, "Math")).
createCriteria("group", student).add(Restrictions.eq(student.name, "John"));
When to use createCriteria and when createAlias? I think the boath are the same...
CreateAlias and CreateCriteria are identical in the current versions of Hibernate and NHibernate. The only difference being that CreateCriteria has 2 additional overloads without the alias parameter.
Presumably they were different in a older version, but any differences are long gone.
An alias can be defined in terms of another alias, so your first example can be written as:
// Java
Criteria criteria = session.createCriteria(Student.class)
.createAlias("courses", "course")
.createAlias("course.group", "student")
.add(Restrictions.eq("course.name", "Math"))
.add(Restrictions.eq("student.name", "John"));
// C#
ICriteria criteria = session.CreateCriteria<Student>()
.CreateAlias("Courses", "course")
.CreateAlias("course.Group", "student")
.Add(Restrictions.Eq("course.Name", "Math"))
.Add(Restrictions.Eq("student.Name", "John"));
Adding to xavierzhoa's answer:
There is actually quite a big difference between the two methods which you will notice if you chain Criteria methods. You will continue to work on the original Criteria object when using createAlias, whereas you work on a more nested scope when using createCriteria.
Consider this:
Criteria c = getSession()
.createCriteria(YourEntity.class)
.createCriteria("someMember", "s")
.add(Restrictions.eq("name", someArgument)); // checks YourEntity.someMember.name
versus
Criteria c = getSession()
.createCriteria(YourEntity.class)
.createAlias("someMember", "s")
.add(Restrictions.eq("name", someArgument)); // checks YourEntity.name
However, if you always assign and use an alias you will be able to work around the difference. Like:
Criteria c = getSession()
.createCriteria(YourEntity.class, "y")
.createAlias("someMember", "s")
.add(Restrictions.eq("y.name", someArgument)); // no more confusion
Please refer to the following source code from the Hibernate
public Criteria createCriteria(String associationPath, String alias, int joinType) {
return new Subcriteria( this, associationPath, alias, joinType );
}
public Criteria createAlias(String associationPath, String alias, int joinType) {
new Subcriteria( this, associationPath, alias, joinType );
return this;
}
Criteria criteria = (Criteria)sessionFactory.getCurrentSession().createCriteria(BillEntity.class)
.createAlias("worksEntity", "worksEntity" ,JoinType.LEFT_OUTER_JOIN)
instead of writing (Jointype) use the import file as
(org.hibernate.sql.JoinType..LEFT_OUTER_JOIN)