JPA query getResultList unmodifiable list? - java

I have started using JPA 2 / JPQL. I see a lot of examples where query.getResultList() is returned. But have never seen the resultlist marked as unmodifiable. For instance:
final TypedQuery<String> query = entityManager.createNamedQuery("Some query", String.class); //$NON-NLS-1$
return query.getResultList();
Is there any reason this should not be marked as unmodifiable, if I dont expect to change the result set returned from the database?
Any reason this could cause issues?

It's more there's no reason to mark it as unmodifiable. If you want to change the contents of the list, feel free, it's just the results from a database.
The list in the query object has no reason to make it unmodifiable. It's just an in memory store of what's in the database at that point so modifying this list doesn't make changes to the database unless you actually persist this new list.

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.

hibernate left join lazycollection untouchable

I got something like this:
Criteria crit = session.createCriteria(Parent.class,"p");
parentsList = crit.createCriteria(
"childSet","c",
JoinType.LEFT_OUTER_JOIN,
Restrictions.eq("jt.2ndParentDto.pk2ndParentDto", pk2ndParent))
.list();
My query returns a list of parents with one child each or none, i already tested the logged query directly, so i am pretty sure of it.
I have to retrieve a list of children, so i am adding the parent and creating the ones missing.
List<ChildDto> list=new ArrayList<ChildDto>();
for(ParentDto item:parentsList){
Iterator<ChildDto> it=item.getChildSet().iterator();
if(it.hasNext()){
ChildDto dto = it.next();
dto.setParentDto(item);
list.add(dto);
}
else{
ChildDto dto = new ChildDto();
dto.setParentDto(item);
list.add(dto);
}
}
return list;
By calling item.getChildSet().iterator() hibernate loads the entire collection so i cannot call item.getChildSet().iterator().hasNext to check if there is something in the set, and i cannot call item.getChildSet().size() neither for the exact same reason...
then how?, what else is there?, i am currently out of ideas, how can i get the only item of the set if there is one?
Update: I just tried Extra lazy loading, but it doesn't change for better or worse...
item.getChildSet().iterator() still causes to load the entire collection.
And when i do item.getChildSet().size() hibernate triggers a count... so i always get size of the entire collection (no use).
And that's pretty much it =/
Update: I got it working with a projection by getting a list of Object[] items, and manually creating the classes.
I don't like to do this because, with a change to the Hbm, you're forced to maintain queries of this kind, so i try to avoid this as much as possible.
I'm not sure I understand exactly what you're asking, but I think you're looking for Hibernate "extra lazy" collections, which allow you to get some information about a collection, including the size, without initializing the entire collection, as well as load large collections into memory in batches, rather than all at once.
Can you change the query to return the child records instead? That way you won't get the whole collection. I am not begin clear. Can you just get the child objects from the database, then call something like getParent() to get the parents you need?

Inject attribute into JPQL SELECT clause

Let's depict the following use case: I have a JPQL Query which on the fly creates data objects using the new keyword. In the SELECT clause I would like to inject an attribute which is not known to the database but to the layer which queries it.
This could look like
EntityManager em; // Got it from somewhere
boolean editable = false; // Value might change, e.g. depending on current date
Query q = em.createQuery("SELECT new foo.bar.MyDTO(o, :editable) FROM MyObject o")
.setParameter("editable", editable);
List<MyDTO> results = (List<MyDTO>) q.getResultList();
Any ideas how this kind of attribute or parameter injection into the SELECT clause might work in JPQL? Both JPA and JPA 2.0 solutions are applicable.
Edit: Performance does not play a key role, but clarity and cleanness of code.
Have you measured a performance problem when simply iterating over the list of results and call a setter on each of the elements. I would guess that compared to
the time it takes to execute the query over the database (inter-process call, network communication)
the time it takes to transform each row into a MyObject instance using reflection
the time it takes to transform each MyObject instance into a MyDTO using reflection
your loop will be very fast.
If you're so concerned about performance, you should construct your MyDTO instances manually from the returned MyObject instances instead of relying on Hibernate and reflection to do it.
Keep is simple, safe, readable and maintainable first. Then, if you have a performance problem, measure to detect where it comes from. Then and only then, optimize.
It will not work without possible vendor extensions, because according specification:
4.6.4 Input Parameters
...
Input parameters can only be used in the
WHERE clause or HAVING clause of a query.

Building resultset using collection object

I had an issue in building the resultset using Java.
I am storing a collection object which is organized as row wise taken from a resultset object and putting the collection object (which is stored as vector/array list) in cache and trying to retrieve the same collection object.
Here I need to build back the resultset again using the collection object. Now my doubt is building the resultset in this way possible or not?
The best idea if you are using a collection in place of a cache is to use a CachedRowSet instead of a ResultSet. CachedRowSet is a Subinterface of ResultSet, but the data is already cached. This is far simpler than to write all the data into an ArrayList.
CachedRowSets can also be queried themselves.
CachedRowSet rs;
.......................
.......................
Integer id;
String name;
while (rs.next())
{
if (rs.getInt("id") == 13)
{
id = rs.getInt("id");
name = rs.getString("name"));
}
}
So you just call the CachedRowSet whenever you need the info. It's almost as good as sliced bread. :)
EDIT:
There are no set methods for ResultSet, while there are Update methods. The problem with using the Update method's for the purpose of rebuilding a ResultSet is that it requires selecting a Row to update. Once the ResultSet has freed itself, all rows are set to null. A null reference cannot be called. A List of Lists mimics a ResultSet itself, or more correctly, an array of arrays mimic a ResultSet.
While Vectors are thread safe, there is a huge overhead attached to them. Use the ArrayList instead. As each nested List is created and placed into the outer nest List, insert it in this manner.
nest.add(Collections.unmodifiableList(nested));
After all of the nested Lists are inserted, return the nest List as an umodifiableList as well. This will give you a thread-safe collection without the overhead of the vectors.
Take a look at this page. Try to see if the SimpleResultSet class is fine for your needs.
If you combine its source into a standalone set of classes, it should do the trick.
From what I could get, your code may be like this:
List collection = new ArrayList();
collection.add(" A collection in some order");
List cache = new ArrayList();
cache.add(collection); ...
Now when you retrieve I think you'll get your collection in order, since you have used List.
If this is not what you were expecting, do comment.
I will advise you to use CachedRowSet. Refer http://www.onjava.com/pub/a/onjava/2004/06/23/cachedrowset.html this article to know more about CachedRowSet. Once you create this CachedRowSet, you can disconnect from the database, make some changes to the cached data and letter can even open the DB connection and commit the changes back to the Database.
Another option you should consider is just to refactor your code to accept a Collection instead of a ResultSet.
I'm assuming you pass that ResultSet to a method that iterates over it. You might as well change the method to iterate over an ArrayList...

How to reuse a Criteria object with hibernate?

I'm trying to do query result pagination with hibernate and displaytag, and Hibernate DetachedCriteria objects are doing their best to stand in the way. Let me explain...
The easiest way to do pagination with displaytag seems to be implementing the PaginatedList interface that has, among others, the following methods:
/* Gets the total number of results. */
int getFullListSize();
/* Gets the current page of results. */
List getList();
/* Gets the page size. */
int getObjectsPerPage();
/* Gets the current page number. */
int getPageNumber();
/* Get the sorting column and direction */
String getSortCriterion();
SortOrderEnum getSortDirection();
I'm thinking of throwing my PaginatedList implementation a Criteria object and let it work along theese lines...
getFullListSize() {
criteria.setProjection(Projections.rowCount());
return ((Long) criteria.uniqueResult()).intValue();
}
getList() {
if (getSortDirection() == SortOrderEnum.ASCENDING) {
criteria.addOrder(Order.asc(getSortCriterion());
} else if (getSortDirection() == SortOrderEnum.DECENDING) {
criteria.addOrder(Order.desc(getSortCriterion());
}
return criteria.list((getPageNumber() - 1) * getObjectsPerPage(),
getObjectsPerPage());
}
But this doesn't work, because the addOrder() or the setProjection() calls modify the criteria object rendering it in-usable for the successive calls. I'm not entirely sure of the order of the calls, but the db throws an error on getFullListSize() trying to do a "select count(*) ... order by ..." which is obviously wrong.
I think I could fix this by creating an object of my own to keep track of query conditions and rebuilding the Criteria object for each call, but that feels like reinventing yet another wheel. Is there a smarter way, possibly copying the Criteria initially passed in and working on that copy?
Update:
It looks like getList is called first, and getFullListSize is called multiple times after, so, as soon as there's an ordering passed in, getFullListSize will fail. It would make sense to hit the db only once (in getList I'd say) and cache the results, with no need to copy/reset the Criteria object, but still...
Update (again):
Forget about that, once I've done the count I can't do the select, and vice versa. I really need two distinct Criteria objects.
Criteria.setProjection(null);
Criteria.setResultTransformer(Criteria.ROOT_ENTITY);
Will effectively "reset" the criteria between the rowCount projection and execution of the criteria itself.
I would make sure your Order hasn't been added before doing the rowCount, it'll slow things down. My implementation of PaginatedList ALWAYS runs a count query before looking for results, so ordering isn't an issue.
well, DetachedCriteria are Serializable, so you have built-in (if inelegant) deep clone support. You could serialize the initial criteria to a byte[] once on construction, then deserialize it each time you want to use it.
http://weblogs.asp.net/stefansedich/archive/2008/10/03/paging-with-nhibernate-using-a-custom-extension-method-to-make-it-easier.aspx
In that post I spotted a CriteriaTransformer.clone method.
That should copy the criteria object.
You can also set the projection on your getlist method.
Woops I didn't notice you were referring to java hibernate. Anyway, this http://forum.hibernate.org/viewtopic.php?t=939039
forum post should be able to answer your question.
Ugly as it may be I ended up using the serialization trick. I just serialize the DetachedCriteria object to a byte array on construction of the PaginatedList object and de-serialize it when needed. Ouch.
Another thing worth trying:
implement a generic DAO like the one suggested on hibernate's site and pass it to the PaginatedList object, along with a Restrictions object. The PaginatedList object would then do something like
Criteria.forClass(myDAO.getPersistentClass())
.add(myRestrictions)
.addOrder(<someOrder>)
and
Criteria.forClass(myDAO.getPersistentClass())
.add(myRestrictions)
.setProjection(Projections.rowCount());
Haven't tried that yet, but it should work.
There is a better and easy way to clone criteria, just simply:
ICriteria criteria = ...(your original criteria init here)...;
var criteriaClone = (ICriteria)criteria.Clone();
And getting back to Your problem. For pagination I've made a method, which gives me as a result:
1. Total rows count
2. Rows filtered by page & pageSize
In a single query to DB.
ICriteria criteria = ...(your original criteria init here)...;
var countCrit = (ICriteria)criteria.Clone();
countCrit.ClearOrders(); // avoid missing group by exceptions
var rowCount = countCrit
.SetProjection(Projections.RowCount()).FutureValue<Int32>();
var results = criteria
.SetFirstResult(pageIndex * pageSize)
.SetMaxResults(pageSize)
.Future<T>();
var resultsArray = results.GetEnumerable();
var totalCount = rowCount.Value;
public static DetachedCriteria Clone(this DetachedCriteria criteria)
{
var dummy = criteria.ToByteArray();
return dummy.FromByteArray<DetachedCriteria>();
}

Categories

Resources