How to access a OnetoMany join table in JPQL? - jpa

I want to write a lttl complicated query in JPQL where i access a OneToMany Join table. I get QuerySyntaxException: Pan_PanRes is not mapped.
QUERY -
query = "SELECT p FROM Pan p WHERE p.id IN " +
"(SELECT p_id FROM Pan_PanRes p_prs WHERE prs_id IN " +
"(SELECT r.id FROM PRS r where r.pant = :pant))"+
" ORDER BY pr.clD"
i tried implementing this concept in MYSQL. It works fine. So i know i am not calling the join table in right way. How should it be called then?
I would like to add MYSQL statement which works fine -
mysql> select * from pan where id not in
(select pan_id from pan_panres where panres_id in
(select id from panres where pant_id = 3));
Thanks...

Solved it myself -
query = "SELECT p FROM Pan p WHERE p.id IN " +
"(SELECT p.id FROM p.panRes prs WHERE id IN " +
"(SELECT r.id FROM PanRes r where r.pant = :pant))"+
" ORDER BY pr.clD"
Where panRes is the oneToMany variable name i have used in Pan Class.

Related

Convert a Postgres query into JPQL

I have the following query in Postgres:
SELECT *
from table1
LEFT OUTER JOIN table1.table2 ON table1.latest_id = table2.id
WHERE table1.table2.status = 0
AND table1.id NOT IN (
(SELECT id from table3 where userId = table2.user))
I do not have the ability to join table1 and table3 and I am stuck writing the subquery in a format JPA will understand - working with a spring boot app. Here is where I got so far in my repository class:
#Query("SELECT c FROM #{#entityName} c JOIN FETCH c.table2 WHERE c.table2.status = 0")
fun findByIdAndStatus(id: String): MyEntity
I have attempted at the subquery as follows, but with no joy - there is a clear syntax error I cannot wrap my head around:
#Query("SELECT c FROM #{#entityName} c JOIN FETCH c.table2 WHERE c.table2.status = 0" AND c.id NOT IN (" +
"SELECT * FROM Table3 WHERE userId = c.table2.user")
Can you help?
Thank you
Assuming:
your SQL query doesn't utilize any input arguments
table1 is mapped by MyEntity and table3 is mapped by Table3 entity
entity associated with table2 is mapped in MyEntity using latest_id join column
status is mapped as a number (i.e Long / Integer)
You can rewrite the query to following JPQL form:
#Query("SELECT t1 FROM MyEntity t1 "
+ "JOIN t1.table2 t2 "
+ "WHERE t2.status = 0 AND t1.id NOT IN ("
+ " SELECT t3.id from Table3 t3 "
+ " WHERE t3.userId = t2.user "
+ ")")
List<MyEntity> findMyEntities();

MultiPOCO queries firing error message

I have a query as
return Connection.db.Fetch<Issue, Condition , Result , Status>(
"SELECT * FROM Issue I, Condition C, Result R, Status S " +
"LEFT JOIN Issue ON Condition .ID = Issue.ConditionID" +
"LEFT JOIN Issue ON Result.ID = Issue.ResultID" +
"LEFT JOIN Issue ON Status.ID = Issue.StatusID " +
"WHERE Issue.ID= "+ issueId);
which is giving the error message:
The multi-part identifier "Condition.ID" could not be bound.
The objects "Issue" and "Issue" in the FROM clause have the same exposed names. Use correlation names to distinguish them.
What is worng with the above?
You're joining to Issue table several times, but never use aliases. This is especially a problem because you've added the same table to FROM part of your SELECT clause. Always use aliases in joins. It's the safest way.
So whenever you said Issue.ID query engine could not resolve to which one you're referring. Is it to the one in FROM part or the one in JOIN part.
Try out this code:
return Connection.db.Fetch<Issue, Condition , Result , Status>(
"SELECT * FROM Issue I, Condition C, Result R, Status S " +
"LEFT JOIN Issue i1 ON C.ID = i1.ConditionID" +
"LEFT JOIN Issue i2 ON R.ID = i2.ResultID" +
"LEFT JOIN Issue i3 ON S.ID = i3.StatusID " +
"WHERE I.ID = " + issueId);
And apart from that I strongly suggest you use parameters instead of string concatenation:
return Connection.db.Fetch<Issue, Condition , Result , Status>(
"SELECT * FROM Issue I, Condition C, Result R, Status S " +
"LEFT JOIN Issue i1 ON C.ID = i1.ConditionID" +
"LEFT JOIN Issue i2 ON R.ID = i2.ResultID" +
"LEFT JOIN Issue i3 ON S.ID = i3.StatusID " +
"WHERE I.ID = #0", issueId); // see the change in this line
After some thought this is actually invalid SQL query
The problem that you're having is not PetaPoco related at all. You've actually written an invalid SQL query. If you'd run this same statement in SSMS you'd get the same error and that's because your query is interpreted as:
SELECT * FROM Issue I, Condition C, Result R,
(Status S
LEFT JOIN Issue i1
ON C.ID = i1.ConditionID
LEFT JOIN Issue i2
ON R.ID = i2.ResultID
LEFT JOIN Issue i3
ON S.ID = i3.StatusID
)
WHERE I.ID = x;
And that's the main reason why the first two joins fail. The last one is the only one that works, because it sees both tables. You probably thought that your select makes a result of those tables and then joins as in:
SELECT * FROM (Issue I, Condition C, Result R, Status S)
LEFT JOIN ...
But that is not the case.
Depending on what kind of results you want (you may end up with a large result set the way that you're doing it) you will have to rewrite your query to a different form and possibly omit the multi table list in your FROM part.

JPA query additional group by

How to write JPA query for below SQL ?
select * from opstatus o where o.OPERATIONTYPE=2 and o.RECEIVEDFLAG =2 and o.SENDTIME in (select max(o1.SENDTIME)from opstatus o1 where (o1.OPERATIONTYPE=2 and o1.RECEIVEDFLAG =2) group by o1.dn);
Trying to run the below query
result = em.createQuery("select o from DTO o where "
+ "o.operationType=:operationType"
+ " and o.receivedFlag = :receivedFlag"
+ " and o.startTime in (select max(o1.startTime)from DTO o1 where
o1.receivedFlag = :receivedFlag group by o1.Dn) order by o.startTime").
setParameter("operationType","2").
setParameter("receivedFlag", "2").getResultList();
However during runtime below query gets genereated which has additional 'group by T2.DN' for which we get ' org.apache.openjpa.persistence.PersistenceException: ORA-00979: not a GROUP BY expression'
SELECT t0.OPERATIONID, t0.CURRENTSTEP, t0.DETAILEDSTEPS,t0.DN, t0.OPERATIONTYPE, t0.RECEIVEDFLAG, t0.REQUESTID, t0.SENDTIME FROM OPSTATUS t0, OPSTATUS t2 WHERE (t0.OPERATIONTYPE = ? AND t0.RECEIVEDFLAG = ? AND t0.SENDTIME IN (SELECT MAX(t1.SENDTIME) FROM OPSTATUS t1 WHERE (t1.OPERATIONTYPE = ? AND t1.RECEIVEDFLAG = ?) GROUP BY t1.DN)) GROUP BY t2.DN [params=?, ?, ?, ?]
How to prevent additional 'group by' getting appended ? I tried adding 'order by o.sendtime' no use .
Did you means select the last entry in every Dn group. If two different Dn have the same startTime and only one of it is the last(max) your query will fail. Because the o1 in the sub query not linked to the outer o, the OpenJPA will generate FROM OPSTATUS t0, OPSTATUS t2
Maybe you can change your query.
"select o from DTO o where "
+ "o.operationType=:operationType"
+ " and o.receivedFlag = :receivedFlag"
+ " and not exists (select o1 from DTO o1 where
o1.receivedFlag = :receivedFlag and o1.Dn = o.Dn and o1.startTime > o.startTime) order by o.startTime"

Subquery in JPA

I am trying to write the following SQL query as a JPA query. The SQL query works (MySQL database) but I don't know how to translate it. I get a error token right after the first FROM. There are probably other errors here too because I was not able to find any guides on how to do sub-queries in the from part, aliasing and so on.
SQL query
SELECT tbl.* from (
SELECT u.*, COUNT(u.id) AS question_count FROM app_user AS u
INNER JOIN question AS q ON u.id = q.user_id GROUP BY u.id
) AS tbl ORDER BY tbl.question_count DESC LIMIT 10;
JPA query:
SELECT tbl FROM (SELECT u, COUNT(u.id) question_count FROM User u
INNER JOIN u.questions q ON u.id = q.user_id GROUP BY u.id) tbl
ORDER BY tbl.question_count LIMIT 10")
I can't test this with anything right now, but something along the lines of:
final String queryStr = "SELECT u, COUNT(u.id) FROM User u, Questions q WHERE u.id = q.user_id GROUP BY u.id ORDER BY COUNT(u.id) DESC";
Query query = em().createQuery(queryStr);
query.setMaxResults(10);
List<Object[]> results = query.getResultList(); //Index [0] will contain the User-object, [1] will contain Long with result of COUNT(u.id)

JPQL, How to NOT select something

I have a pretty simple SQL I need to perform.
I have a ProcessUser, Role and a ProcessUserRole table. A straight forward many-to-many
I want to select all ProcessUser's that does also have a Role of admin.
However my JPQL fails because my user also has role officer, so it is retrieved in the list.
Here is the JPQL:
entityManager.createQuery("SELECT p FROM " + ProcessUser.class.getName()
+ " p join p.roles role WHERE role.name NOT IN ('sysadmin')").getResultList();
The generated SQL is:
select
distinct processuse0_.id as id8_,
processuse0_.position as position8_,
processuse0_.username as username8_,
processuse0_.organization_id as organiza9_8_,
processuse0_.passwordHash as password4_8_,
processuse0_.fromEmail as fromEmail8_,
processuse0_.firstname as firstname8_,
processuse0_.lastname as lastname8_,
processuse0_.processes as processes8_
from
ProcessUser processuse0_
inner join
ProcessUserRoles roles1_
on processuse0_.id=roles1_.userId
inner join
Role role2_
on roles1_.roleId=role2_.id
where
(
role2_.name not in (
'sysadmin'
)
)
Proper JPQL syntax using subquery:
SELECT p FROM ProcessUser p
WHERE p.id NOT IN (
SELECT p2.id FROM ProcessUser p2
JOIN p2.roles role
WHERE role.name='sysadmin'
)
Will this work for you?
SELECT *
FROM ProcessUser
WHERE Exists
(
SELECT 1
FROM
ProcessUserRoles
INNER JOIN Roles
ON Roles.RoleId = ProcessUserRoles.RoleId
WHERE 1=1
AND ProcessUser.ProcessUserId = ProcessUserRoles.ProcessUserId
AND Roles.RoleDescription = 'Super User'
)
Your query is basicly bringing back a list of user/roles since your user has two roles he comes back twice, you filter out one row by excluding the role of 'sysadmin'. What it sounds like you want to do is exclude all users who have a role of 'sysadmin' regardless of they have other roles. You would need to add something to you query like. (I'm going by your query not your description)
where processuse0_.id not in
select ( userId from
ProcessUserRoles
inner join
Role
on ProcessUserRoles.roleId=Role.id
where role.name != 'sysadmin'
)
Run a nested query. First select all users with the role of sysadmin. Then select the complement of this, or all users that are not in this result.
JPQL:
TypedQuery<ProcessUser> query = em.createQuery("" +
" SELECT p FROM ProcessUser p " +
" WHERE p.roles.name <> ?1", ProcessUser.class);
query.setParameter(1, "sysadmin");
return query.getResultList;