Hibernate ordering - java

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.

Related

Hibernate - sqlQuery map redundant records while using JOIN on OneToMany

I have #OneToMany association between 2 entities (Entity1 To Entity2).
My sqlQueryString consists of next steps:
select ent1.*, ent2.differ_field from Entity1 as ent1 left outer join Entity2 as ent2 on ent1.item_id = ent2.item_id
Adding some subqueries and writing results to some_field2, some_field3 etc.
Execute:
Query sqlQuery = getCurrentSession().createSQLQuery(sqlQueryString)
.setResultTransformer(Transformers.aliasToBean(SomeDto.class));
List list = sqlQuery.list();
and
class SomeDto {
item_id;
some_filed1;
...
differ_field;
...
}
So the result is the List<SomeDto>
Fields which are highlighted with grey are the same.
So what I want is to group by, for example, item_id and
the List<Object> differFieldList would be as aggregation result.
class SomeDto {
...fields...
List<Object> differFieldList;
}
or something like that Map<SomeDto, List<Object>>
I can map it manually but there is a trouble:
When I use sqlQuery.setFirstResult(offset).setMaxResults(limit)
I retrieve limit count of records. But there are redundant rows. After merge I have less count actually.
Thanks in advance!
If you would like to store the query results in a collection of this class:
class SomeDto {
...fields...
List<Object> differFieldList;
}
When using sqlQuery.setFirstResult(offset).setMaxResults(n), the number of records being limited is based on the joined result set. After merging the number of records could be less than expected, and the data in List could also be incomplete.
To get the expected data set, the query needs to be broken down into two.
In first query you simply select data from Entity1
select * from Entity1
Query.setFirstResult(offset).setMaxResults(n) can be used here to limit the records you want to return. If fields from Entity2 needs to be used as condition in this query, you may use exists subquery to join to Entity2 and filter by Entity2 fields.
Once data is returned from the query, you can extract item_id and put them into a collection, and use the collection to query Entity 2:
select item_id, differ_field from Entity2 where item_id in (:itemid)
Query.setParameterList() can be used to set the item id collection returned from first query to the second query. Then you will need to manually map data returned from query 2 to data returned from query 1.
This seems verbose. If JPA #OneToMany mapping is configured between the 2 entity objects, and your query can be written in HQL (you said not possible in comment), you may let Hibernate lazy load Entity2 collection for you automatically, in which case the code can be much cleaner, but behind the scenes Hibernate may generate more query requests to DB while lazy loading the entity sitting at Many side.
The duplicated records are natural from a relational database perspective. To group projection according to Object Oriented principles, you can use a utility like this one:
public void visit(T object, EntityContext entityContext) {
Class<T> clazz = (Class<T>) object.getClass();
ClassId<T> objectClassId = new ClassId<T>(clazz, object.getId());
boolean objectVisited = entityContext.isVisited(objectClassId);
if (!objectVisited) {
entityContext.visit(objectClassId, object);
}
P parent = getParent(object);
if (parent != null) {
Class<P> parentClass = (Class<P>) parent.getClass();
ClassId<P> parentClassId = new ClassId<P>(parentClass, parent.getId());
if (!entityContext.isVisited(parentClassId)) {
setChildren(parent);
}
List<T> children = getChildren(parent);
if (!objectVisited) {
children.add(object);
}
}
}
The code is available on GitHub.

Select multiple items based on multiple IDs with hibernate

I am using hibernate in eclipse.
And i have 3 ONE TO MANY relation tables.
ONE [Company] --> MANY [Officer], And ONE [Officer] --> MANY [Task].
They all have unique id (companyId, officerId, taskId).
Currently i know how to find all the tasks belong to an officer and i also know how to find all the officers belong to a company.
here is the code snippet:
public static ArrayList<Officer> getOfficersByCompany(Company company){
ArrayList<Officer> officers = new ArrayList<Officer>();
DetachedCriteria detachedCriteria = DetachedCriteria.forClass(Officer.class);
detachedCriteria.add(Restrictions.eq(Key.COMPANY, company));
detachedCriteria.add(Restrictions.eq(Key.OBJSTATUS, Value.ACTIVED));
List<Object> list = HibernateUtil.detachedCriteriaReturnList(detachedCriteria);
for(Object o : list){
officers.add((Officer) o);
}
return officers;
}
And below is the detachedCriteriaReturnList method in HibernateUtil class.
public static List<Object> detachedCriteriaReturnList(DetachedCriteria dc){
Session session = getSessionFactory().openSession();
session.beginTransaction();
Criteria criteria = dc.getExecutableCriteria(session);
List<Object> list = criteria.list();
session.getTransaction().commit();
session.close();
return list;
}
However, if i try to get all the task belongs to a company, how should i implement the code. I have tried using: detachedCriteria.add(Restrictions.allEq(officers));
public static ArrayList<Task> getTasksByOfficers(Map<String, Object> officers){
ArrayList<Task> tasks = new ArrayList<Task>();
DetachedCriteria detachedCriteria = DetachedCriteria.forClass(Task.class);
detachedCriteria.add(Restrictions.allEq(officers));
List<Object> list = HibernateUtil.detachedCriteriaReturnList(detachedCriteria);
for(Object o : list){
tasks.add((Task) o);
}
return tasks;
}
But i realise that the map only stores unique key and value pair, if i try to use the second officer's id the first one will be replaced.
Or is there any other ways to perform the selection faster and more efficient?
ONE [Company] --> MANY [Officer], And ONE [Officer] --> MANY [Task].
In HQL this should be pretty straight forward (I gave up using Criteria API, as it did not allow joining the same object class twice):
Obviously, I did not test the queries, but they should work...
select Task
from Task join Task.Officer o join o.Company c
where c.name = 'xxx'
or
select t
from Company c join c.Officers o join o.Tasks t
where c.name = 'xxx'
Note: My experience with criteria is limited, as I have said. But looking at your code, I have two comments. Maybe you should not use the Map of officers but the list officers.values() to allEq(). Second remark: If you didn't do a mistake, Hibernate obviously magically finds the reference to the property you want to be allEq with, otherwise, the allEq misses a property you want to compare to.
UPDATE: Ok, as promised, I checked the Javadocs for you: https://docs.jboss.org/hibernate/orm/3.5/api/org/hibernate/Criteria.html
A join in HQL is the equivalent of a criteria added to another criteria or an alias:
detachedCriteria = DetachedCriteria.forClass(Task.class)
.createCriteria("Officer") // Officer property of task
.createCriteria("Company") // Company property of officer
.add(Restriction.eq(Key.COMPANY, compKey); // the company
List<Object> list = detachedCriteria
.getExecutableCriteria(hibernateSession)
.list();
This should get you going...
Note on HQL:
To run the query, you get a query from the hibernate session with the HQL and then you call list() or executeUpdate()on it. taskHqlString is a HQL-statement as above. Replace the companyKey in the query with a named parameter :companyKey:
String taskHqlString = "select Task "
+ " from Task join Task.Officer o "
+ " join o.Company c "
+ " where c.name = :companyKey";
List<Task> list = (List<Task>)hibernateSession
.createQuery(taskHqlString)
.setParameter("companyKey", companyKeyValue)
.list();

Hibernate result list cast to get rows

Hello I had problem with iterate Hibernate ResultList
I had followed query that I got from external class:
queryContent = "select distinct c.identity, c.number, c.status, ctr.name, aab.paymentConditions.currency from AgreementStateBean ast join ast.currentAgreement aab join conagr.contract c where c.agreementStateId = ? and con.mainContractor = true ? "
And I must sum whole aab.paymentConditions.currency, check numbers of statutes and names.
I want to do this by iterate list of results:
Query q = session.createQuery(queryContent);
List result = q.list();
Long wholeCurrency, numberOfStatutes;
for(Object res : result){
//wholeCurrency += res.getColumnName?
}
My question is how to cast res Object to have possibility to get concrete column values? I had read about create map inside hibernate query but I don't know it is good practice to modyfied query string by adding
"new map(" prefix and then ")"
before from sql statement
Solution:
After All I decided to use map in my query. I modified my external query by adding hibernate map statement by replacing select by 'select new map(' and from by ') from'.
Additional thing is to add 'as' statement with name of key because without them column keys are integer.
So after all my query looks like follow:
"select new map( distinct c.identity, c.number, c.status as status, ctr.name as name, aab.paymentConditions.currency as currency ) from AgreementStateBean ast join ast.currentAgreement aab join conagr.contract c where c.agreementStateId = ? and con.mainContractor = true ? "
That was the most siutable solution for me, I tried with 'NEW com.example.MyClass' as Kostja suggested but in my case I didn't have control for incoming query so I can not rely on pernament constructor.
new List( select...
Is also interest but it also didn't tell me on with position I have my field that give me information.
If I understand correctly, you want to have a typed representation of your result without it being an entity itself. For this, you can use constructor queries:
"SELECT NEW com.example.MyClass( e.name, e.data) FROM Entity e"
MyClass has to have a matching constructor. Full qualification (com.example) is not mandatory AFAIK.
If you are using this query often, creating a view in the DB may be a good idea. You can map a view to an entity just as if it were a regular table, but please note that you cannot store changes to you data over a mapped view.
EDIT: Turns out, mapping to an unspecified Map is alright with Hibernate:
select new map( mother as mother, offspr as offspr, mate as mate )
As per http://docs.jboss.org/hibernate/orm/4.2/devguide/en-US/html/ch11.html#ql-select-clause
you can use
queryContent = "select new list(distinct c.identity, c.number, c.status, ctr.name, aab.paymentConditions.currency) from AgreementStateBean ast join ast.currentAgreement aab join conagr.contract c where c.agreementStateId = ? and con.mainContractor = true ? "
And you get List<List> as result.

JPA CriteriaBuilder - sort by the number of associated entities in a one-to-many relationship

I have two entities Customer and Order in a one-to-many relation.
For each customer I need to count the number of associated orders and sort the results by this number.
In a native postgres query it looks like this:
select cust.id, count(order.id) from customers cust
left outer join orders order
on cust.id = order.customer_id
where .... conditions ...
group by cust.id
order by count desc;
But I must do this using CriteriaBuilder because this query is part of a larger piece of code that uses CriteriaBuilder to put in additional conditions. In Hibernate I would have probably used Projections, but I can't find anything similar in JPA.
Any help in composing the query using CriteraBuilder would be much appreciated.
Thank you in advance.
Supposing that the entity Customer has a OneToMany property like this:
#OneToMany(mappedBy = "customerId")
private Collection<Order> orders;
You can use the following query:
EntityManager em; // to be built or injected
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Tuple> cq = cb.createTupleQuery();
Root<Customer> customer = cq.from(Customer.class);
CollectionJoin<Customer, Order> orders = customer.join(Customer_.orders, JoinType.LEFT);
cq.select(cb.tuple(customer, cb.count(orders)));
cq.where(... add some predicates here ...);
cq.groupBy(customer.get(Customer_.id));
cq.orderBy(cb.desc(cb.count(orders)));
List<Tuple> result = em.createQuery(cq).getResultList();
for (Tuple t : result) {
Customer c = (Customer) t.get(0);
Long cnt = (Long) t.get(1);
System.out.println("Customer " + c.getName() + " has " + cnt + " orders");
}
The above approach uses Metamodel. If you don't like it, you can replace Customer_.orders with "orders" and Customer_.id with "id".
If the OneToMany property is of another type, replace CollectionJoin with the collection of the proper type (ListJoin, SetJoin, MapJoin).
Use this inside the specification
cq.orderBy(cb.desc(cb.count(orders)));
Also send PageRequest(1, 10, Sort.unsorted()). This is how I did it.
If you are passing the Sort value as unsorted and then override criteria query with your own logic of sorting on your joined entity

What is the best optimized way to select many entities with subentities in JPA?

Let's say we have:
#Entity public class Order {
#Id private int id;
#OneToMany(mappedBy="order") private List<Item> items;
...
}
and
#Entity public class Item {
#Id private int id;
#ManyToOne private Order order;
...
}
And let's say there is 10.000 orders with each having 20 items.
We need to iterate thought all order and all their items.
What is the best way to do it in JPA?
My issue is, if I just iterate the elements like:
for (Order order: em.createTypeQuery("select o from Order o", Order.class).getResultList()) {
report.printOrder(order);
for (Item item: order.getItems()) {
report.printOrderItem(item);
}
}
it will result in 10.001 sql queries:
1 time: select * from order
10.000 times: select * from item where order_id = ?
Is there any way to optimize it? Two queries? One query?
(We are using EclipseLink)
Thanks.
You may also want to consider the EclipseLink query Hint "eclipselink.batch" with value "o.items". This results in two queries but can be more effecient than once large joined query.
You may use join fetch (this also requires distinct since join fetch have a join semantics):
select distinct o from Order o join fetch o.items

Categories

Resources