HQL - Delete with JOIN error - java

Im trying to execute an HQL delete with join.. After soon searching I found that I need to create a query just like suggested HERE:
http://dasunhegoda.com/1093-you-cant-specify-target-table-table_name-for-update-in-from-clause/104/
This is my query:
dao.executeByHql(
"DELETE FROM FinalGradeResult e WHERE e.id IN "
+ "( SELECT id FROM "
+ "( SELECT x FROM FinalGradeResult x "
+ "where x.student.id = :studentId "
+ " AND x.classDiscipline IN " +
+ "(SELECT cd from ClassDiscipline cd "
+ " where cd.clazz.id = :clazzId ) ) as X )",
new HqlParameter("studentId", student.getId()),
new HqlParameter("clazzId", from.getId()));
But I keep getting this error:
org.hibernate.hql.internal.ast.QuerySyntaxException: unexpected token:( near line 1, column 94 [DELETE FROM xxxxxxxxxxxx.entity.FinalGradeResult e WHERE e.id IN ( SELECT id FROM ( SELECT x FROM xxxxxxxxxxxxxxx.entity.FinalGradeResult x where x.student.id = :studentId AND x.classDiscipline IN (SELECT cd from xxxxxxxxxxxxxxxx.entity.ClassDiscipline cd where cd.clazz.id = :clazzId ) ) as X )]
The error points out that the second ( is wrong, the one right after "SELECT id FROM"
EDIT I ve tried like this and same error occours:
dao.executeByHql(
"DELETE FROM FinalGradeResult e WHERE e.id IN "
+ "( SELECT id FROM "
+ "( SELECT x FROM FinalGradeResult x "
+ " where x.student.id = :studentId "
+ " AND x.classDiscipline.clazz.id = :clazzId )"
+ " as X )",
EDIT 2 : The query like this doesnt work because of the problem described on the link I've posted in this question:
dao.executeByHql(
"DELETE FROM FinalGradeResult e WHERE e.id IN " + "( SELECT x.id FROM FinalGradeResult as x "
+ " where x.student.id = :studentId " + " AND x.classDiscipline.clazz.id = :clazzId )",
new HqlParameter("studentId", student.getId()), new HqlParameter("clazzId", from.getId()));
The error is:
Caused by: java.sql.SQLException: You can't specify target table 'tb_final_grade_result' for update in FROM clause
SOLUTION
After trying everything out here's our conclusion:
We cant have only one query directly because we cant have joins on DELETE.
We cant have only 2 queries (one subquery) because we have a MYSQL BUG (described on the link provided)
We cant have 3 queries (2 subqueries) because we cant have a subquery in the FROM clause. That's why our second query doesn´t work (select * from (select ...)) is invalid.
So I decided to use NativeSQL to solve the problem:
dao.executeBySQL(
" delete from tb_final_grade_result where id in "
+ " (select * from ( select finalgrade1_.id from tb_final_grade_result finalgrade1_ cross join tb_class_discipline classdisci2_ "
+ " where finalgrade1_.id_class_discipline=classdisci2_.id and finalgrade1_.id_student= :studentId and classdisci2_.id_class= :clazzId ) as tmp )",
new HqlParameter("studentId", student.getId()), new HqlParameter("clazzId", from.getId()));
Special thanks to #scaisEdge

Why do you need to use in (select on the same table that you are deleting from? Can't you just put the condition in the where clause?
DELETE FROM FinalGradeResult e WHERE e.student.id = :studentId " + " AND e.classDiscipline.clazz.id = :clazzId )",
new HqlParameter("studentId", student.getId()), new HqlParameter("clazzId", from.getId()));
Also, I'm not sure what you are referring to with the parameter classDiscipline.clazz.id? Is classDiscipline some other entity with a field named clazz that is yet another entity? That's what the query seems to be saying.

The problem with your original query is that you are not allowed you have inner selects anywhere but in select and where clauses. Because HQL operates in terms of entities and not tables or columns, selecting from subsets of tables doesn't make sense. See here:
Note that HQL subqueries can occur only in the select or where clauses.
We had a similar issue in our project and I struggled for awhile to find a single query solution, but I'm afraid with MySQL it isn't possible due to HQL's language design.
The best I came up with was to first fetch the ids as a list, and then pass that list as a list parameter to your update. For you it might be something like:
Query idQuery = createQuery("select id from FinalGradeResult gr where gr.student.id = :studentId AND gr.classDiscipline IN (SELECT cd from ClassDiscipline cd where cd.clazz.id = :clazzId"));
//add parameters
List<Number> ids = query.list();
Query entityQuery = createQuery("delete from FinalGradeResult where id in (:ids)");
entityQuery.setParameterList("ids", ids);
query.executeUpdate()

could be the position of as X is wrong
and you are repeting x instead of column name ( set ***column_name*** with the proper column name)
"DELETE FROM FinalGradeResult e WHERE e.id IN "
+ "( SELECT id FROM
( SELECT ***column_name *** FROM FinalGradeResult x where x.student.id = :studentId AND x.classDiscipline IN
(SELECT cd from ClassDiscipline cd where cd.clazz.id = :clazzId ) as X) )",
If the sibquery don't work in hibernate then try with a simple query with join
DELETE FROM FinalGradeResult e
WHERE e.id in (
SELECT id FROM FinalGradeResult x
JOIN ClassDiscipline cd ON ( cd.cd.clazz.id = :clazzId
and x.classDiscipline = cd.ClassDiscipline )
WHERE x.student.id = :studentId);

SOLUTION
After trying everything out here's our conclusion:
We cant have only one query directly because we cant have joins on DELETE.
We cant have only 2 queries (one subquery) because we have a MYSQL BUG (described on the link provided)
We cant have 3 queries (2 subqueries) because we cant have a subquery in the FROM clause. That's why our second query doesn´t work (select * from (select ...)) is invalid.
So I decided to use NativeSQL to solve the problem:
dao.executeBySQL(
" delete from tb_final_grade_result where id in "
+ " (select * from ( select finalgrade1_.id from tb_final_grade_result finalgrade1_ cross join tb_class_discipline classdisci2_ "
+ " where finalgrade1_.id_class_discipline=classdisci2_.id and finalgrade1_.id_student= :studentId and classdisci2_.id_class= :clazzId ) as tmp )",
new HqlParameter("studentId", student.getId()), new HqlParameter("clazzId", from.getId()));

Related

Problem with query update and select with where and join

select new mvp.backend.finance.dto.projection.SplitShipDto(spl.id, spl.promoVolume, spl.baselineVolumeProportion) from SplitShipment spl inner join DateDay spd on spl.dateDay.id = spd.id " +
"where spl.sku.id =:sku_id and spd.day > cast(:date as string)
skuIdAndDates.parallelStream().forEach(spl -> {
float rcpPromo = priceFromSap.getBasePrice() * spl.getPromoVolume();
float rcpBaseline = priceFromSap.getBasePrice() * spl.getBaselineVolumeProportion();
RequestSplitShipmentRCP requestSplitShipmentRCP = RequestSplitShipmentRCP.builder().id(spl.getId()).rcpPromo(rcpPromo).rcpBaseline(rcpBaseline).build();
splitShipmentRCP.add(requestSplitShipmentRCP);
});
update split_shipments set " +
"rcp_promo =:rcpPromo, rcp_baseline =:rcpBaseline " +
"where id =:id
Guys help me. I must use this 2 queries in one query. In first query i select spl.id, spl.promoVolume, spl.baselineVolumeProportion from table split_shipments. How I can creat this? Sorry for my eng...

incorrect #query in spring boot

I am trying to use this query in springboot so I can display the results in a webpage. I know that this query works because I tested it in postgresql and it gave me the right results.
But JPA is telling me that the '(' after the first FROM is an unexpected token and the query was therefore viewed as invalid.
This is my query:
#Query(
"SELECT com.example.imse22.model.TrvlA_Cust_Dto(books_query.name, count(travelA_query.customer_id)) " +
"FROM (SELECT DISTINCT customer_servant.employee_id, books.customer_id FROM customer_servant " +
"INNER JOIN books ON customer_servant.employee_id = books.customer_servant_id) AS travelA_query " +
"INNER JOIN " +
"(SELECT travel_agency.id, travel_agency.name, employee.employee_id FROM travel_agency " +
"INNER JOIN employee ON travel_agency.id = employee.travel_agency_id) AS books_query " +
"ON travelA_query.employee_id = books_query.employee_id " +
"GROUOP BY travelA_query.name")
can somebody help me out how I could rewrite the query so that JPA approves it?
Your query is native so you should declare it in that way:
#Query(
value = "SELECT com.example.imse22.model.TrvlA_Cust_Dto(books_query.name, count(travelA_query.customer_id)) " +
"FROM (SELECT DISTINCT customer_servant.employee_id, books.customer_id FROM customer_servant " +
"INNER JOIN books ON customer_servant.employee_id = books.customer_servant_id) AS travelA_query " +
"INNER JOIN " +
"(SELECT travel_agency.id, travel_agency.name, employee.employee_id FROM travel_agency " +
"INNER JOIN employee ON travel_agency.id = employee.travel_agency_id) AS books_query " +
"ON travelA_query.employee_id = books_query.employee_id " +
"GROUOP BY travelA_query.name", nativeQuery = true)
link point 2.2: https://www.baeldung.com/spring-data-jpa-query
Ok so this is how I solved it:
I changed my query into a native one just like #notAPPP pointed out and then I only had to add an alias for the LEFT JOIN (also I changed INNER JOIN to LEFT JOIN).
here the code example:
#Query(value = "SELECT combined.name as name, count(combined.customer_id) as id " +
"FROM (" +
"(SELECT travel_agency.name, travel_agency.id, employee.employee_id " +
"FROM travel_agency INNER JOIN employee ON travel_agency.id = employee.travel_agency_id) as trvlEmp " +
"LEFT JOIN " +
"(SELECT books.customer_id, books.customer_servant_id, customer_servant.employee_id " +
"FROM books INNER JOIN customer_servant ON books.customer_servant_id = customer_servant.employee_id) as custBooks " +
"ON trvlEmp.employee_id = custBooks.employee_id) as combined " + // this "AS combined" got added
"GROUP BY combined.name", nativeQuery = true)
This makes sense, because after a FROM clause one should wirte the name of a table or a result table (e.g. from two joined queries like in my case). As I didnt specify an alias for the LEFT JOIN of my two subqueries, JPA obviously didnt know how to handle the result of those subqueries. Therefore always name your subqueries if they are not used in a WHERE clause, but rather with a FROM clause, like in my case. E.g. the name I gave my LEFT JOIN is "combined" as seen in the code example above.
Also I changed my INNER JOIN to a LEFT JOIN to get the value 0 of the elements that have 0 counts of what I wanted to count in the table.
If you want to know how to handle the result which such a query returns follow this link.thorben-janssen.com/spring-data-jpa-dto-native-queries

QuerySyntaxException: unexpected token: FROM

I'm trying to write a query that will UPDATE a table generated in hibernate, with the result of another hibernate generated table.
I've written this query in ssms, in ssms it runs great.
UPDATE DMVOUCHER set VOUCHERPARTYID = EXTERNALID
from
(
select
EXTERNALID
,row_number() over (partition by dv.VoucherUid order by dc.PRIMARYCOMPANY desc, dc.ExternalId) row_num
,dv.VoucherUid as vUid from DMVOUCHER dv
join DMCOMPANY dc on dv.VOUCHERPARTYID = dc.REGISTRATION
where VOUCHERTYPE = 'R03') as t1
where t1.row_num = 1
and VoucherUid = vUid
and VOUCHERTYPE = 'R03'
row_num is to make it prefer the result if primaryCompany is equal to 1 (PrimaryCompany is a boolean), if that doesn't exist then it can do it based on any externalId. in my query I just prefer the lowest externalId
however the problem then comes when I'm trying to execute this in my application. here I am trying to execute the equivelant string which can be seen below:
String queryString =
"UPDATE DMVOUCHER \n"
+ "SET VOUCHERPARTYID = EXTERNALID \n"
+ "FROM ( \n"
+ " SELECT EXTERNALID \n"
+ " , row_number() over (partition by dv.VoucherUid order by dc.PRIMARYCOMPANY desc, dc.ExternalId) row_num \n"
+ " , dv.VoucherUid as vUid \n"
+ " FROM DMVOUCHER dv \n"
+ " JOIN DMCOMPANY dc ON dv.VOUCHERPARTYID = dc.REGISTRATION \n"
+ " WHERE VOUCHERTYPE = 'R03') as t1 \n"
+ "WHERE t1.row_num = 1 \n"
+ " AND VoucherUid = vUid \n"
+ " AND VOUCHERTYPE = 'R03'";
however I then end up with this error message:
2021-06-21 13:23:27.173. Error: TopDmCompanyGtiNumberEntityAfterMapper failed: QuerySyntaxException: unexpected token: FROM near line 3, column 1 [UPDATE DMVOUCHER
SET VOUCHERPARTYID = EXTERNALID
FROM (
SELECT EXTERNALID
, row_number() over (partition by dv.VoucherUid order by dc.PRIMARYCOMPANY desc, dc.ExternalId) row_num
, dv.VoucherUid as vUid
FROM DMVOUCHER dv
JOIN DMCOMPANY dc ON dv.VOUCHERPARTYID = dc.REGISTRATION
WHERE VOUCHERTYPE = 'R03') as t1
WHERE t1.row_num = 1
AND VoucherUid = vUid
AND VOUCHERTYPE = 'R03']
org.hibernate.hql.internal.ast.QuerySyntaxException.convert(QuerySyntaxException.java:74)
org.hibernate.hql.internal.ast.ErrorTracker.throwQueryException(ErrorTracker.java:93)
org.hibernate.hql.internal.ast.QueryTranslatorImpl.parse(QueryTranslatorImpl.java:301)
org.hibernate.hql.internal.ast.QueryTranslatorImpl.doCompile(QueryTranslatorImpl.java:189)
org.hibernate.hql.internal.ast.QueryTranslatorImpl.compile(QueryTranslatorImpl.java:144)
org.hibernate.engine.query.spi.HQLQueryPlan.<init>(HQLQueryPlan.java:113)
org.hibernate.engine.query.spi.HQLQueryPlan.<init>(HQLQueryPlan.java:73)
org.hibernate.engine.query.spi.QueryPlanCache.getHQLQueryPlan(QueryPlanCache.java:162)
org.hibernate.internal.AbstractSharedSessionContract.getQueryPlan(AbstractSharedSessionContract.java:604)
org.hibernate.internal.AbstractSharedSessionContract.createQuery(AbstractSharedSessionContract.java:716)
org.hibernate.internal.AbstractSharedSessionContract.createQuery(AbstractSharedSessionContract.java:113)
com.schantz.generallife.dm.logic.writer.DmUtil.executeQuery(DmUtil.java:16)
com.schantz.generallife.dm.logic.datamart.DatamartWriter.handleSynchronousRequest(DatamartWriter.java:211)
com.schantz.generallife.dm.logic.datamart.DatamartWriter.write(DatamartWriter.java:151)
com.schantz.generallife.dm.logic.datamart.DatamartWriter$1.execute(DatamartWriter.java:107)
com.schantz.foundation.config.hibernate.AbstractDatabaseTargetThreadLogic$DatabaseTargetThread.execute(AbstractDatabaseTargetThreadLogic.java:170)
com.schantz.foundation.config.hibernate.AbstractDatabaseTargetThreadLogic$DatabaseTargetThread.run(AbstractDatabaseTargetThreadLogic.java:159)
java.base/java.lang.Thread.run(Thread.java:834)
I've tried to look around on stackoverflow for similiar errors, however I havn't been able to identify any queries like mine. or where the problem is that the unexpected token is the FROM
I think a possible issue is that ssms uses a different sql language than my application does. I must admit I don't know the actual sql language either that my ssms uses or my application does. which also makes debugging the problem a bit harder. so if you are able to identify it based on what I've written, that information would also be appriciated.
edit following the method call where I execute my queryString, it reaches a new method which calls another method. doing that a few times I finally get this method:
public static Integer executeQuery(PersistenceManager persistenceManager, Class<? extends AbstractEntity> clazz, String queryString, Map<String, Object> parameters) {
Session session = (Session) ((PersistenceManagerImpl) persistenceManager).getTransactionalEntityManager().getDelegate();
Query<?> query = session.createQuery(queryString);
for (Entry<String, Object> entry : parameters.entrySet()) {
query.setParameter(entry.getKey(), entry.getValue());
}
return query.executeUpdate();
}
where executeUpdate() is from jakarta.persistence-api-2.2.3.jar
--edit--
I messed around a bit and tried to use the way it would written in mysql instead. therefore I now get this error message instead.
2021-06-22 11:44:10.496. Error: TopDmCompanyGtiNumberEntityAfterMapper failed: QuerySyntaxException: expecting IDENT, found '(' near line 2, column 1 [UPDATE
(SELECT EXTERNALID
, row_number() over (partition by dv.VoucherUid order by dc.PRIMARYCOMPANY desc, dc.ExternalId) row_num
, dv.VoucherUid as vUid
FROM DMVOUCHER dv
JOIN DMCOMPANY dc ON dv.VOUCHERPARTYID = dc.REGISTRATION
WHERE VOUCHERTYPE = 'R03') as t1 SET VOUCHERPARTYID = EXTERNALID
WHERE t1.row_num = 1
AND VoucherUid = vUid
AND VOUCHERTYPE = 'R03']
org.hibernate.hql.internal.ast.QuerySyntaxException.convert(QuerySyntaxException.java:74)
org.hibernate.hql.internal.ast.ErrorTracker.throwQueryException(ErrorTracker.java:93)
org.hibernate.hql.internal.ast.QueryTranslatorImpl.parse(QueryTranslatorImpl.java:301)
org.hibernate.hql.internal.ast.QueryTranslatorImpl.doCompile(QueryTranslatorImpl.java:189)
org.hibernate.hql.internal.ast.QueryTranslatorImpl.compile(QueryTranslatorImpl.java:144)
org.hibernate.engine.query.spi.HQLQueryPlan.<init>(HQLQueryPlan.java:113)
org.hibernate.engine.query.spi.HQLQueryPlan.<init>(HQLQueryPlan.java:73)
org.hibernate.engine.query.spi.QueryPlanCache.getHQLQueryPlan(QueryPlanCache.java:162)
org.hibernate.internal.AbstractSharedSessionContract.getQueryPlan(AbstractSharedSessionContract.java:604)
org.hibernate.internal.AbstractSharedSessionContract.createQuery(AbstractSharedSessionContract.java:716)
org.hibernate.internal.AbstractSharedSessionContract.createQuery(AbstractSharedSessionContract.java:113)
com.schantz.generallife.dm.logic.writer.DmUtil.executeQuery(DmUtil.java:16)
com.schantz.generallife.dm.logic.datamart.DatamartWriter.handleSynchronousRequest(DatamartWriter.java:211)
com.schantz.generallife.dm.logic.datamart.DatamartWriter.write(DatamartWriter.java:151)
com.schantz.generallife.dm.logic.datamart.DatamartWriter$1.execute(DatamartWriter.java:107)
com.schantz.foundation.config.hibernate.AbstractDatabaseTargetThreadLogic$DatabaseTargetThread.execute(AbstractDatabaseTargetThreadLogic.java:170)
com.schantz.foundation.config.hibernate.AbstractDatabaseTargetThreadLogic$DatabaseTargetThread.run(AbstractDatabaseTargetThreadLogic.java:159)
java.base/java.lang.Thread.run(Thread.java:834)
I believe it makes the problem a bit clearer, I need to covert my query query so its in hql instead. I will answer the post if I figure out how to do that, as I'm not so familiar with hql
the problem was indeed that the queryString was supposed to be in hql. I've rewritten the queryString so it now works, it now looks like this:
String queryString =
"UPDATE DmVoucher SET voucherPartyId = (SELECT externalId \n"
+ " FROM DmCompany dc \n"
+ " WHERE voucherType = 'R03' \n"
+ "And dc.primaryCompany = CASE WHEN (SELECT count(dcp) FROM DmCompany dcp WHERE primaryCompany = 1 and voucherPartyId = dcp.registration) > 0 THEN 1 else 0 END\n"
+ "and voucherPartyId = dc.registration)\n";

Works well in pgadmin but doesn't work in java code

with recursive
Ancestor(a,d) as (select parent as a, child as d from ParentOf
union
select Ancestor.a , ParentOf.child as d
from Ancestor, ParentOf
where Ancestor.d = ParentOf.parent)
Hi I ran the above code in pgadmin and it worked fine so I tried moving this to my java code the same way. But here it is called an error in the SQL syntax. What is the reason for this? I also put ; in the SQL statement, but the same error occurred.
stmt.executeUpdate("with recursive " +
"Ancestor(a,d) as (select parent as a, child as d from ParentOf " +
"union " +
"select Ancestor.a , ParentOf.child as d " +
"from Ancestor, ParentOf " +
"where Ancestor.d = ParentOf.parent)");
Below is the error
Exception in thread "main" org.postgresql.util.PSQLException: error: syntax error, at the end of input
Position: 185
at org.postgresql.core.v3.QueryExecutorImpl.receiveErrorResponse(QueryExecutorImpl.java:2553)
at org.postgresql.core.v3.QueryExecutorImpl.processResults(QueryExecutorImpl.java:2285)
at org.postgresql.core.v3.QueryExecutorImpl.execute(QueryExecutorImpl.java:323)
at org.postgresql.jdbc.PgStatement.executeInternal(PgStatement.java:473)
at org.postgresql.jdbc.PgStatement.execute(PgStatement.java:393)
at org.postgresql.jdbc.PgStatement.executeWithFlags(PgStatement.java:322)
at org.postgresql.jdbc.PgStatement.executeCachedSql(PgStatement.java:308)
at org.postgresql.jdbc.PgStatement.executeWithFlags(PgStatement.java:284)
at org.postgresql.jdbc.PgStatement.executeUpdate(PgStatement.java:258)
at SqlTest3.main(SqlTest3.java:44)
You have two errors. First, to run a query, you need to use executeQuery(). Second, your SQL string in Java doesn't contain the final SELECT statement, it only contains the CTE.
ResultSet rs = stmt.executeQuery(
"with recursive "Ancestor(a,d) as (" +
"select parent as a, child as d from ParentOf " +
"union " +
"select Ancestor.a ParentOf.child as d " +
"from Ancestor " +
" join ParentOf on Ancestor.d = ParentOf.parent " +
") " +
"select * from ancestor"); // <<< this SELECT was missing
I also replaced your ancient and outdated implicit join in the where clause with a "modern" (over 25 years old) explicit JOIN operator which is the recommended way to write a join these days.

MySQL JDBC Fail calculating row number

I have this select that works every time running in workbench but fails sometimes for the same arguments over jdbc. The problem is that sometimes, over JDBC, 'pos' value returns null. I Think that, for some reason, the #p as not started, but dont know how to fix.
SELECT t1.wId, t1.twId, t1.name, t1.timeout, t1.pos
FROM (
SELECT w.id AS wId, tw2.id AS twId, w.name AS name, tw2.timeout AS timeout, #p:=#p+1 AS pos
FROM timeout_workqueue tw1
INNER JOIN timeout_workqueue tw2
ON tw1.workqueue_id = tw2.workqueue_id
INNER JOIN workqueue w
ON tw1.workqueue_id = w.id
WHERE tw1.id = ?
ORDER BY tw2.id) t1, (SELECT #p:=1) c
WHERE t1.twId = ?;
The whole Java Code Are:
public TimeoutWorkqueueView getTimeoutWorkqueueView(Integer id) {
String sql = "SELECT t1.wId, t1.twId, t1.name, t1.timeout, t1.pos"
+ " FROM ("
+ " SELECT w.id AS wId, tw2.id AS twId, w.name AS name, tw2.timeout AS timeout, #p:=#p+1 AS pos"
+ " FROM timeout_workqueue tw1"
+ " INNER JOIN timeout_workqueue tw2"
+ " ON tw1.workqueue_id = tw2.workqueue_id"
+ " INNER JOIN workqueue w"
+ " ON tw1.workqueue_id = w.id"
+ " WHERE tw1.id = ?"
+ " ORDER BY tw2.id) t1, (SELECT #p:=1) c"
+ " WHERE t1.twId = ?";
return (TimeoutWorkqueueView) getJdbcTemplate().queryForObject(sql, new BeanPropertyRowMapper(TimeoutWorkqueueView.class), id, id);
}
Ok so the problem i can see here (please verify) is that you think you are running the query with parameter set to 1 to begin with.
However if you set #p = 1 -> this #p:=#p+1 will not evaluate to 1 any more.
Also assuming you have 20 rows, you run this query 20 times but on the last run it will return null because pos will be 21 and this does not exists.

Categories

Resources