Update using join or nasted sub-query Hibernate (HQL) - java

I have a problem creating HQL that updates entity CommitteeMembership with nested queries or using joins, i tried this query first:
update CommitteeMemberShip cms set cms.isDeleted=1
where cms.committeeCycle.committee.id=:committeeId
but the generated SQL was wrong as following:
update CommitteeMemberShip cross join set isDeleted=1 where committee_id=?
without any this after "cross join" makes Hibernate throws SQLGrammarException
After that, i have changed my query to use sub-query:
update CommitteeMemberShip cms set cms.isDeleted=1
where cms.id in (select cmsIn.id from CommitteeMemberShip cmsIn inner join
cmsIn.committeeCycle cc inner join cc.committee c where c.id=:committeeId)
now Hibernate throws
java.sql.SQLException: You can't specify target table 'CommitteeMemberShip' for
update in FROM clause
any one have any idea how i can write this update query in HQL??

Try this
update CommitteeMemberShip cms set cms.isDeleted=1
where cms.id = some(select cmsIn.id from CommitteeMemberShip cmsIn inner join
cmsIn.committeeCycle cc inner join cc.committee c where c.id=:committeeId)

Try this
"UPDATE table1 r SET r.column = #value WHERE r.column IN("SELECT rd.Id FROM r.table2 rd WHERE rd.Column='"+#value+"' AND rd.Column='"+value+"'";

It seems that HQL does not yet support path expressions / implicit joins in DML statements. Here's how jOOQ does it (in SQL, assuming all paths are to-one relationships (Disclaimer: I work for the company behind jOOQ)):
UPDATE CommitteeMemberShip cms
SET cms.isDeleted = 1
WHERE (
SELECT Committee.id
FROM Committee
WHERE (
SELECT CommitteeCycle.committeeId
FROM CommitteeCycle
WHERE CommitteeCycle.id = cms.committeeCycleId
) = Committee.id
) = :committeeId
Your own approach didn't work because you repeated the CommitteeMemberShip entity unnecessarily. That's a known MySQL limitation, see:
MySQL Error 1093 - Can't specify target table for update in FROM clause
This would have worked, I suspect?
UPDATE CommitteeMemberShip cms
SET cms.isDeleted = 1
WHERE cms.committeeCycleId IN (
SELECT CommitteeCycle.id
FROM CommitteeCycle cc
JOIN cc.committee c
WHERE c.id = :committeeId
)
Starting with Hibernate 6 and their support for derived tables, you might be able to work around MySQL's limitation like this (the same way as jOOQ does it out of the box, see here):
UPDATE CommitteeMemberShip cms
SET cms.isDeleted=1
WHERE cms.id IN (
-- Wrap the query once more in a derived table
SELECT t.id
FROM (
SELECT cmsIn.id
FROM CommitteeMemberShip cmsIn
JOIN cmsIn.committeeCycle cc
JOIN cc.committee c WHERE c.id=:committeeId
) t
)

Related

Oracle SQL Developer - JOIN on 2 queries with a one-to-many relationship

I have two queries that I'm trying to join together.
In first_query TABLE2.PROCESS_ID, every PROCESS_ID is unique in that table. In second_query though there are several PROCESS_ID's with the same number in TABLE3, so I think I have to do a one-to-many join. The join_query I have is giving me an error ORA-00933: SQL command not properly ended which I'm assuming has something to do with the one-to-many relationship with the JOIN.
I'm not really sure how to resolve this. Any help would be much appreciated!
first_query = """
SELECT TABLE1.RULE_ID, TABLE2.STATUS, TABLE2.ERROR_MESSAGE, TABLE2.PROCESS_ID
FROM TABLE2 LEFT JOIN
TABLE1
ON TABLE1.RULE_ID = TABLE2.RULE_ID
WHERE TABLE1.RULE_NAME IN ('TEST1', 'TEST2')
"""
second_query = """
SELECT RECORDS_PROCESSED, PROCESS_ID, STATUS
FROM TABLE3
"""
join_query = """
SELECT RULE_ID, STATUS, ERROR_MESSAGE, PROCESS_ID
FROM (first_query) as query_1
INNER JOIN (second_query) as query_2
ON query_1.PROCESS_ID = query_2.PROCESS_ID
GROUP BY PROCESS_ID desc
"""
You can not select 4 columns and group by only one of them unles you include selected columns as part of aggregation fucntion(like max(), sum(),...). One of the options is this:
SELECT query_1.RULE_ID --1
, query_2.STATUS --2
, query_1.ERROR_MESSAGE --3
, query_1.PROCESS_ID --4
FROM (SELECT TABLE1.RULE_ID
, TABLE2.STATUS
, TABLE2.ERROR_MESSAGE
, TABLE2.PROCESS_ID
FROM TABLE2
LEFT JOIN TABLE1
ON TABLE1.RULE_ID = TABLE2.RULE_ID
WHERE TABLE1.RULE_NAME IN ('TEST1', 'TEST2')) query_1
INNER JOIN (SELECT RECORDS_PROCESSED
, PROCESS_ID
, STATUS
FROM TABLE3) query_2
ON query_1.PROCESS_ID = query_2.PROCESS_ID
GROUP BY query_1.RULE_ID
, query_2.STATUS
, query_1.ERROR_MESSAGE
, query_1.PROCESS_ID
Also please do consider using aliases like this(in your first query):
SELECT T1.RULE_ID
, T2.STATUS
, T2.ERROR_MESSAGE
, T2.PROCESS_ID
FROM TABLE2 T2
LEFT JOIN TABLE1 T1 ON T1.RULE_ID = T2.RULE_ID
WHERE T1.RULE_NAME IN ('TEST1', 'TEST2')
Also, apply the same logic with aliases on your final query or else you will have a different kind of error : "ORA-00918: column ambiguously defined"
Here is a small demo
CTE (i.e. the WITH factoring clause) might help.
WITH first_query
AS (SELECT table1.rule_id,
table2.status,
table2.error_message,
table2.process_id
FROM table2 LEFT JOIN table1 ON table1.rule_id = table2.rule_id
WHERE table1.rule_name IN ('TEST1', 'TEST2')),
second_query
AS (SELECT records_processed, process_id, status FROM table3)
SELECT a.rule_id,
a.status,
a.error_message,
a.process_id
FROM first_query a INNER JOIN second_query b ON a.process_id = b.process_id
GROUP BY you used is invalid; you can't group results by only one column. If results have to be unique, use select distinct. If you have to use group by, specify all columns returned by select (which leads you back to what distinct does), or see whether some column(s) have to be aggregates - in that case, group by makes sense.
Also, you should always use table aliases. Without them, query is invalid as database engine doesn't know which table those columns (if they share the same name) belong to.

Querydsl ignores common table expression alias for JPA Entities

I need to build SQL query with common table expression using QueryDSL:
WITH cte AS (
SELECT DISTINCT BUSINESS_ID FROM BUSINESS WHERE MERCHANT_CODE like ?
)
SELECT t0.*
FROM PAYMENT t0
LEFT JOIN cte t1 ON t0.PAYER = t1.BUSINESS_ID
LEFT JOIN cte t2 ON t0.PAYEE = t2.BUSINESS_ID
WHERE (t1.BUSINESS_ID IS NOT NULL OR t2.BUSINESS_ID IS NOT NULL)
I have two JPA entities (Payment, Business).
This is how I implemented that:
String merchantCode = "abcd%";
QPayment payment = QPayment.payment;
QBusiness business = QBusiness.business;
QBusiness cte = new QBusiness("cte");
QBusiness merchant1 = new QBusiness("t1");
QBusiness merchant2 = new QBusiness("t2");
Configuration configuration = new Configuration(new OracleTemplates());
new JPASQLQuery<>(entityManager, configuration)
.with(cte,
JPAExpressions.selectDistinct(business.businessId).from(business)
.where(business.merchantCode.like(merchantCode)))
.select(payment)
.from(payment)
.leftJoin(cte, merchant1).on(payment.payer.eq(merchant1.businessId))
.leftJoin(cte, merchant2).on(payment.payee.eq(merchant2.businessId))
.where(merchant1.businessId.isNotNull()
.or(merchant2.businessId.isNotNull()));
And the problem is that during leftJoin it doesn't treat cte as a link, instead it inserts table name and two aliases: LEFT JOIN BUSINESS cte t1 ON .... I tried different templates – didn't help.
Am I doing something wrong or it's a QueryDSL bug?
JPQL doen't support CTEs, as we can see in grammar. And querydsl works over JPQL. CTEs are pretty vendor-specific, so you'll have to do one of following:
Rewrite query to be JPA-compatible
Use JPA native query
Query sql with querydsl (actually I don't remember if it supports CTEs)
From all above I would chose the 2nd option. Making native queries doen't harm your code. It makes your code more performant.
Take a good look at the tutorial
QCat cat = QCat.cat;
QCat mate = new QCat("mate");
QCat kitten = new QCat("kitten");
query.from(cat)
.innerJoin(cat.mate, mate)
.leftJoin(cat.kittens, kitten)
.list(cat);
You will want to .leftjoin(cte.merchant1, merchant1).on(...) or whatever the corresponding field is called in the parent "cte".
Basically you need to name the field which you want to join. Just stating the meta model does not suffice as there is no way of telling what you actually want. You can see it in your code (as well in the tutorial's kitten example): you have two Merchant you want to join to the cte, so which one is which.
The .on()-clause just states the conditions under which a join is valid, like you could place filters there.
.

How to write equivalent JPQL query for LEFT OUTER JOIN written in native sql?

This is the native query -
#Query(nativeQuery = true, value = "SELECT CA FROM CLASS_ATTRIBUTE,WORD_PROCESSING WHERE ( word_processing.uoid = class_attribute.c_user_explanation(+)) ORDER BY CLASS_ATTRIBUTE.DESCRIPTION ASC
I tried the following JPQL query, but it didn't work -
#Query(value = "SELECT CA FROM ClassAttribute CA LEFT JOIN CA.WordProcessing AS WP ON ( WP.id = CA.userExplanationUoid) ORDER BY CA.description ASC")
Also tried the following JPQL -
#Query(value = "SELECT CA FROM ClassAttribute CA LEFT OUTER JOIN CA.WordProcessing AS WP WHERE ( WP.id = CA.userExplanationUoid) ORDER BY CA.description ASC")
The compilation goes into an infinite loop if I try these. Something from the Spring framework is causing it to refresh again and again. There seems to be some issue with the syntax of the converted JPQL query.
It appears that here the issue was with the pom.xml where hibernate.entitymanager.version was 5.0.12.Final. Changing it to 5.1.10.Final did the trick for me.
Since hibernate.entitymanager is now deprecated by Maven (https://mvnrepository.com/artifact/org.hibernate/hibernate-entitymanager), it actually downloaded the jar hibernate-core:5.1.10.Final because of which JOIN related queries are working fine now.

How to rewrite sql statement to work with hibernate?

I'm using PostgreSQL in my java application without ORM. I want to go further and add Hibernate to my project. I have this sql query which I add to PreparedStatement() and it returns a number.
SELECT COUNT(pr.id) FROM prisoner pr
JOIN cell c ON c.id = pr.cell_id
JOIN prison p ON p.id = c.prison_id
WHERE p.id = ?
I'm new to Hibernate. How would you suggest me to rewrite this statement to work with Hibernate? Should I use HSQL, or criteria or query or something different ?
You can do it Either of following way.
1) Keep you query as it and use nativeSQL for hibernate.
hibernate native query, count
2) make model of all your join table and put hibernate query.

Find duplicated rows in MySQL with QueryDSL JPA

I would like to execute this SQL request in QueryDSL JPA
SELECT authorizationitem.*
FROM authorizationitem
INNER JOIN
(
SELECT `authorize`
FROM authorizationitem
GROUP BY `authorize`
HAVING COUNT(*)>1
) a2
ON authorizationitem.`authorize` = a2.`authorize`;
in order to find duplicated row in a table, i should execute this request. But with QueryDSL, i cannot find the way to write this.
It seems QueryDSL does not allow subQuery in Inner Join :s
Any suggestion?
Thanks
regards,
You can't express this with HQL/JPQL, so you will need to expess this with SQL. Querydsl JPA provides the possibility to express both JPQL and SQL queries through its API.
You can try using a subquery in the WHERE clause, but it will probably be less efficient than the subquery in the FROM clause. Make sure there is an index on authorizationitem.authorize to optimize the joins and the GROUP BY.
SELECT authorizationitem.*
FROM authorizationitem
WHERE EXISTS (
SELECT `authorize`
FROM authorizationitem2
WHERE authorizationitem2.authorize = authorizationitem.authorize
GROUP BY `authorize`
HAVING COUNT(*)>1
);
or
SELECT authorizationitem.*
FROM authorizationitem
WHERE (
SELECT count(*)
FROM authorizationitem2
WHERE authorizationitem2.authorize = authorizationitem.authorize
GROUP BY `authorize`
) > 1;

Categories

Resources