(JPA) Append condition in ON clause during Joins - jpa

I need the JPQL in this format:
SELECT *
FROM Person p
LEFT JOIN Address a ON p.id = a.id AND a.flat = 100;
But when i run the code the query being slightly modified henceforth the output of data changed..
SELECT * FROM Person p LEFT JOIN Address a ON p.id = a.id where a.flat = 100;

I suppose you want to format the sql output.
So for hibernate, you can set hibernate.format_sql=true to well format the sql.
If you are using spring boot, you can config it via spring.jpa.properties.hibernate.format_sql=true simply.

JPQL does not allow joining to arbitrary other root objects, so you can't do that in JPQL; only allowing you to join along relations. Individual vendors may offer a vendor extension to allow joining across arbitrary roots but you then lose portability.
To provide more than that you have to post the actual JPA entities, and you haven't.

Related

SQL Tables with no direct relation

I have a PostgreSQL DB and I need to build a query to retrieve the information from a table that has no direct link with the main one.
The client is linked to the client_identity_document through its UUID and client_identity_document to the identity_document through the uuid_identity_document. I know I have to make an inner join, but I just started with relational databases and I don't know exactly the syntax to join tables that don't have direct relation.
try this
select *
from client c
inner join client_identity_document cid on c.UUID = cid.UUID_Client
inner join identity_document id on cid.uuid_identity_document = id.UUID

Can I apply predicates to the same columns against multiple tables in a JOIN only once?

I want to join two tables together and add additional information from two other tables to the same columns in both queried tables. I've come up with the below code, which works, but I don't feel comfortable about having to add another JOIN clause for each table, as it would make the query substantially long if I wanted to join/add more things.
Is there a way to combine it, so that I can join additional tables only once (just use S and E aliases every time)?
SELECT
J.JobId,
J.StandardJobId,
S.JobName,
J.EngineerId,
E.EngineerName,
JF.JobId AS FollowUpJobId,
JF.StandardJobId AS FollowUpStandardJobId,
SF.JobName AS FollowUpJobName,
JF.EngineerId AS FollowUpEngineerId,
EF.EngineerName AS FollowUpEngineerName
FROM
Jobs J
INNER JOIN
Jobs JF
ON
J.FollowUpJobId = JF.JobId
INNER JOIN
StandardJobs S
ON
J.StandardJobId = S.StandardJobId
INNER JOIN
Engineers E
ON
E.EngineerId = J.EngineerId
INNER JOIN
StandardJobs SF
ON
SF.StandardJobId = JF.StandardJobId
INNER JOIN
Engineers EF
ON
EF.EngineerId = JF.EngineerId
One approach would be to use a Common Table Expression (CTE) - something like:
with cte as
(SELECT J.JobId,
J.StandardJobId,
S.JobName,
J.EngineerId,
E.EngineerName,
J.FollowUpJobId
FROM Jobs J
INNER JOIN StandardJobs S ON J.StandardJobId = S.StandardJobId
INNER JOIN Engineers E ON E.EngineerId = J.EngineerId)
SELECT O.*,
F.StandardJobId AS FollowUpStandardJobId,
F.JobName AS FollowUpJobName,
F.EngineerId AS FollowUpEngineerId,
F.EngineerName AS FollowUpEngineerName
FROM CTE AS O
JOIN CTE AS F ON O.FollowUpJobId = F.JobId
You can sort of do this with either a CTE (Common Table Expressions, the WITH clause) or a View:
;WITH Jobs_Extended As
(
SELECT j.*,
s.JobName,
E.EngineerName
FROM Jobs As j
JOIN StandardJobs As s ON s.StandardJobId = j.StandardJobId
JOIN Engineer As e ON e.EngineerId = j.EngineerId
)
SELECT
J.JobId,
J.StandardJobId,
J.JobName,
J.EngineerId,
J.EngineerName,
JF.JobId AS FollowUpJobId,
JF.StandardJobId AS FollowUpStandardJobId,
JF.JobName AS FollowUpJobName,
JF.EngineerId AS FollowUpEngineerId,
JF.EngineerName AS FollowUpEngineerName
FROM Jobs_Extended J
JOIN Jobs_Extended JF ON J.FollowUpJobId = JF.JobId
In this example the CTE Jobs_Extended becomes a defined alias for the relationship between the Jobs, Engineers and StandardJobs tables. Then once defined, you can use it multiple times in the query without having to redefine those interior relations.
You can do the same thing by change the WITH to a View, which will make the defined alias permannet in your database.
No, you cannot avoid JOINing related tables each time a separate reference is needed. The issue is that you are not working with the tables in a general sense but instead working with the specific rows of each table, even more specifically, just those rows that match the JOIN and WHERE conditions.
There is no way to specify the references to either StandardJobs or Engineers only once because you are needing to work with two rows from each table at the same time, at least in the given example.
However, depending on which direction you are wanting to go with "additional tables" (more references to Jobs or more lookups like StandardJobs and Engineers for the given 2 references of Jobs), the CTE construct shown by Mark is the probably the easiest / best way to abstract it. I posted this answer mainly to explain the issue at hand.

How to prevent OpenJPA 2.2.2 add extra cross join for multiple-root query

I have two tables that don't have a defined relation, but I'm still trying to join them together in a single query, using criteria builder API.
This query work as I want it to - synchronizing the rows from both tables:
Root<E_Application> root = q.from(E_Application.class);
Root<E_Searcher> root2 = q.from(E_Searcher.class);
q.where(cb.equal(root.get(E_Application_.packageName),
root2.get(E_Searcher_.packageName)));
q.select(cb.sum(cb.literal(1)));
the query that comes out is: select sum(1) from application t0 cross join application_searcher t1 where (t0.package_name = t1.package_name);
However, if I add another join:
Root<E_Application> root = q.from(E_Application.class);
Root<E_Searcher> root2 = q.from(E_Searcher.class);
Join<E_Application, E_AppState> j1 =
root.join(E_Application_.publicAppState);
q.where(cb.equal(root.get(E_Application_.packageName),
root2.get(E_Searcher_.packageName)));
q.select(cb.sum(cb.literal(1)));
I get an extra cross join, and end up with a Cartesian product: SELECT SUM(1) FROM application t0 CROSS JOIN application t1 CROSS JOIN application_search t3 INNER JOIN application_state t2 ON t1.PUBLICAPPSTATE_ID = t2.id WHERE (t0.package_name = t3.package_name)
Is there any way to prevent this (except for defining the proper relationship between Application and Searcher)? Is this a proper implementation of the spec? It's sort of weird that the resulting query effectively has a root that I didn't explicitly request, and don't have control over...
The database is postgres, if it matters.
P.S. The reason there is no relationship is because these two tables have their PKs the same, and you can't have a PK that is also an entity reference.

zf many to many relationship, how to find values stored in intermediary table without a second lookup

I have a many to many relationship between two tables which is represented with an intermediary table. I am using the ZF1 table relationships methodology to model the database in my application and this works fine. One thing I am struggling with is to pull data from the intermediary table when performing a many to many lookup. For exmaple:
productsTable
product_id,
product_name
customerTable
customer_id,
customer_name
salesTable
customer_id,
product_id,
date_of_sale
In this case where the sales table is the intermediary table and the many to many relationship is between customers and products. I add the referenceMap to the sales table model for products and customers and the dependent table "sales" to the product table model and the customer table model.
I can then successfully use the following code to get all the products for a given customer (or vice-versa).
$productTable = new productsTable();
$product = $productTable->find(1)->current();
$customers = $product->findManyToManyRowset('customerTable','salesTable');
But it does not include the "date_of_sale" value in the rowset returned. Is there a way of including the values from the intermediary table without doint a separate database lookup. Ican't see anything in the zf docs.
Any help would be cool.
I hope to eventually replace the zend_table with a datamapper implementation as it seems highly inefficient in terms of the number of db queries it executes which could be hugely reduced with slightly more complex SQL join queries rather than multiple simple selects but for now I'm stuck with this.
Thanks.
You can use JOIN queries in your code to make it in one call. From what I understand, you want to build this query
SELECT p.*, c.*, s.date_of_sale FROM sales AS s
INNER JOIN products AS p ON p.product_id = s.product_id
INNER JOIN customers AS c ON c.customer_id = s.customer_id
WHERE p.product_id = '1';
To achieve this, you can refer to Complex SQL query into Zend to see how I translate it to a Zend_Db Query or just use something like
$select = $this->select()->setIntegrityCheck(false);
$select->from(array('s'=>'sales'), array('date_of_sale'));
$select->join(array('p'=>'products'), 'p.product_id = s.product_id', '*');
$select->join(array('c'=>'customers'), 'c.customer_id = s.customer_id', '*');
$select->where('p.product_id = '.$productId);
Obviously, it would be better to quoteInto the where clause but I'm just too lazy.
Hope this helps !

SQL Server 2008: many to many tables with relational tables ordering field + grouping

To understand what I need, here is the table's I'm using diagram: http://pascalc.nougen.com/stuffs/diagram.png
I need to get a project's properties + all it's relation, all listed based on the corresponding relational tables' OrderNumber column.
Let's say I need "Project Z", I want to get:
Project's BaseUrl, ... where ID = #ID
All testsuites associated to that project, listed by ProjectsToTestSuites.OrderNumber
All testcases associated to the matching testsuites, listed by TestSuitesToTestCases.OrderNumber
All testaction associated to the matching testcases, listed by TestCasesToTestActions.OrderNumber
So far, all my attempts are returning back results with mixed ordering. A testcase is mixed inside a testsuite it doesn't belong to and alike.
I try to avoid using cursors (loop each relation in specific order required), tried the use of UNION but couldn't get it to work either.
I wouldn't have troubles with cursor but if a solution exists withuout the need to use it, I prefer of course.
Thanks
If you want a flat result you could start with something like this (untested)
select
p.Id,
p.BaseUrl,
ts.*,
tc.*,
ta.*,
pts.OrderNumber as SuitesOrder,
tstc.OrderNumber as CasesOrder,
tcta.OrderNumber as ActionsOrder
from
Projects p
join
ProjectToTestSuites pts
on pts.Projects_Id = p.Id
join
TestSuites ts
on ts.Id = pts.TestSuites_Id
join
TestSuitesToTestCases tstc
on tstc.TestSuites_Id = ts.Id
join
TestCases tc
on tc.Id = tstc.TestCases_Id
join
TestCasesToTestActions tcta
on tcta.TestCases_Id = tc.Id
join
TestActions ta
on ta.Id = tcta.TestActions_Id
where
p.Id = #Id
order by
pts.OrderNumber,
tstc.OrderNumber,
tcta.OrderNumber