I'm using JPQL to retrieve data. I can get data using the statement
List persons = null;
persons = em.createQuery("select p.albumName from PhotoAlbum p , Roleuser r
where r = p.userId and r.userID = 1");
Now I can get the album names using this:
int i=0;
for (i=0;i<persons.size(); i++)
{
System.out.println("Testing n "+ i +" " + persons.get(0));
}
Now I want to get the album name and the roleuser's row named firstname
I'm using the query
persons = em.createQuery("select r.firstName , p.albumName from PhotoAlbum p ,
Roleuser r where r = p.userId and r.userID = 1").getResultList();
Now how do I get the rows firstname and albumname as the persons.get(0) is returning a object
by running the code :
for (i=0;i<persons.size(); i++)
{
//r = (Roleuser) persons.get(i);
System.out.println("Testing n "+ i +" " + persons.get(i));
}
I'm getting this:
Testing n 0 [Ljava.lang.Object;#4edb4077
INFO: Testing n 1 [Ljava.lang.Object;#1c656d13
INFO: Testing n 2 [Ljava.lang.Object;#46dc08f5
INFO: Testing n 3 [Ljava.lang.Object;#654c0a43
How do I map the persons.get(0) and get the firstname and albumname?
Now how do get the rows firstname and albumname as the persons.get(0) is returning a object
Queries with multiple select_expressions in the SELECT clause return an Object[] (or a List of Object[]). From the JPA specification:
4.8.1 Result Type of the SELECT Clause
The type of the query result specified
by the SELECT clause of a query is an
entity abstract schema type, a
state-field type, the result of an
aggregate function, the result of a
construction operation, or some
sequence of these.
The result type of the SELECT clause
is defined by the the result types of
the select_expressions contained in
it. When multiple
select_expressions are used in the SELECT clause, the result of the query
is of type Object[], and the
elements in this result correspond in
order to the order of their
specification in the SELECT clause and
in type to the result types of each of
the select_expressions.
So in your case, you probably want something like this:
for (i=0;i<persons.size(); i++) {
//r = (Roleuser) persons.get(i);
System.out.println("Testing n " + i + " " + persons.get(i)[0] + ", " +
persons.get(i)[1]);
}
Note that specifying an inner join by the use of a cartesian product in the FROM clause and a join condition in the WHERE clause is less typical than specifying an explicit join over entity relationships (using the [LEFT [OUTER] | INNER ] JOIN syntax). See the whole section 4.4.5 Joins in the specification.
References
JPA 1.0 Specification
Section 4.8.1 "Result Type of the SELECT Clause"
Section 4.8.2 "Constructor Expressions in the SELECT Clause"
Section 4.4.5 "Joins"
Related
I need to send a query to retrieve values that has a specific group of characters as following:
Lets say I am interested in 'XX' so it should search for any field that its value starts with 'XX' or has ' XX' (space XX). For example XXCDEF, PD XXRF and CMKJIEK XX are valid results.
I have following query that
returns the correct results but I need to sort them
in a way that it first return those with XX at the beginning then other results. As following:
XXABCD
XXPLER
XXRFKF
AB XXAB
CD XXCD
ZZ XXOI
POLO XX
Code
Criteria criteria = session.createCriteria(Name.class, "name")
.add(Restrictions.disjunction()
.add(Restrictions.ilike("name.fname", fname + "%"))
.add(Restrictions.ilike("name.fname", "%" + " " + fname + "%"))
)
.setProjection(Projections.property("name.fname").as("fname"));
List<String> names = (List<String>) criteria.list();
With JPQL (HQL):
select fname from Name
where upper(fname) like :fnameStart or upper(fname) like :fnameMiddle
order by (case when upper(fname) like :fnameStart then 1 else 2 end), fname
query.setParameter("fnameStart", "XX%");
query.setParameter("fnameMiddle", "% XX%");
With Criteria
With Criteria it's much trickier. Firstly, you have to resort to native SQL in the order clause. Secondly, you have to bind the variable.
public class FirstNameOrder extends Order {
public FirstNameOrder() {
super("", true);
}
#Override
public String toSqlString(Criteria criteria, CriteriaQuery criteriaQuery) throws HibernateException {
return "case when upper(FIRST_NAME) like ? then 1 else 2 end";
}
}
The case expression syntax and the upper function name should be changed in accordance with your database (and the column name if it's different, of course).
It is easy to add this to the Criteria, but there is no API to bind the parameter.
I tried to trick Hibernate by passing in an unused variable to the custom sql restriction so that it is effectively used for the variable in the order by clause:
Criteria criteria = session.createCriteria(Name.class, "name")
.add(Restrictions.disjunction()
.add(Restrictions.ilike("name.fname", fname + "%"))
.add(Restrictions.ilike("name.fname", "%" + " " + fname + "%")))
.setProjection(Projections.property("name.fname").as("fname"))
.add(Restrictions.sqlRestriction("1 = 1", fname + "%", StringType.INSTANCE))
.addOrder(new FirstNameOrder())
.addOrder(Order.asc("fname"));
and it works fine.
Obviously, this solution is not recommended and I suggest using JPQL for this query.
Hibernate supports Order: http://docs.jboss.org/hibernate/orm/4.2/devguide/en-US/html/ch11.html#ql-ordering
Because of the special criteria, I think you have to custom the Order in Hibernate. This link may help:
http://blog.tremend.ro/2008/06/10/how-to-order-by-a-custom-sql-formulaexpression-when-using-hibernate-criteria-api/
Run two selects, one filtered for all the strings starting with 'XX', the second filtered for the others.
You can use Predicates in criteria... something like this:
public List<Name> findByParameter(String key, String value, String orderKey)
CriteriaBuilder builder = this.entityManager.getCriteriaBuilder();
CriteriaQuery<Name> criteria = builder.createQuery(this.getClazz());
Root<Name> root = criteria.from(Name.getClass());
criteria.select(root);
List<Predicate> predicates = new ArrayList<Predicate>();
predicates.add(builder.equal(root.get(key), value));
criteria.where(predicates.toArray(new Predicate[predicates.size()]));
if (orderKey!=null && !orderKey.isEmpty()) {
criteria.orderBy(builder.asc(root.get(orderKey)));
}
result = this.entityManager.createQuery(criteria).getResultList();
return result;
}
Stupid but it may work for your case.
Since you got your correct result you can just reconstruct your results as follows:
pick up all results starting with XX you put them into a list L1 and do the normal sort like Collections.sort(L1);
for all other results, do the same like Collections.sort(L2)as list of L2
At last , put them together
List newList = new ArrayList(L1);
newList.addAll(L2);
Please note. Collections.sort are following the natural ordering of its elements.
If you don't want to sort the result in memory,you can modify your criteria,I'll show you the SQL
select * from table where fname like 'XX%' order by fname
union all
select * from table where fname like '% XX%' order by fname
union all
select * from table where fname like '% XX' order by fname
the result will be your order and alphabetical and then apply your filter.
I get a list from my query.list(). After that, I want to display the object inside this list but I got this error for this line for(Commerce[] c: this.newsList) {
java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to [Lcom.model.Commerce;
com.action.CommerceAction.searchCommerces(CommerceAction.java:35)
Here is my code:
My business service
public List<Commerce[]> getCommercesBySearch(String categorie, String lieux) {
Query query = hibUtil.getSession().createQuery("from Commerce c, Categorie ca,Lieux l "
+ "where c.categorie=ca.idCategorie and c.lieux=l.idLieux and"
+ " ca.libelle='" + categorie + "' and l.ville='" + lieux + "' ");
List<Commerce[]> tuples = (List<Commerce[]>) query.list();
return tuples;
}
My action class
private CommerceService service;
private Commerce commerce = new Commerce();
private List<Commerce[]> newsList;
public String searchCommerces() {
String libelle = ServletActionContext.getRequest().getParameter("libelle");
String ville = ServletActionContext.getRequest().getParameter("ville");
this.newsList = service.getCommercesBySearch(libelle,ville);
for(Commerce[] c: this.newsList){
System.out.println(c[0].getNom());
}
if (this.newsList.size() > 0) {
return SUCCESS;
}
addActionError("Il n'existe aucun commerce de cette catégorie dans cette ville");
return ERROR;
}
I am certain that this statement:
List<Commerce[]> tuples = (List<Commerce[]>) query.list();
produces an unchecked type conversion warning. Your code is polluting the heap by doing this unchecked type conversion. query.list() returns a raw List, which will contain Object[]. Here is the relevant Hibernate documentation:
Return the query results as a List. If the query contains multiple results per row, the results are returned in an instance of Object[].
Note that you cannot cast an array to an array of it's sub-type.
There are a couple of ways to fix this problem:
Use List<Object[]> instead of List<Commerce[]>. You can then transform the result of the list() method into a more usable form, preferably within it's own method, before passing it on to the rest of your code. This is the preferable method if you need to select more than just the Commerce object.
Add SELECT c to the beginning of your query, this will allow you to do a safe List<Commerce> cast.
Use select c before your from or you will have some trouble with the returned Object[]. If you don't use this, Hibernate will return a list of Object[] containing more than one object into this. For your problem it will be like:
Object[0] is an instance of Commerce
Object[1] is Categorie
Object[3] is Lieux
etc.
You will throw an ClassCastException if you try to cast Object[] into Commerce.
I think the problem lies here:
Query query = hibUtil.getSession().createQuery("from Commerce c, Categorie ca,Lieux l "
+ "where c.categorie=ca.idCategorie and c.lieux=l.idLieux and"
+ " ca.libelle='" + categorie + "' and l.ville='" + lieux + "' ");
List<Commerce[]> tuples = (List<Commerce[]>) query.list();
That's not correct, considering your idea. I believe that your query should return a List<Commerce>, not List<Commerce[]>.
List<Commerce> tuples = (List<Commerce>) query.list();
To make that works, you need to add SELECT c in your query:
SELECT c from Commerce c, Categorie ca,Lieux l...
It will select a list of object Commerce. If you leave the query like originally, it will return a list of Object array (actually they are Commerce[], Categorie[], Lieux[]...). Not to mention you can't cast directly an array in Java, the objects aren't the same type anyway.
I have three tables/classes which I'm fetching with a left outer join. I'm executing the following query:
List<Object[]> l = session.createSQLQuery("SELECT a.*, b.*, c.* " +
" FROM (table_a a INNER JOIN table_b b ON a.some_id = b.some_id) " +
" LEFT OUTER JOIN table_c c ON c.some_id = a.some_id"
).addEntity("a", A.class).addEntity("b", B.class).addEntity("c", C.class).list();
The query fails when rows in A and B exist but there is no corresponding row in C, as hibernate tries to assign null values to non-null attributes in C (I can't change these attributes to be nullable). Is there a setting in hibernate to return a null object for C if a row in C does not exist rather than trying to create an object with null values?
if i didn't understand wrong. you are trying to get result if c is null or not null anyway.
you can add this to where clause like this:
and ((c.name=1234 and c.price=1234) or c.id is null)
How can i access the elements from the list created as below? I tried creating a class containing a String and an int and cast to it but it doesn't work.
List SOList = iadao.getSession().createQuery("select a.sistemOperare, count(a.sistemOperare) from User a, IstoricAfisari b, Banner c, Advertiser d where b.bannerId = c.id and c.advertiserId = d.id and b.userId = a.id group by a.sistemOperare").list();
Thank you
This produces a List of Object arrays -> List<Object[]>
Since the createQuery(HQL).list() returns a List matching the indexes of the selected fields:
List SOList = ...
for (Object obj : SOList) {
Object[] fields = (Object[]) obj;
System.out.println("sistemOperare = " + fields[0] + " (count = " + fields[1] + ")");
}
Would print the results from the query, if any. According to hibernate documentation I could find (Since I'm more used to creating criteria for objects I want and then use Java for the rest).
How can I extract variables total, min, max from hibernate SQL queries and assign them to java variables?
(select count(*) as total, min(price) as min, max(price) as max from product).addScalar("total", Hibernate.INTEGER).addScalar("min", Hibernate.INTEGER).addScalar("max", Hibernate.INTEGER);
This post should help you.
Later edit:
String sQuery = "select min(myEntity.x), max(myEntity.y) from MyEntity myEntity";
Query hQuery = session.createQuery(sQuery);
List result = hQuery.list();
Iterator iterator = result.iterator();
while (iterator.hasNext()) {
Object[] row = (Object[])iterator.next();
for (int col = 0; col < row.length; col++) {
System.out.println(row[col]);
}
}
Scalar queries return a List of Object arrays (Object[]) - or a single Object[] in your case.
It is however possible to return non-managed entities using a ResultTransformer. Quoting the Hibernate 3.2: Transformers for HQL and SQL blog post:
SQL Transformers
With native sql returning non-entity
beans or Map's is often more useful
instead of basic Object[]. With
result transformers that is now
possible.
List resultWithAliasedBean = s.createSQLQuery(
"SELECT st.name as studentName, co.description as courseDescription " +
"FROM Enrolment e " +
"INNER JOIN Student st on e.studentId=st.studentId " +
"INNER JOIN Course co on e.courseCode=co.courseCode")
.addScalar("studentName")
.addScalar("courseDescription")
.setResultTransformer( Transformers.aliasToBean(StudentDTO.class))
.list();
StudentDTO dto =(StudentDTO) resultWithAliasedBean.get(0);
Tip: the addScalar() calls were
required on HSQLDB to make it match a
property name since it returns column
names in all uppercase (e.g.
"STUDENTNAME"). This could also be
solved with a custom transformer that
search the property names instead of
using exact match - maybe we should
provide a fuzzyAliasToBean() method
;)
See also
16.1.5. Returning non-managed entities