JPA2 select list from entity - java

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).

Related

How to fix JPA Specification API with join on condition returns 2 query instead of 1?

I have a requirement to form a query using JPA as;
select PARENT.ID, PARENT.NAME, CHILD.ID, CHILD.STATUS FROM PARENT
LEFT OUTER JOIN CHILD ON PARENT.ID=CHILD.ID AND CHILD.STATUS in ('Active')
WHERE PARENT.ID=?
Where child table is OneToMany relation to its parent.
Using JPA Spec API I have added below Predicate for Join
public static Specification<PARENT> hasChildrenWithStatus(List<String> status) {
return (root, criteriaQuery, criteriaBuilder)
-> {
Join<Object, Object> join = root.join(PARENT_.ChildList, JoinType.LEFT);
join.on(join.get(CHILDREN_.STATUS).in(status));
return criteriaBuilder.conjunction();
};
}
and added other predicates for PARENTs, but my query executed as 2 different queries
Hibernate: select PARENT.id as id1_0_, PARENT.name as namev2_0_
from PARENT PARENT
left outer join CHILD CHILD on (PARENT.id=CHILD.id) and (CHILD.status in('Active'))
where PARENT.id=? and 1=1
Hibernate: select CHILD.id as id5_2_0_, CHILD.status as stat4_2_1_ from CHILD CHILD where CHILD.id=?
Here in the second query fetches all records with child.id(without status check), thus giving me records that are in "deleted" status as well.
How can I fix this? Also, Can this be executed as single query as my requirement?
You've got separate issues here. To fetch parent instances with its children in a single query, you need to look at fetch joins which are different from the 'join' semantics you've used - see https://docs.oracle.com/javaee/6/api/javax/persistence/criteria/FetchParent.html. Calling root.fetch(PARENT_.ChildList, JoinType.LEFT) will cause the Parent and children to be fetched in the same SQL statement
When you look at fetch joins though, you'll see that they do not support query filtering. When JPA returns managed entities, because it must manage changes to those instances, they must reflect exactly what is in the database. If you get a parent with only a partially populated list of children, it can't really know what to do when it saves the parent; should it modify that list or just ignore differences it finds from the database values. It is just not supported by the spec, and the query language doesn't allow it. You just can't fetch Parents and partial lists of the children on demand though JPA semantics.
What you can do though is fetch the children yourself. The equivalent of
"SELECT child, parent from Parent parent join parent.ChildList child where parent.id=:parentId and child.status in ('Active')"
This will give you a list of Object[} containing child and parent pairs, but only active child instances. The Parent instance will all be the single instance with id equal to the value you pass in as a query parameter. If you access its parent.ChildList though, you will find all its children - so don't. Mark it as lazy and don't touch it, and use the returned child instances in your operations.
Your query though is just asking for certain attributes from the tables, so I'm guessing you don't really need full Parent/Child instances. Try a JPQL data query instead:
#Query(value = "SELECT parent.id, parent.name, child.id, child.status from Parent parent join parent.ChildList child where parent.id=:parentId and child.status in ('Active')"
This will return a List<Object[]>, with the Object[] containing the data from each row, with no need to use Spring specifications

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.

Hibernate - HQL to fetch a collection from Unidirectional OneToMany relationship

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!

Categories