Collecting all child entities from multiple different entities - eclipselink

I have 3 entities A, B and C.
A contains a one-to-one linked B entity
A contains a one-to-many linked List of C entities
B contains a one-to-many linked List of C entities
Database-wise there are 5 tables. The 3 tables for the respective entities and 2 separate linking tables, one containing the links between A and C (A_to_C), and another one containing the links between B and C (B_to_C).
In my repository I'm trying to retrieve all C entities from a specific A record, meaning the C entities from A itself and the C entities which are linked through B.
In traditional SQL this could be done using something like:
select C.*
from A
left join A_to_C on A_to_C.A_ID = A.ID
left join B_to_C on B_to_C.B_ID = A.B_ID
inner join C on C.ID = A_to_C.C_ID OR C.ID = B_to_C.C_ID
where A.ID = '1';
or (starting from C)
select C.*
from C
left join A_to_C on A_to_C.C_ID = C.ID
left join B_to_C on B_to_C.C_ID = C.ID
inner join A on A.B_ID = B_to_C.B_ID OR
A.ID = A_to_C.A_ID
where A.ID = '1';
There's no link to the table for B in these SQL examples because A contains the ID for B and it's also the one used in B_to_C so I don't really need it. I also know these aren't strictly the same but they produce the same result when I'm only interested in C.
I'm really struggling with how to do this in either CriteriaBuilder (preferably) or JPQL though.
I'm still relatively new to jpa so I'm hoping someone here could help me with this.

I believe that, to even have a chance of succeeding with vanilla JPQL, you would need to convert the association to a bidirectional one (or a unidirectional many-to-one) by adding the #ManyToOne side. You could then start the query from C. The #OneToMany side, should you want to retain it, becomes the inverse side of the association in such a scenario, though:
#Entity
public class C {
#ManyToOne
#JoinTable
private B b;
#ManyToOne
#JoinTable
private A a;
}
#Entity
public class B {
#ManyToOne
#JoinTable
private A a;
#OneToMany(mappedBy = "b")
private List<C> cs;
}
#Entity
public class A {
#OneToMany(mappedBy = "a")
private List<B> bs;
#OneToMany(mappedBy = "a")
private List<C> cs;
}
Once you do that, the JPQL query becomes sth like:
SELECT c FROM C c
LEFT JOIN c.a a
LEFT JOIN c.b b
LEFT JOIN b.a a2
WHERE a.id = :id OR a2.id = :id
If you're not OK with making the 'one' side of the association the inverse side, then you're out of luck. The easiest solution is to use a native query.
Eclipselink JPQL Extensions include an ON clause, so perhaps you could combine ON with MEMBER OF:
SELECT c FROM C c
LEFT JOIN A a ON c MEMBER OF a.cs
LEFT JOIN B b ON c MEMBER OF b.cs
LEFT JOIN A a2 ON b MEMBER OF a2.cs
WHERE a.id = :id OR a2.id = :id
I highly doubt it will work, though, and even if it does, I'd be wary of the generated SQL as it might be suboptimal.

Related

Entity Framework with Multiple Joins and Subquery

I have a complex query with multiple left joins and subqueries which I need to implement in Entify Framework. I've received the monster SQL and
my goal is to do it on a elegant way with EF. The query consumes multiple tables and creates a "WITH" subquery on top which
is included in the joins later. I've done a first approach with EF but when I inspect the output that EF sends to the DB, inner joins are sent when
I am expecting LEFT JOINs.
A summary of the SQL follows:
WITH SUB_QUERY
AS ( SELECT FIELD_A,
FIELD_B,
FIELD_C,
MAX (FIELD_D) MAX_FIELD_D
FROM TABLE_X
WHERE SOME FIELD_A = 'WHATEVER'
GROUP BY FIELD_A, FIELD_B, FIELD_C)
SELECT C.FIELD_A,
C.FIELD_B,
B.FIELD_X,
D.FIELD_S,
E.FIELD_J,
F.FIELD_Y
FROM TABLE_A A
LEFT JOIN SUB_QUERY B
ON A.FIELD_C = B.FIELD_C
LEFT JOIN TABLE_C C
ON B.FIELD_A = C.FIELD_A
LEFT JOIN TABLE_D D
ON A.FIELD_C = D.FIELD_C
LEFT JOIN TABLE_E E
ON A.FIELD_X = E.FIELD_X
LEFT JOIN TABLE_F F
ON A.FIELD_W = F.FIELD_W
WHERE A.FIELD_H = D.FIELD_H
AND A.FIELD_D = B.MAX_FIELD_D
As you see, a subquery on top filters and groups some data to be consumed in a join below. Then all the joins take place
and some fields are taken from different tables as the output of the query.
Which approach would you recommend me to accomplish this task? I've tried different approaches and no one of them works (either retrieve nothing, or many more rows than the SQL query on the DB, etc..)
Please note that the Domain Model in Entity Framework is properly setup: Primary Keys, collections, nested objects etc.. so I believe some of these
joins are not even required because my EF entities contain already references to the child collections and parent objects (navigation properties).
Thanks a lot!!
if you really need a left join you should mode the where condition related to a left joined table in the proper on clause
FROM TABLE_A A
LEFT JOIN SUB_QUERY B
ON A.FIELD_C = B.FIELD_C
LEFT JOIN TABLE_C C
ON B.FIELD_A = C.FIELD_A
LEFT JOIN TABLE_D D
ON A.FIELD_C = D.FIELD_C AND A.FIELD_D = B.MAX_FIELD_D
LEFT JOIN TABLE_E E
ON A.FIELD_X = E.FIELD_X
LEFT JOIN TABLE_F F
ON A.FIELD_W = F.FIELD_W
the use of a left join table column in where force the relation to work as a INNER JOIN

What is the SQL command for a many to many relationship together with one to many?

The following is my sample:
How do List All items belonging to building X, with a category of Y?
I am unsure what to look for (is it joins? inner outer?). I think my difficulty in finding the answer via google is because I do not know how to properly phrase the question. Any help please.
How about exists?
select i.*
from items i
where exists (select 1
from building b
where b.id = i.building_id and b.name = 'X'
) and
exists (select 1
from category_has_item chi join
category c
on c.category_id = chi.id
where chi.item_id = i.id and c.category = 'Y'
);
In your case, you can also reasonably express this directly with joins:
select i.*
from items i join
buildings b
on i.building_id = b.id join
category_has_item chi
on chi.item_id = i.id join
category c
on chi.category_id = c.id
where b.name = 'X' and c.category = 'Y';
I think this is essentially the same in your case, assuming there are no duplicates in category_has_item. If both relationships were many-to-many, then the first approach would probably be faster.

Same ID field on multiple tables

So I've been given a task where i should present the following:
ID, LNAME, FNAME, MNAME, BIRTH_DATE, RELG_CODE, NAT_CODE, PT_STATUS, RM_NO, DTTM_ADM
The tables are:
HISR_CODES, PASR_NAMES, PASR_PROFILE, PAST_PATIENT_ADM
--Viewing them using DESC--
So while I viewed them, I was told that the ID on these tables are the same. So what I did so far in the coding (I'll finish the rest but I need to make sure this works first):
SELECT
A.ID,
A.LNAME,
A.FNAME,
A.MNAME,
A.BIRTH_DATE,
C.RELG_CODE,
C.NAT_CODE,
B.PT_STATUS,
B.RM_NO,
B.DTTM_ADM
FROM
PASR_NAMES A,
PASR_PROFILE B,
PAST_PATIENT_ADM C,
HISR_CODES D
WHERE
A.ID = B.ID
AND
B.ID = C.ID
AND
C.ID = D.ID
Is there a way to tell that all of the ID's from the tables are the same? A simpler code than going on like this:
WHERE
A.ID = B.ID
AND
B.ID = C.ID
AND
C.ID = D.ID
Or is JOIN - ON the only option for this?
You can use NATURAL JOIN as below:
SELECT
A.ID,
A.LNAME,
A.FNAME,
A.MNAME,
A.BIRTH_DATE,
C.RELG_CODE,
C.NAT_CODE,
B.PT_STATUS,
B.RM_NO,
B.DTTM_ADM
FROM
PASR_NAMES A
NATURAL JOIN PASR_PROFILE B
NATURAL JOIN PAST_PATIENT_ADM C
NATURAL JOIN HISR_CODES D;
From Oracle Reference, "A natural join is based on all columns in the two tables that have the same name." So, there is a chance that the joins happen based on other columns as well. Therefore, it is recommended that you still use the INNER JOIN syntax and explicitly specify the JOIN columns.
References:
NATURAL JOIN on Oracle® Database SQL Language Reference
Related SO question
Use the proper join syntax:
FROM PASR_NAMES A JOIN
PASR_PROFILE B
ON A.ID = B.ID JOIN
PAST_PATIENT_ADM C
ON B.ID = C.ID JOIn
HISR_CODES D
ON C.ID = D.ID
Or:
FROM PASR_NAMES A JOIN
PASR_PROFILE B
USING (ID) JOIN
PAST_PATIENT_ADM C
USING (ID)
HISR_CODES D
USING (ID)
I would discourage you from using the natural join. It might seem like the right thing at first. However, the semantics of the query are highly dependent on the structure of the tables. If columns are renamed, removed, or added, the query might still work but produce highly unexpected results.

SQL Server Need Some Help Joins , please

I have 3 tables A, B, C. There is a relationship between tables A and C while there is a relationship between tables B and C . There is no relationship between A and B.
What I would really like to do is get a list of all the records from B when there are records in C related to B given a value from A .
Please let me know, if this is not clear enough
Thanks
you can right query something like this...
SELECT B.* FROM B
INNER JOIN C ON C.aa = B.aa
INNER JOIN A ON A.bb = C.bb
WHERE A.cc = #yourvalue
#yourvalue is your value on which bases you need to select the value from B table. if you need match mutliple values from A then you need to change bit of query some thing like this...
WHERE A.cc IN (#val1,#val2,#val3....,#valNth)
In this query we have used INNER JOIN so it will gives only those records which are common on both the tables LIKE if you only join B with C then it will give the records which are common in B and C and then you join A with C then it will give those records which are common in A and C.
So suppose in B there is records something like 1,2,3 and in C there is 2,3,4,5 and in A there is 1,3,4,5
so the output of above query (without applying WHERE cause) is 1,3 only because this is common in all three table A,B,C.
you can got more information for joins in sqlserver by refering this links..
http://blog.sqlauthority.com/2009/04/13/sql-server-introduction-to-joins-basic-of-joins/
http://www.dotnet-tricks.com/Tutorial/sqlserver/W1aI140312-Different-Types-of-SQL-Joins.html
http://www.aspdotnet-suresh.com/2011/12/different-types-of-joins-in-sql-server.html
Simple math dictates if there is a relationship between A and C, and a relationship between B and C, there is, albeit by association, a relationship between A and B (through C).
Thus you will need to join all three together, going from A, through C, to B:
SELECT B.*
FROM A
JOIN C ON A.x = C.x
JOIN B ON B.y = C.y
WHERE A.z = #z

Path Expected for Join! Nhibernate Error

I trying to do a join and I keep getting this error
Path expected for join! [SELECT
t.CourseId FROM Task as t INNER JOIN
Courses as c, CoursePermissions as cp
WHERE (t.CourseId = 1)]
I have
const string query = "SELECT t.CourseId FROM Task as t INNER JOIN Courses as c, CoursePermissions as cp WHERE (t.CourseId = 1)";
var a = session.CreateQuery(query);
My Sql I am trying to achieve
SELECT dbo.Tasks.CourseId
FROM dbo.Tasks INNER JOIN
dbo.Courses ON dbo.Tasks.CourseId = dbo.Courses.CourseId INNER JOIN
dbo.CoursePermissions ON dbo.Courses.CourseId = dbo.CoursePermissions.CourseId
WHERE (dbo.Tasks.CourseId = 1)
I am using nhibernate 3.1 and fluent nhibernate 1.2
It means that you using an inner join in HQL works a little different than using it in SQL. In HQL you join the tables by providing the "path", which is basically the referenced property of your class.
So instead of
SELECT t.CourseId FROM Task as t INNER JOIN Courses as c ...
you need to write
// c.Taks is the IList property in your Courses class
SELECT t.CourseId FROM Courses as c INNER JOIN c.Tasks as t ...
Although Florian Lim has suggested a great solution but in my case I didn't had IList for Tasks in Courses. I achieved that through Theta-Style Joins which actually is Cartesian product provide all possible combinations which can be filtered (in where clause) according to need.