Querying a root entity in entity inheritance in JPA - jpa

I am experimenting the joined table inheritance in JPA (EclipseLink 2.6.0 having JPA 2.1).
If I execute the following JPQL statement on the root entity Vehicle,
List<Vehicle> vehicleList = entityManager.createQuery("SELECT v FROM Vehicle v", Vehicle.class).getResultList();
The EclipseLink provider generates the following SQL statements.
SELECT DISTINCT vehicle_type FROM vehicle
SELECT t0.vehicle_id, t0.vehicle_type, t0.manufacturer, t1.vehicle_id, t1.no_of_passengers, t1.saddle_height FROM vehicle t0, BIKE t1 WHERE ((t1.vehicle_id = t0.vehicle_id) AND (t0.vehicle_type = ?))
bind => [Bike]
SELECT t0.vehicle_id, t0.vehicle_type, t0.manufacturer, t1.vehicle_id, t1.no_of_doors, t1.no_of_passengers FROM vehicle t0, CAR t1 WHERE ((t1.vehicle_id = t0.vehicle_id) AND (t0.vehicle_type = ?))
bind => [Car]
SELECT t0.vehicle_id, t0.vehicle_type, t0.manufacturer, t1.vehicle_id, t1.load_capacity, t1.no_of_containers FROM vehicle t0, TRUCK t1 WHERE ((t1.vehicle_id = t0.vehicle_id) AND (t0.vehicle_type = ?))
bind => [Truck]
If an ORDER BY clause is added to the given JPQL statement like the following,
List<Vehicle> vehicleList = entityManager.createQuery("SELECT v FROM Vehicle v ORDER BY v.vehicleId DESC", Vehicle.class).getResultList();
the EclipseLink provider however, generates the following single SQL statement with outer joins.
SELECT t0.vehicle_id,
t0.vehicle_type,
t0.manufacturer,
t1.vehicle_id,
t1.load_capacity,
t1.no_of_containers,
t2.vehicle_id,
t2.no_of_passengers,
t2.saddle_height,
t3.vehicle_id,
t3.no_of_doors,
t3.no_of_passengers
FROM vehicle t0
LEFT OUTER JOIN truck t1
ON ( t1.vehicle_id = t0.vehicle_id )
LEFT OUTER JOIN bike t2
ON ( t2.vehicle_id = t0.vehicle_id )
LEFT OUTER JOIN car t3
ON ( t3.vehicle_id = t0.vehicle_id )
ORDER BY t0.vehicle_id DESC
This is mentioned in the JPA Wiki Book.
The poorest performing queries will be those to the root or branch
classes. Avoiding queries and relationships to the root and branch
classes will help to alleviate this burden. If you must query the
root or branch classes there are two methods that JPA providers use,
one is to outer join all of the subclass tables, the second is to
first query the root table, then query only the required subclass
table directly. The first method has the advantage of only requiring
one query, the second has the advantage of avoiding outer joins which
typically have poor performance in databases.
When a particular JPA provider determines which method to apply to generate/produce SQL statement(s), when a root entity is queried -- why does the ORDER BY clause in this particular case make a difference?
I have not yet given it a try on other JPA providers.
I presumed the entity classes are unrelated but let me know, if you need the inheritance hierarchy of classes used in these examples.

Related

How to join an entity and a custom select using CriteriaBuilder

I need to convert a certain query to JPA using CriteriaBuilder. The query looks like this:
SELECT *
FROM ENTITY1 a
INNER JOIN (
SELECT *
FROM ENTITY2 b
where b.STARTDATE = '2019-10-31T00:00:00.000+0100'
) ON ENTITY2.ID = ENTITY1.ENTITY2_ID
Before you ask: for the sake of performance, I wish to avoid converting the query to:
SELECT *
FROM ENTITY1 a
INNER JOIN ENTITY2 b ON ENTITY2.ID = ENTITY1.ENTITY2_ID
where b.STARTDATE = '2019-10-31T00:00:00.000+0100'
Indeed, both ENTITY1 and ENTITY2 contain such a huge lot of rows, even with the right indexes, executing the latter version of the query takes an unacceptable amount of time.
Now, I'm at a loss how to implement the former version with JPA. Any hint would be appreciated!

JPA criteria query + self join on rowid

I'm trying to write JPA criteria query.
Select * from classA t1
inner join
(SELECT rowid
FROM classA
where conditions...
ORDER BY clause
)t2
on t1.rowid = t2.rowid
ORDER BY clause
where rownum <= 500
I'm having problems in joining the main criteria query with inner criteria query(with predicates)? .Is there a possibilty to do join on criteria queries(not on roots)?
Any help is much appreciated.
note:domain class already having composite PK- annoted with embeddedId.
CriteriaQuery joins can only be defined on explicitly defined relationships between entities. E.g. in your example for ClassA to join to itself there would need an explicit field such as this:
#ManyToOne
#JoinColumn(name = "linked_class_a")
private ClassA linkedClassA
Is there a possibilty to do join on criteria queries(not on roots)?
The simple answer is no - as you've alluded to, it's possible for a CriteriaQuery to define multiple roots but these end up as cartesian products (CROSS JOINs), which can be very inefficient.

JPA Query over a join table

I have 3 tables like:
A AB B
------------- ------------ ---------------
a1 a1,b1 b1
AB is a transition table between A and B
With this, my classes have no composition within these two classes to each other. But I want to know that , with a JPQL Query, if any records exist for my element from A table in AB table. Just number or a boolean value is what I need.
Because AB is a transition table, there is no model object for it and I want to know if I can do this with a #Query in my Repository object.
the AB table must be modeled in an entity to be queried in JPQL. So you must model this as
an own entity class or an association in your A and or your B entity.
I suggest to use Native query method intead of JPQL (JPA supports Native query too). Let us assume table A is Customer and table B is a Product and AB is a Sale. Here is the query for getting list of products which are ordered by a customer.
entityManager.createNativeQuery("SELECT PRODUCT_ID FROM
SALE WHERE CUSTOMER_ID = 'C_123'");
Actually, the answer to this situation is simpler than you might think. It's a simple matter of using the right tool for the right job. JPA was not designed for implementing complicated SQL queries, that's what SQL is for! So you need a way to get JPA to access a production-level SQL query;
em.createNativeQuery
So in your case what you want to do is access the AB table looking only for the id field. Once you have retrieved your query, take your id field and look up the Java object using the id field. It's a second search true, but trivial by SQL standards.
Let's assume you are looking for an A object based on the number of times a B object references it. Say you are wanting a semi-complicated (but typical) SQL query to group type A objects based on the number of B objects and in descending order. This would be a typical popularity query that you might want to implement as per project requirements.
Your native SQL query would be as such:
select a_id as id from AB group by a_id order by count(*) desc;
Now what you want to do is tell JPA to expect the id list to comeback in a form that that JPA can accept. You need to put together an extra JPA entity. One that will never be used in the normal fashion of JPA. But JPA needs a way to get the queried objects back to you. You would put together an entity for this search query as such;
#Entity
public class IdSearch {
#Id
#Column
Long id;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
}
Now you implement a little bit of code to bring the two technologies together;
#SuppressWarnings("unchecked")
public List<IdSearch> findMostPopularA() {
return em.createNativeQuery("select a_id as id from AB group by a_id
order by count(*) desc", IdSearch.class).getResultList();
}
There, that's all you have to do to get JPA to get your query completed successfully. To get at your A objects you would simply cross reference into your the A list using the traditional JPA approach, as such;
List<IdSearch> list = producer.getMostPopularA();
Iterator<IdSearch> it = list.iterator();
while ( it.hasNext() ) {
IdSearch a = it.next();
A object = em.find(A.class,a.getId());
// your in business!
Still, a little more refinement of the above can simplify things a bit further actually given the many many capabilities of the SQL design structure. A slightly more complicated SQL query will an even more direct JPA interface to your actual data;
#SuppressWarnings("unchecked")
public List<A> findMostPopularA() {
return em.createNativeQuery("select * from A, AB
where A.id = AB.a_id
group by a_id
order by count(*) desc", A.class).getResultList();
}
This removes the need for an interm IdSearch table!
List<A> list = producer.getMostPopularA();
Iterator<A> it = list.iterator();
while ( it.hasNext() ) {
A a = it.next();
// your in business!
What may not be clear tot the naked eye is the wonderfully simplified way JPA allows you to make use of complicated SQL structures inside the JPA interface. Imagine if you an SQL as follows;
SELECT array_agg(players), player_teams
FROM (
SELECT DISTINCT t1.t1player AS players, t1.player_teams
FROM (
SELECT
p.playerid AS t1id,
concat(p.playerid,':', p.playername, ' ') AS t1player,
array_agg(pl.teamid ORDER BY pl.teamid) AS player_teams
FROM player p
LEFT JOIN plays pl ON p.playerid = pl.playerid
GROUP BY p.playerid, p.playername
) t1
INNER JOIN (
SELECT
p.playerid AS t2id,
array_agg(pl.teamid ORDER BY pl.teamid) AS player_teams
FROM player p
LEFT JOIN plays pl ON p.playerid = pl.playerid
GROUP BY p.playerid, p.playername
) t2 ON t1.player_teams=t2.player_teams AND t1.t1id <> t2.t2id
) innerQuery
GROUP BY player_teams
The point is that with createNativeQuery interface, you can still retrieve precisely the data you are looking for and straight into the desired object for easy access by Java.
#SuppressWarnings("unchecked")
public List<A> findMostPopularA() {
return em.createNativeQuery("SELECT array_agg(players), player_teams
FROM (
SELECT DISTINCT t1.t1player AS players, t1.player_teams
FROM (
SELECT
p.playerid AS t1id,
concat(p.playerid,':', p.playername, ' ') AS t1player,
array_agg(pl.teamid ORDER BY pl.teamid) AS player_teams
FROM player p
LEFT JOIN plays pl ON p.playerid = pl.playerid
GROUP BY p.playerid, p.playername
) t1
INNER JOIN (
SELECT
p.playerid AS t2id,
array_agg(pl.teamid ORDER BY pl.teamid) AS player_teams
FROM player p
LEFT JOIN plays pl ON p.playerid = pl.playerid
GROUP BY p.playerid, p.playername
) t2 ON t1.player_teams=t2.player_teams AND t1.t1id <> t2.t2id
) innerQuery
GROUP BY player_teams
", A.class).getResultList();
}

In JPA 2.0 JPQL, when one returns a NEW object, how may one make use of FETCH JOINs?

A colleague of mine has the following (apparently invalid) JPQL query:
SELECT NEW com.foobar.jpa.DonationAllocationDTOEntity(a.id, a.campaign, a.campAppeal, a.campDivision, a.divisionFund)
FROM DonationAllocation a JOIN a.donation d JOIN a.allocationType t
JOIN FETCH a.campaign
WHERE d.id = :donationId
AND (t.code = 'Pledge' OR t.code = 'MatchingPledge')
It is worth noting (for later in this message) that DonationAllocation's relationship with a Campaign entity is many-to-one, and is marked as FetchType.LAZY. My colleague's intent with this query is to (among other things) ensure that a.campaign is "inflated" (eagerly fetched).
Hibernate (obviously just one JPA implementation of several), when faced with this query, says:
query specified join fetching, but the owner of the fetched association was not present in the select list
This makes sense, as the select list contains only NEW DonationAllocationDTOEntity(), and section 4.4.5.3 of the JPA 2.0 specification says:
The association referenced by the right side of the FETCH JOIN clause must be an association or element collection that is referenced from an entity or embeddable that is returned as a result of the query.
So since there is no "entity or embeddable that is returned as a result of the query" (it's a DTO constructed using the NEW operator), it follows that there is no possible association for a FETCH JOIN to reference, and hence this query is invalid.
How, given this limitation, should one construct a JPQL query in this case such that a.campaign--passed into the constructor expression--is fetched eagerly?
I would simply select the entity and its association, and llopover the results to invoke the DTO constructor explicitely. You would have the additional advantage of compile-time checks and refactorable code:
select a from DonationAllocation a
JOIN a.donation d
JOIN a.allocationType t
JOIN FETCH a.campaign
WHERE d.id = :donationId
AND (t.code = 'Pledge' OR t.code = 'MatchingPledge')
...
for (DonationAllocation a : list) {
result.add(new DonationAllocationDTOEntity(a.id,
a.campaign,
a.campAppeal,
a.campDivision,
a.divisionFund));
}
EDIT:
This query should also select what's needed, and avoid selecting the whole DonationAllocation entity:
select a.id, a.campaign, a.campAppeal, a.campDivision, a.divisionFund
from DonationAllocation a
JOIN a.donation d
JOIN a.allocationType t
WHERE d.id = :donationId
AND (t.code = 'Pledge' OR t.code = 'MatchingPledge')
and you might just add the DTO constructor in the query if you want:
select new com.foobar.jpa.DonationAllocationDTOEntity(a.id, a.campaign, a.campAppeal, a.campDivision, a.divisionFund)
from DonationAllocation a
JOIN a.donation d
JOIN a.allocationType t
WHERE d.id = :donationId
AND (t.code = 'Pledge' OR t.code = 'MatchingPledge')
The fact the a.campaign is in the select clause should be sufficient to tell Hibernate to load the entity. At least that's how it behaves in my tests.

JPA OUTER JOIN without relation

I need make OUTER JOIN of two entities in JPA (saying master, detail), but the problem that at the entity level there are no relations (and I don't want add it).
#Entity
class Master
{
#Column(name="altKey")
Integer altKey;
}
#Entity
class Detail
{
#Column(name="altKeyRef")
#Basic (optional = true)
Integer altKeyRef;
}
SELECT m, d FROM Master m OUTER JOIN ????? d.altKeyRef = m.altKey
My understanding of the spec (see 4.14 BNF) is that a [ LEFT [OUTER] | INNER ] JOIN mush be done along a path expression (either a single valued association field or a collection valued association field).