Hibernate Criteria Projection - java

Well as the question title says, I am trying to make a projection criteria querying only couple of the table attributes.
So I have a Person Table/class and it has about 40 attributes. I want my criteria to get dynamical number of attributes, lets say 10, 11 or 12 (SQL terms select firstname, lastname from person) and I was doing it like this:
Transaction tx = session.beginTransaction();
Criteria crit = session.createCriteria(Person.class);
crit.setCacheable(true);
ProjectionList projList = Projections.projectionList();
projList.add(Projections.property("id"));
Criterias c = null;
for (int i = 0; i < checked.size(); i++) {
Attribute attr = checked.elementAt(i);
switch (attr) {
case LASTNAME:
projList.add(Projections.property("lastName"));
c = enumMap.get(attr);
if (c.isChanged()) {
String tmp = (String) c.getAnswer();
tmp = tmp.replace('*', '%');
crit.add(Restrictions.like("lastName", tmp));
crit.addOrder(Order.asc("lastName"));
}
case ...THE REST .....
}
crit.setProjection(projList);
retList = crit.list();
tx.commit();
return retList;
and it gives back that the retList elements are not from the Person.class:
INFO [AWT-EventQueue-0] (UserGroupManagerApp.java127) - [Ljava.lang.Object;#14b9b80
FATAL [AWT-EventQueue-0] (Login.java78) - java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to usergroupmanager.model.db.Person
java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to usergroupmanager.model.db.Person
Please help, for now I am listing all the 40+ attr, and it takes up querying time and I do not like it. I am looking also an alternative solution which will help me solve this. I read about ResultTransformer but havent found how to use it in my case.

You can use criteria.setResultTransformer()
There's some Transformers provided in Hibernate. If your Person has not any association use this:
criteria.setResultTransformer(Transformers.aliasToBean(Person.class));
But if Person has any association, consider use Seimos at http://github.com/moesio/seimos
A lot of code could be saved if you use it instead Criteria.

[Ljava.lang.Object; cannot be cast to
usergroupmanager.model.db.Person
Says in clean words Object[] cannot be cast to Person. When you do a projection, you will get the attributes you selected as an array of objects instead of a hydrated entity.
Your code is missing the declaration of retlist. I guess it's a raw List which you cast to a List<Person> somewhere. Just replace that with List<Object[]>.

If you use a projection in Hibernate you are not querying all the data Hibernate needs to create the objects. Thus Hibernate cannot create the objects.
Thus the query from a projection just returns an array of the SQL returned from the query ie it returns a s List and you access the fields as plain entries in that array.

Related

Unable to cast Object to Pojo class [duplicate]

I use JPA 1.0:
Query query;
query = em.createNamedQuery("getThresholdParameters");
query.setParameter(1, Integer.parseInt(circleId));
List<Object[]> resultList = new ArrayList();
resultList = query.getResultList();
Here I get result as List<Object[]>, thus I have to type convert all the parameters of the row to their respective types which is cumbersome.
In JPA 2.0 there is TypedQuery which return an entity object of type one specifies.
But as I am using JPA 1 I can't use it.
How to get result as Entity object of type I want??
EDIT:
QUERY
#Entity
#Table(name="GMA_THRESHOLD_PARAMETERS")
#NamedQuery(
name = "getThresholdParameters",
query = "select gmaTh.minNumberOc, gmaTh.minDurationOc, gmaTh.maxNumberIc, gmaTh.maxDurationIc, gmaTh.maxNumberCellId,"
+ "gmaTh.distinctBnumberRatio, gmaTh.minPercentDistinctBnumber from GmaThresholdParameter gmaTh "
+ "where gmaTh.id.circleId=?1 AND gmaTh.id.tspId=?2 AND gmaTh.id.flag=?3 "
)
Your query selects many fields. Such a query always returns a list of Object arrays. If you want a list containing instances of your GmaThresholdParameter entity, then the query should be
select gmaTh from GmaThresholdParameter gmaTh
where gmaTh.id.circleId=?1 AND gmaTh.id.tspId=?2 AND gmaTh.id.flag=?3
The code to get the list of entities would then be
List<GmaThresholdParameter> resultList = query.getResultList();
You'll get a type safety warning from the compiler, that you can ignore.
I can't respond to this as a comment so I'll just go ahead and make it an answer.
List<Object[]> resultList = new ArrayList(); // CREATE an empty ArrayList object
resultList = query.getResultList(); // getResultList ALSO returns its own ArrayList object
And since you assign the list that getResultList() returns to the same variable as you used for your own empty ArrayList, your application loses any connection to your own empty ArrayList and Java will collect it as garbage. Essentially you created it for absolutely no purpose.
what JB Nizet posted is enough.
List<GmaThresholdParameter> resultList = query.getResultList();
I have done something similar since I was using JPA 1 at that time:
final Collection<YourType> typedResult = new ArrayList<YourType>
for(final Object result : query.getResultList())
{
typedResult.add((YourType) result);
}
return typedResult;
List<GmaThresholdParamerter> result= query.getResultList();
for( GmaThresholdParamerter res : result)
{
System.out.println("" +res.getMinNumberOc());
System.out.println("" +res.getMinDurationOc());
}

How to add Distinct in Hibernate Criteria

In my database I have a Test table, with columns: testName, testType
there are 2 different tests with the same type I.e "SUN", so I want only one of them for which I use Distinct in my hibernate / criteria as below, but it still giving me both the types with the same name as "sun".
Criteria crit = session.createCriteria(Test.class);
final ResultTransformer trans = new DistinctRootEntityResultTransformer();
crit.setResultTransformer(trans);
List rsList = trans.transformList(crit.list());
Any idea what could be the reason, or any other way of filtering duplicates.
Use Projections.distinct.
Criteria crit = session.createCriteria(Test.class).setProjection(
Projections.distinct(Projections.projectionList()
.add(Projections.property("type"), "type") )
.setResultTransformer(Transformers.aliasToBean(YourBean.class));
List lst = crit.list();
where YourBean.class has a property "type". The returned list will be List<YourBean>.
Try to use :
cr.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);
It work perfectly for me
I finally have found out to get values of other columns:
Criteria criteria = session.createCriteria(Test.class);
ProjectionList projectionList = Projections.projectionList();
ProjectionList projectionList2 = Projections.projectionList();
projectionList2.add(Projections.distinct(projectionList.add(Projections.property("distinctColumn"), "distinctColumn")));
projectionList2.add(Projections.property("col1"), "col1");
projectionList2.add(Projections.property("col2"), "col2");
criteria.setProjection(projectionList2);
criteria.setResultTransformer(Transformers.aliasToBean(Test.class));
List list = criteria.list();
Try to use :
Criteria criteria =
session.createCriteria(Test.class).setProjection(
Projections.distinct(Projections.property("testType")));
List<Test> rsList = criteria.list();
Had the same problem and ended up solving using the Group By projection and then adding in all the columns I needed. For example
Criteria query = session.createCriteria(Class.class)
.setProjection(Projections.projectionList()
.add(Projections.groupProperty("Col1"), "Col1")
.add(Projections.groupProperty("Col2"), "Col2"))
.setResultTransformer(Transformers.aliasToBean(Class.class));
List list = query.list();
Try
setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY)
Criteria crit = session.createCriteria(Test.class);
List list = crit.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY).list();
Projections provide the result of the marked properties only. but, it creates problem for the child entities. See my post for the real problem I faced it.
Hibernate: Parent and Child relationship data structure
Try using:
criteria.setResultTransformer(DistinctRootEntityResultTransformer.INSTANCE);
It uses default hashcodes to find matches in results.
Thanks.

How to run an aggregate function like SUM on two columns in JPA and display their results?

I am new to JPA. So my question should be so simple to some.
Below is the Simple Query in SQL which i would like to convert to JPA. I already have an entity class called TimeEnt.
SELECT
SUM(TimeEntryActualHours) as UnBilledHrs,
SUM (TimeEntryAmount) as UnbilledAmount
FROM TimeEnt WHERE MatterID = 200
The JPA Query Language does support aggregates functions in the SELECT clause like AVG, COUNT, MAX, MIN, SUM and does support multiple select_expressions in the SELECT clause, in which case the result is a List of Object array (Object[]). From the JPA specification:
4.8.1 Result Type of the SELECT Clause
...
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.
In other words, the kind of query you mentioned in a comment (and since you didn't provide your entity, I'll base my answer on your example) is supported, no problem. Here is a code sample:
String qlString = "SELECT AVG(x.price), SUM(x.stocks) FROM Magazine x WHERE ...";
Query q = em.createQuery(qlString);
Object[] results = (Object[]) q.getSingleResult();
for (Object object : results) {
System.out.println(object);
}
References
JPA 1.0 Specification
4.8.1 Result Type of the SELECT Clause
4.8.4 Aggregate Functions in the SELECT Clause
Lets think we have entity called Product:
final Query sumQuery = entityManager
.createQuery("SELECT SUM(p.price), SUM(p.sale) FROM Product p WHERE p.item=:ITEM AND ....");
sumQuery.setParameter("ITEM","t1");
final Object result= sumQuery.getSingleResult(); // Return an array Object with 2 elements, 1st is sum(price) and 2nd is sum(sale).
//If you have multiple rows;
final Query sumQuery = entityManager
.createQuery("SELECT SUM(p.price), SUM(p.sale) FROM Product p WHERE p.item in (" + itemlist
+ ") AND ....");
// Return a list of arrays, where each array correspond to 1 item (row) in resultset.
final List<IEniqDBEntity> sumEntityList = sumQuery.getResultList();
Take a look at the EJB Query Language specification.
The idiom is very similiar to standard SQL
EntityManager em = ...
Query q = em.createQuery ("SELECT AVG(x.price) FROM Magazine x");
Number result = (Number) q.getSingleResult ();
Regards,

Hibernate: how do I retrieve my entities from a ScrollableResults?

I do a query that returns a list of entities. How can I retrieve the entities from a ScrollableResults:
Session s = ....;
Query q = s.createQuery("....") # returns 100000s rows
ScrollableResults sr = q.scroll();
sr.scroll(45999); # just a number
Employee employee = ???
How do I get an employee in the last line of code
try the get(0) method, or get()[0]
Here's a link to API: ScrollableResults
get() returns the entire current row, get(index) returns object at index position without initializing the rest of them. There are also a bunch of convenience getXXX() methods that cast result to given type.
I do a query that returns a list of entities. How can I retrieve the entities from a ScrollableResults... How do I get an employee.
Just to improve the other answers, the ScrollableResults does the entity conversion for you although this isn't immediately clear from the Javadocs.
As #Bozho says, calling sr.get() will return the entity at the current location, but wrapped in an array. In looking at the code for ScrollableResultsImpl the current row's result is set with:
if ( result != null && result.getClass().isArray() ) {
currentRow = (Object[]) result;
} else {
currentRow = new Object[] { result };
}
So ScrollableResults.get() always returns an array of results and if your entity is not an array, it will be at get()[0].
So, with your code you would do something like:
while (sr.next()) {
// get the entity which is the first element in an Object[]
Employee employee = sr.get()[0];
...
}
To retrieve entities the simplest way would be to cast the object to whichever object you want:
E.g:
ScrollableResults sr = q.scroll();
while (sr.next()) {
CustomObject object = (CustomObject) sr.get()[0]; // Now CustomObject will have all the properties mapped
}
This works perfect for all the scenarios.

Hibernate ordering

I have the Vehicles class and mapping file for it and i want to get all rows from vehicles table ordered by ID desc (I also need the same for my other tables).
I got the following code:
session = HibernateUtil.getSessionFactory().getCurrentSession();
tx = session.beginTransaction();
q = session.createQuery("from Vehicles order by ID DESC");
for (Iterator it=q.iterate(); it.hasNext();){
//some logic
}
But my set isn't ordered by ID and each time it has a different order like RAND() or something. I was wondering what is the easiest way to keep the functionality and just to add order by clause because I have the same syntax on many places...
Try after "q = session.createQuery(...);" part:
List results = q.list()
//loop through results
There is probably something wrong elsewhere, because "sort by id desc" part is correct. Check your database/mapping files if you have correct data types and if indexes are set properly.
I'm assuming your vehicles class looks like this? I'm using JPA here because thats what I know...
class Vehicles {
#Id
#Column(name="vehicles_id")
private int id;
// other stuff here
}
I don't expect your session.createQuery to be different from mine so wouldn't something like this work?
Query q = session.createQuery("select v from Vehicles v order by v.id desc");
Also you could use criteria if you wanted yeah?
class Main {
List<Vehicles> cars;
}
Criteria main = session.createCriteria(Main.class);
Criteria secondary = main.createCriteria("cars");
secondary.addOrder(Order.asc("id"));
Have you tried "from Vehicles v order by v.id desc"? Also another option is to add the comparable interface to the entity and then bring the list created by the query into a sortedset. That's typically what I do when I need to sort.

Categories

Resources