Hibernate - HQL to fetch a collection from Unidirectional OneToMany relationship - java

I have an Class with a unidirectional one to many relationship as follows:
public class Order {
#OneToMany(cascade = CascadeType.ALL)
#JoinTable(name="order_item", joinColumns={#JoinColumn(name="order_id")}, inverseJoinColumns={#JoinColumn(name="item_id")})
public Set<Item> getItems() {
return items;
}
}
Normally getting the contents of this order is straightforward:
List<Item> items = order.getItems();
But for whatever reason I might want to filter my results in some way and retrieve only part of a collection of items such as all items more than a certain price, below a certain stock etc in the fastest way possible (Not returning then all then filtering afterwards). To do this I would run a HQL query to retrieve the items for a particular order and add some more stuff into my where clause or onto my query object.
Intuitively I would want this behaviour (Which is completely wrong):
SELECT jointable.ITEM from order_item as jointable inner join jointable.order where order = :order
But of course this is wrong as HQL works in terms of mapped entities, so I cannot use the join table in the query. So what is the correct way to do this?
Edit:
I have found the answer to this question, I want the following Query:
Select o.items from Order o where o = ?
This allows me to fetch the collection of items for an order, without having to use a bidirectional relationship. I am now however confused on the second stage of this question, which is how to filter the results of this collection, the most simple example being:
Select o.items from Order o where o = ? order by o.items.somenumberfield asc
Which returns illegal attempt to dereference collection, so how would I filter my items?
Edit:
The ticket solution is actually correct, I misread the solution originally.

select item from Order order
inner join order.items item
where order = :order
and ...
HQL queries use entities and their associations. The fact that the association uses a join table or not is not important for HQL: you navigate through associations and Hibernate does the appropriate translation to SQL.

It sounds like you want to map the other side of this relationship i.e. make this bi-directional mapping.
Then you can use the order number in your HQL:
from Item as item where item.amount > :amount and item.order.id = :orderId
From the HQL documentation:
from Cat as cat where cat.mate.id = 69
The query is efficient and does not require a table join.
The fact that you are trying to do this query suggests that the relationship between a Line and its Order is important!

Related

Why we always persist the owning side of relationship in JPA ? why don't we persist the other side of relation

I am new to learning JPA. while going through the one-to-many relation in JPA, I have noticed one thing that they always persit the owning side (the side which contains the foreign key). For example if we have two entities (order and orderitems) and we have following code for unidirectional relation between order and order items.
#Entity
public class Order {
#OneToMany
#JoinColumn(name = “fk_order”)
private List<OrderItem> items = new ArrayList<OrderItem>();
…
}
Now when ever we have to persist order. we need to save order item to database like following
Order o = em.find(Order.class, 1L);
OrderItem i = new OrderItem();
o.getItems().add(i);
em.persist(i);
Now my questions is that should be it like we fetch the order from the database or creater new order and add the order items to its orderitmeslist and then save this order to database instead of saving orderitems like follwing
Order o = em.find(Order.class, 1L);
OrderItem i = new OrderItem();
o.getItems().add(i);
em.persist(o);
and it should automatically save the orderitem to orderitem table as these two have one-to-many relation.
Why we always need to persist the owning side of relation, like here we are always persisting the orderitems to database either we are using bidirectional or unidirectional approach, why we always need to save the owning side (orderitem) ? why don't we just add the order items to the ordertiermslist of order entity and just persist the other side of relation (order) ?
You are looking for cascading operations and jpa actually support this. U need CascadeType.PERSIST (or CascadeType.ALL if you need).
#OneToMany(cascade = CascadeType.PERSIST) // or CascadeType.ALL
#JoinColumn(name = “fk_order”)
private List<OrderItem> items = new ArrayList<OrderItem>();
After persisting your "Order" you will also save items
Here you can read more about cascade types: https://www.baeldung.com/jpa-cascade-types
Btw:
Avoid naming entities and columns with sql keywords like: order, from etc. It may make some conflicts during hibernate query generation (I lost a lot of time for bugs like that :D).
When you map a many-to-many association, you should use a Set instead of a List....:
In many-to-many relation you need to have additional association table.
Here is an exampel to visualize:
In this case both sides (customer and product) will have records sales from their collections (sales).
When one side has a list and will try to remove it will delete all of the records and then reinsert all that should be in db (cuz list may keep duplicates).
-- delete existing
delete from customer
where customer_id = ?
-- n inserts
insert into sales(col1, col2, ...)
values(val1, val2, ...)
In case when you will use set during removing item you will do something like:
delete from customer
where customer_id = ? and product_id = ?
Here u can read more: Here is a pretty good article: https://dzone.com/articles/why-set-is-better-than-list-in-manytomany

Is it possible to query both from entity and list object

I want to query from both the entity and a list object. Say I have an entity called "Customer", and I have a list of potential customers of object Potential.
Customer {
id, name, address, ...
}
Potential {
id, name, address, ...
}
In my repository I write the query as follows if I want to customize the query to get customers
#Query("SELECT c FROM Customer c WHERE c.status = :status")
List<Customer> findAllSpecialCustomers(String status)
But if I currently have a list object
List<Potential> potentials
And I want to include it in the Query above, how should I do it? Or is it even possible? The reason why I want to do this is because both entities represent different tables but i want to do sorting and pagination on the combined records of the two entities. Also, potentials is queried from graph database. While entity Customer is from mysql database.
Basically, I have a potentials list object that is queried from graph database. I want to union it with Customer entity from a mysql database through #Query and apply sorting and pagination to the combined records.
Use a native union query and instead of using Customer or Potential, create another POJO class to map query results.
I assume that there is some property in your Potential class that identifies a Customer. For argument's sake, let's assume that the id field in the two classes are the same, e.g., you want to pair up the Potential with id == 123 with the Customer with id == 123.
The simplest thing that I can think of is to map the list of Potentials to a list of Integers (or whatever type the id is), and then use that as a parameter to an "in" clause in your Customer query. For example,
#Query("SELECT c FROM Customer c WHERE c.id in :idList")
List<Customer> findCustomersById(List<int> idList)
and
findCustomersById(
potentials
.stream()
.map(Potential::getId)
.collect(Collectors.toList()
);
As far as "zipping" the two lists, i.e. pairing up the matches from the two lists, I'll leave that as an exercise for you :-)

JPA/Hibernate seems to convert a query with an in clause into multiple queries with = clauses

A solution we implemented in order to collect a large amount of heavy objects in reasonable time and without memory overflow (I'm speaking of objects with multiple fetchType.eager relations, to entities that themselves have eager fetched relations), was to first select ids of those objects, and then select the objects themselves based on these ids.
Coming the time to optimise our code, we noticed (using hibernate.show_sql=true) that the query we used to gather those objects (select a from A a where a.id in :ids) is translated by JPA/Hibernate into thousands of queries of the form select a from ...endless join list... where a.id = ?
The question is the following:
Why JPA/Hibernate transforms our initial query with an "in" clause into so many queries with "=" clauses. Isn't it inefficent ? If so, is there a way to prevent that ?
Here's how the query is called in our code:
Query q = this.getContext().createQuery("select a from A a where a.id in :ids");
q.setParameter("ids", idList);
return (List<A>) q.getResultList();
Hi you should use setParameterList instead of setParameter.
Also no need to use select in hibernate if you want to get the complete entity object.
Query q = this.getContext().createQuery("from A a where a.id in (:ids)");
q.setParameterList("ids", idList);
return (List<A>) q.getResultList();
If your idList is more that 1000 elements, you probably hit this Oracle-specific deoptimization https://hibernate.atlassian.net/browse/HHH-9299.
One way to solve this is to break idList into multiple chunks, execute a query for each and concatenate the results.
Though I still can't explain why a query is generated for each ID provided in the in clause (provided in my original question), I found a solution to this problem on this blog (https://thoughts-on-java.org/fetch-multiple-entities-id-hibernate/).
The solution consists in using Hibernate's session API as follows:
//get session object from entity manager
Session session = em.unwrap(Session.
MultiIdentifierLoadAccess<A> multiLoadAccess = session.byMultipleIds(A.class);
List<A> aObjects= multiLoadAccess.withBatchSize(1000).multiLoad(idList);
return aObjects;

How to speed up eager loading of foreign collections?

I have a database table mapped with ORMlite, it contains some data (18 columns) and it also contains ForeignCollectionField(eager = true).
Problem is when loading all data from this table ... ORMlite is creating query for every item instead using joins. Which is resulting in 67124 queries and taking forever to load all objects from this table.
This could be however done in right join query under few seconds? Why to generate thousands of queries instead?
How can I speed it up? Do I have to write raw query and then RawRowMapper , which makes using ORM pointless..
How to deal with loading eager collections in ormlite? Because queryForAll is not way..
Problem is when loading all data from this table ... ORMlite is creating query for every item instead using joins. Which is resulting in 67124 queries and taking forever to load all objects from this table.
It's ORM_Lite_ for a reason. Lot of people have asked for the join support on foreign collections but I've not gotten to it yet. It's not easy.
If you still want to use ORMLite then I'd recommend not using eager = true and doing 2 queries instead. One query for your main item and then another query using the DAO associated with the collection entity using IN. Something like:
qb = accountDao.queryBuilder();
qb.where()...;
List<Account> accounts = qb.query();
// build a list of account-ids
List<Long> accountIds = new ArrayList<>();
for (Account account : accounts) {
accountIds.add(account.getId());
}
// now use this list of ids to get your other entities
List<Order> orders = orderDao.queryBuilder().where().in("accountId", accountIds).query();
// now you have a list of orders for all of your ids
// you will need to associate each order with its account
Hope this helps.

JPA2 select list from entity

I have a JPA mapping with a oneToMany, now I want to select this collection with a CriteriaQuery, just like
query.select(Root_.collection).where(cb.equal(root.id, id))
List<ResultObject> results = em.createQuery(query).getResultList();
I can't imagine this isn't possible at all... Because from a repository it is possible too...
The use case for this is to put the result of several queries in a DTO.
Thnx!
The use of collections in select clause is not allowed and so this
query.select(Root_.collection) does not work in Criteria API.
You can do this, though not recommended approach (http://www.kumaranuj.com/2013/07/jpa-2-fetch-joins-and-whether-we-should.html):
root.fetch(Root_.collection);
query.select(root).distinct(true).where(cb.equal(root.get("id"), id));
Then from the above, depending on the number of distinct Root rows returned, you can now access the collection for oneToMany relationship in the root as below:
final Root result = em.createQuery(query).getSingleResult(); //Since we are getting results using primary key and distinct, we then use getSingleResult
final Collection rootCollection = result.getCollection();
Key points here are in the use of root.fetch(Root_.collection) and select(root).distinct(true).

Categories