Using DISTINCT keyword in JPA on individual columns - java

I am reading some values from a database that is horribly un-normalized (which I can't control). The call retrieves announcements for university departments, and if a user is in multiple departments (which is possible), then the same results are returned multiple times for these users. However, some departments might have different announcements, while some have the same.
Is there a way for me to use the DISTINCT keyword in JPA on individual columns? This is what I currently have for the query:
String jpql = "SELECT DISTINCT annoucement FROM Announcment announcement "
+ "WHERE (announcement.date <= :now AND announcement.endDate >= :now) "
+ "AND announcement.approved = true AND announcement.departmentId IN (:departmentIDs)";
TypedQuery<Announcement> query = entityManager.createQuery(jpql,
Announcement.class);
query.setParameter("now", new Date());
query.setParameter("departmentIDs", departmentIDs);
The departmentID value might be different, but the announcement, dates, etc. are all identical. This query returns announcements that have duplicate values.

two ways I come up with your problem:
"select distinct annoucement.x, annoucement.y, annoucement.z
...(without depId) from..."
then construct an announcement. but you
lost the persistent object and its references. you have to load them
again by your Dao objects if you need.
override equals() [hashCode() too] in your Annoucement class, of
course, in equals(), depId should be out of the comparison. getting
the list as you did, then convert the list to Set. you got
"Distinct" objects
hope it helps

In my opinion a better approach is to return a more specific object holding only the columns you're interested in. See also following answer:
JPQL Constructor Expression - org.hibernate.hql.ast.QuerySyntaxException:Table is not mapped
According to your example you could create a new Object with only the columns you are interested in:
SELECT DISTINCT new com.mypackage.MyInterestingAnnouncement(annoucement.x, annoucement.y, annoucement.z) FROM Announcment annoucement

Related

How to retrieve only part (few) of pojo fields in Hibernate?

I have a situation where I need to return only few fields of a POJO.
Here is a SOF Question: retrieve-single-field-rather-than-whole-pojo-in-hibernate question regarding the same, but few things still seems to be obscure.
1) The answer suggests to use -
String employeeName = session.createQuery("select empMaster.name from EmployeeMaster empMaster where empMaster.id = :id").setInteger("id",10).uniqueResult();
So, here is my concern - Every pojo field is normally private, so "empMaster.name" will simply not work. And am not sure if empMaster.getName() is the solution for this. Will calling the getter methods work?
2) If i am querying multiple fields, (which is my case) (assuming getter methods work) the query will be some thing like -
List<String> employeeDetails = session.createQuery("select empMaster.getName(), empMaster.getDesignation() from EmployeeMaster empMaster where empMaster.id = :id").setInteger("id",10).uniqueResult();
Note the return type has changed from String to List<String>.
2(a) Hope this is right?
2(b) what if i am interested in age/salary of employee which will be of int type. I think the return type will be List<String> or List<Object>. Well any how in the application i can recast the String or Object to the proper type int or float etc. So this should not be a problem.
3) Now what if I am querying multiple employee details (no where clause), so the query will be something like - (not sure if the part of query after from is correct)
List<List<<String>> employeesDetails = session.createQuery("select empMaster.getName(), empMaster.getDesignation() from EmployeeMaster;
Anyway, point here is to emphasise the change in the return type to : List<List<<String>> employeesDetails. Does it work this way ???.
(The question quoted above also has answers pointing to use Projections. I have questions about it but will post them on another question, don't want to mixup.)
I will list the points in the order you mentioned them:
The query has nothing to do with the POJO's field visibility. You are doing a simple query to the database, as if you were doing a query using SQL, and columns in a table have nothing to do with the fact that their mapped POJOs' fields in an application are public or private. The difference is only the language that you're using: now you're using the Hibernate Query Language (HQL), which allows you to express your query with respect to the POJOs' definitions instead of the database's tables' definitions. In fact, doing
session.createQuery("select empMaster.getName() from EmployeeMaster...");
will throw a syntax error: there can be no parenthesis in an object's field name.
By the way, you have to parse your query result to a String, otherwise there would be a compiler semantics error.
String name = (String) session.createQuery("select empMaster.name from EmployeeMaster...").setYourParameters().uniqueResult();
When you do a query whose SELECT clause contains more than one field, and you call uniqueResult(), you're obtaining an array of Objects (Object[]). You iterate through each element in the array and cast it to the corresponding type.
First of all, you probably forgot to add a .list() method call at the end of your query.
session.createQuery("select empMaster.name, empMaster.designation from EmployeeMaster").list();
Otherwise, the query will not be executed, and you would just have a QueryImpl object waiting to be triggered.
Now, when you're expecting multiple results in your query and selecting several fields, you're getting back a List<Object[]>. The elements of the list will be the array of fields for a specific record, and the array per se will be the fields.

HQL order by type()

For example, I have a base entity called 'MyEntityParent' and two sub types 'ZChild' and 'AChild'.
When using the following HQL, this will sort the result list by the type's internal integer value (same as the special class property):
select e from MyEntityParent e
order by type(e)
However, I need to sort the result list by their entity type's name. I.e., first, instances of type 'AChild', then, instances of type 'ZChild'.
Try
select e from MyEntityParent e order by e.class
From the Hibernate 3.6 documentation:
The special property class accesses the discriminator value of an instance in the case of polymorphic persistence. A Java class name embedded in the where clause will be translated to its discriminator value.
http://docs.jboss.org/hibernate/core/3.6/reference/en-US/html/queryhql.html#queryhql-where
But this is deprecated in Hibernate 4.1
Also this is not exactly your wish as it returns the discriminator value, not the exact type name if i read the docs correctly.
There is no straightforward solution, because HQL is translated to SQL and database is not aware of how the value maps to the name of the class. There is way to make it, but it is not nice:
Query gets complex, because we have to define order in query.
Select list contains additional value that defines order, so we cannot return list of entities.
When model changes, query should be modified
Query is:
SELECT e,
CASE TYPE(e) WHEN AChild THEN 1
WHEN ZChild THEN 2
ELSE 3
END AS orderValue
FROM MyEntityParent e ORDER BY orderValue
And as said, result is no more list of entities, it is list of object arrays, so accessing entity is bit more hard. Entity itself is first item in array (index 0) and ordedValue in second:
List<Object[]> result = em.createQuery("JPQL given above").getResultList();
for (Object[] resultRow : result) {
MyEntityParent myEntityParent = (MyEntityParent) resultRow[0];
}
Solution is valid JPQL, so it is not Hibernate specific, but works also with other JPA providers.

hibernate: how to map a Set association so that HQL order by is preserved

OK, this looks mostly like (another) bug/unimplemented functionality in Hibernate.
Is it possible to map a Set association in such a way that the "order by" clause in HQL is respected?
For example, say you have 2 entities: PageEntity and QuestionEntity. A page has a Set.
How do I make the following HQL work:
from
PageEntity p
left outer join fetch p.questionEntities q
order by
q.orderIndex
What I don't want:
to use sort in Java (we have "order by" in SQL so we don't have to do this!). SortedSet, Comparators etc. are out of the question
to map it as a List with a <list-index>. This will add the "order by" to all SQL queries, and I don't want this
to use a <set order-by="orderIndex"> because again, this will be applied to all queries
Debugging, I see that the implementation of Set that is being used is org.hibernate.collection.PersistentSet which wraps a Set. The implementation of the wrapped Set is HashSet. I would expect Hibernate to be smart enough to use a LinkedHashSet instead, so that it can honor my "order by" HQL clause.
I'm pretty sure Hibernate can't do this for you, and would recommend using a List instead. To remove duplicates introduced by the join, you can use the distinct keyword. The resulting List will be just as good as any Set.
If you still need them in an ordered Set (maybe there's a 3rd party API involved) you could create your own LinkedHashSet and move all objects there:
List<PageEntity> list = runQuery(...);
return new LinkedHashSet<PageEntity>(list);
It seems you want to get results sorted by q.orderIndex but
do you want the resulting list of PageEntity to be sorted?
or (as I think) do you want the set of QuestionEntitys for each PageEntity to be sorted?
If you want the latter: Sets in Java do not preserve order.
After your edit: no Hibernate does not have to be smart: if you define your collection to be a Set it will be treated as a set. There are many applications where preserving the order costs resources and is not needed.
order by applies to the results of the query. Not to the entities in the collection of questions. I don't think it's a bug: what would it do if one of the returned page entity is already loaded in the session, using another query with another order by clause?
If you want an ordered list of questions, select the questions:
select q from QuestionEntity q inner join fetch q.page p order by q.orderIndex
or
select p, q from PageEntity p
left outer join fetch p.questionEntities q
order by q.orderIndex
should do the trick.

Hibernate Criteria limit select

So, I have a rather complex query I am trying to make using the Hibernate Criteria API. I have the following entity classes:
Code
Gift
GiftVendor
GiftVendorStatus
with the following relationships:
Code 1<>1 Gift
Gift 1<>* GiftVendor
GiftVendor 1<>1 GiftVendorStatus
I need to build a Criteria query that returns a List of Code objects, but that restricts it to only Codes that have a Gift that have at least one GiftVendor with a GiftVendorStatus of Online. Here is the code I am using to build the criteria:
Criteria base = CodeDao.getBaseCriteria();
base.createAlias("gift","gift");
base.createAlias("gift.giftVendor","giftVendor");
base.createAlias("giftVendor.giftVendorStatus","giftVendorStatus");
base.add(Restrictions.like("giftVendorStatus.description", "Online%"));
return base.list();
This gives me a List of Code objects, restricted as I would expect. However, it also does additional queries to build out all of the unused relationships of the Gift object, even though I have all of the mappings set up with a fetch mode of Lazy. This results in 4 additional, separate queries for each of my 10000+ results.
I have code to do the query using HQL that works as expected:
String hql = "select c FROM Code c inner join c.gift g inner join g.giftVendors gv inner join gv.giftVendorStatus gvs" +
" WHERE gvs.description like :desc";
HashMap<String,Object> params = new HashMap<String, Object>();
params.put("desc", "Online%");
return performQuery(hql, params);
That code gives me a List of Code objects, as expected, without doing all of the extra queries to populate the Gift object. How do I tell Hibernate not to do those extra queries with the Criteria API?
UPDATE: The problem here is not the retrieval of the Gift table, but rather unrelated one-to-one relationships from the Gift table. For example, Gift has a one-to-one relationship to GiftCommentAggregateCache. This table is in no way related to this particular query, so I would expect lazy initialization rules to apply, and the query to GiftCommentAggregateCache to not occur unless a read is attempted. However, with the Criteria query written out as above, it makes that separate query to populate the model object for GiftCommentAggregateCache.
If I use:
base.setFetchMode("gift.giftCommentAggregateCache", FetchMode.JOIN);
then I do not have any problems. However, that means that in order for this to work as I would expect, I need to add that line for every single unused one-to-one relationship that Gift has. Any ideas as to why the Lazy rules specified in the mappings are not coming into play here?
There are a few different things I have tried:
base.setFetchMode("gift", FetchMode.LAZY); // Still does additional queries
and
base.setFetchMode("gift", FetchMode.SELECT); // Still does additional queries
and
base.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY); // Still does additional queries
and
base.setResultTransformer(Criteria.ROOT_ENTITY); // Still does additional queries
and
base.setResultTransformer(Criteria.ALIAS_TO_ENTITY_MAP); // Still does additional queries
and
base.setProjection(Projections.property("gift")); // Does not do additional queries, but incorrectly returns a List of Gift objects, instead of a List of Code objects
From the Hibernate documentation:
Join fetching: Hibernate retrieves the associated instance or collection in the same SELECT, using an OUTER JOIN.
In other words, try using base.setFetchMode("gift", FetchMode.JOIN)
I hope that helps.
Cheers

How do I write hql query with cast?

I need to combine 2 tables using hql, both are having common column, but table1 common column is integer and table2 common column is String
For example,
select a.id as id,a.name as name,b.address as address
from Personal as a,Home as b
where a.id=b.studid
Here a.id is an integer while b.stduid is a string, but Data of both columns is the same.
How can I get the result of the query using hql query?
HQL supports CAST (if underlying database supports it), you can use it:
select a.id as id,a.name as name,b.address as address
from Personal as a,Home as b
where cast(a.id as string) = b.studid
See also:
16.10. Expressions
You really need to think why have you got a need to join two entities by properties of different types. Most likely it suggests that some of the entities need to be refactored, which could include changing data types for columns of the underlying db tables. If the model is correct there will be no need to twist Hibernate.
I had to cast it to String like so :
#Query( value = "select new com.api.models.DResultStatus("+
"cast(ds.demoId as java.lang.String),cast(ds.comp as java.lang.String),cast(ds.dc as java.lang.String),cast(be.buildUrl as java.lang.String)")
Just noticed that you are using JPA, there you can not cast or convert datatpes. In the query language, only values of the same type can be compared! read in http://download.oracle.com/javaee/5/tutorial/doc/bnbuf.html#bnbvu

Categories

Resources