I have a query that runs on MySQL databases but not on h2.
This is my repository:
#Query(value = "SELECT r.* FROM rewards r "
+ "INNER JOIN models m ON r.model_id = m.model_pk "
+ "WHERE m.printer_family = :businessPrinterFamily "
+ "AND r.reward_type IN (:rewardTypes) "
+ "AND IF(:isOpportunities, m.model_pk IN (:businessPrinterModels), TRUE) "
+ "ORDER BY :sortingMethod",
countQuery = "SELECT r.* FROM rewards r "
+ "INNER JOIN models m ON r.model_id = m.model_pk "
+ "WHERE m.printer_family = :businessPrinterFamily "
+ "AND r.reward_type IN (:rewardTypes) "
+ "AND IF(:isOpportunities, m.model_pk IN (:businessPrinterModels), TRUE) ",
nativeQuery = true)
List<Reward> getFilteredRewards(#Param("sortingMethod") String sortingMethod,
#Param("isOpportunities") boolean isOpportunities,
#Param("businessPrinterModels") List<Integer> businessPrinterModels,
#Param("rewardTypes") List<Integer> rewardTypes,
#Param("businessPrinterFamily") int businessPrinterFamily, Pageable pageable);
But only on h2 I get the following error:
could not prepare statement; SQL [SELECT r.* FROM rewards r INNER JOIN models m ON r.model_id = m.model_pk WHERE m.printer_family = ? AND r.reward_type IN (?, ?) AND IF(?, m.model_pk IN (?), TRUE) ORDER BY ? limit ?]; nested exception is org.hibernate.exception.SQLGrammarException: could not prepare statement
org.springframework.dao.InvalidDataAccessResourceUsageException: could not prepare statement; SQL [SELECT r.* FROM rewards r INNER JOIN models m ON r.model_id = m.model_pk WHERE m.printer_family = ? AND r.reward_type IN (?, ?) AND IF(?, m.model_pk IN (?), TRUE) ORDER BY ? limit ?]; nested exception is org.hibernate.exception.SQLGrammarException: could not prepare statement
...
Caused by: org.h2.jdbc.JdbcSQLSyntaxErrorException: Syntax error in SQL statement "SELECT r.* FROM rewards r INNER JOIN models m ON r.model_id = m.model_pk WHERE m.printer_family = ? AND r.reward_type IN (?, ?) AND [*]IF(?, m.model_pk IN (?), TRUE) ORDER BY ? limit ?"; expected "INTERSECTS (, NOT, EXISTS, UNIQUE, INTERSECTS"; SQL statement:
SELECT r.* FROM rewards r INNER JOIN models m ON r.model_id = m.model_pk WHERE m.printer_family = ? AND r.reward_type IN (?, ?) AND IF(?, m.model_pk IN (?), TRUE) ORDER BY ? limit ? [42001-214]
In the logs I get the following:
2022-09-15 09:35:15.647 ERROR 267713 --- [ Test worker] o.h.engine.jdbc.spi.SqlExceptionHelper : Syntax error in SQL statement "SELECT r.* FROM rewards r INNER JOIN models m ON r.model_id = m.model_pk WHERE m.printer_family = ? AND r.reward_type IN (?, ?) AND [*]IF(?, m.model_pk IN (?), TRUE) ORDER BY ? limit ?"; expected "INTERSECTS (, NOT, EXISTS, UNIQUE, INTERSECTS"; SQL statement:
SELECT r.* FROM rewards r INNER JOIN models m ON r.model_id = m.model_pk WHERE m.printer_family = ? AND r.reward_type IN (?, ?) AND IF(?, m.model_pk IN (?), TRUE) ORDER BY ? limit ? [42001-214]
My h2 database is configured as follows:
spring:
datasource:
url: jdbc:h2:mem:testdb;MODE=MySQL
username: sa
password:
driver-class-name: org.h2.Driver
jpa:
defer-datasource-initialization: false
h2:
console:
enabled: true
path: /h2-console
Any ideas of whats wrong?
The H2 MySQL compatibility mode doesn't mention anything about an IF() function support. Your options are (at least):
Request it from H2
Replace IF(a, b, c) by standard SQL CASE WHEN a THEN b ELSE c END
Use testcontainers to run integration tests directly on MySQL rather than on H2 (I've blogged about this here, as I highly recommend it, irrespective of the ORM used)
Use a SQL translator like jOOQ to translate your native SQL to the target dialect, under the hood. Disclaimer: I work for the company behind jOOQ
The quickest fix is to use CASE. But personally, I recommend integration testing directly on MySQL, unless you're using H2 also as a production database product.
Related
I am using Spring Boot Data JPA and want to query data from my MySQL database. I wrote a query in MySQL workbench that works out perfectly fine in it and now I'd like to use exactly that query in my Repository as a native query.
This is the query working my MySQL Workbench:
SELECT s.* FROM fips.schedule s
inner join lecture_object lo on s.id_lecture_object = lo.id_lecture_object
inner join lecture_semester ls on lo.id_lecture_semester = ls.id_lecture_semester
inner join lecture_semester_has_possible_lecturers ll on ls.id_lecture_semester = ll.id_lecture_semester
where s.id_scenario = 1 and ll.id_lecturer=103 and ll.status="fixed";
This is the line in my Repository:
#Repository
public interface ScheduleRepository extends CrudRepository<Schedule, Integer> {
#Query(value="SELECT s.* FROM fips.schedule s " +
"inner join lecture_object lo on s.id_lecture_object = lo.id_lecture_object " +
"inner join lecture_semester ls on lo.id_lecture_semester = ls.id_lecture_semester " +
"inner join lecture_semester_has_possible_lecturers ll on ls.id_lecture_semester = ll.id_lecture_semester" +
"where s.id_scenario = :scenarioId " +
"and ll.id_lecturer = :lecturerId " +
"and ll.status = \"fixed\"", nativeQuery = true)
List<Schedule> getAllByFixedLecturerAndScenario(#Param("lecturerId") int lecturerId, #Param("scenarioId") int scenarioId);
}
Now upon execution I get the following error:
2020-09-22 18:02:15.607 DEBUG 7156 --- [nio-8081-exec-4] org.hibernate.SQL : SELECT s.* FROM fips.schedule s inner join lecture_object lo on s.id_lecture_object = lo.id_lecture_object inner join lecture_semester ls on lo.id_lecture_semester = ls.id_lecture_semester inner join lecture_semester_has_possible_lecturers ll on ls.id_lecture_semester = ll.id_lecture_semesterwhere s.id_scenario = ? and ll.id_lecturer = ? and ll.status = "fixed"
2020-09-22 18:02:15.608 WARN 7156 --- [nio-8081-exec-4] o.h.engine.jdbc.spi.SqlExceptionHelper : SQL Error: 1064, SQLState: 42000
2020-09-22 18:02:15.608 ERROR 7156 --- [nio-8081-exec-4] o.h.engine.jdbc.spi.SqlExceptionHelper : You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 's.id_scenario = 1 and ll.id_lecturer = 103 and ll.status = "fixed"' at line 1
2020-09-22 18:02:15.610 ERROR 7156 --- [nio-8081-exec-4] c.v.flow.server.DefaultErrorHandler :
could not extract ResultSet; SQL [n/a]; nested exception is org.hibernate.exception.SQLGrammarException: could not extract ResultSet
It seems like there is something wrong with the WHERE-clause, but it is exactly the same statement that works in MySQL WB. Am I missing something on the native queries? I also tried changing the " around fixed to ', which did not change the outcome (the same goes for replacing the status = "fixed" by status LIKE "fixed".
You need a space in the end of the string before "where". Change
on ls.id_lecture_semester = ll.id_lecture_semester"
to
on ls.id_lecture_semester = ll.id_lecture_semester "
I have this named query (simplified a little [removed some date filters], to be more clear at first sight):
#Entity
#XmlRootElement
#AdditionalCriteria("this.deleted <> 'deleted'")
#NamedQueries({
#NamedQuery(name = "FacilityForm.findByDateFacility", query = "SELECT f FROM FacilityForm f WHERE f.formDate BETWEEN :from and :to AND f.id In (SELECT o.id from InventoryLog l, FormItem i, FacilityForm o WHERE (:facilityId IS NULL OR i.facilityId = :facilityId) AND l.formItemId = i.id AND i.formId = o.id) ORDER BY f.formDate DESC")
})
Extracted query string:
"SELECT f FROM FacilityForm f WHERE f.formDate BETWEEN :from and :to AND f.id In (SELECT o.id from InventoryLog l, FormItem i, FacilityForm o WHERE (:facilityId IS NULL OR i.facilityId = :facilityId) AND l.formItemId = i.id AND i.formId = o.id) ORDER BY f.formDate DESC"
I know that if I want to use the main Entity in also one subquery I have to use another alias for it, I believe I done that (with: FacilityForm o in the subquery).
My problem is, that I get this error when calling it up on my form:
Info: ERROR APPNAME - Exception [EclipseLink-4002] (Eclipse Persistence Services - 2.6.1.v20150605-31e8258): org.eclipse.persistence.exceptions.DatabaseException
Internal Exception: com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '.ID)) AND (t2.FORM_ID = t1.ID.t1.ID)) AND (t3.DELETED <> 'deleted')) AND ((t2.DE' at line 1
Error Code: 1064
Call:
SELECT t0.ID, t0.FORM_TYPE, t0.BANK_ACCOUNT_NO, t0.BANK_NAME, t0.COMMENT_BOTTOM, t0.COMMENT_UP, t0.COMPANY_ID, t0.CREATED_BY, t0.CREATED_TS, t0.CURRENCY, t0.DAILY_RATE, t0.DELETED, t0.DELIVERY_DATE, t0.DIRECTION, t0.EMAIL_SENT, t0.EU_INTERNAL_BOO, t0.FOREIGN_LANG_BOO, t0.FORM_DATE, t0.FORM_NR, t0.IBAN, t0.MODIFIED_BY, t0.MODIFIED_TS, t0.PAYMENT_BOOKED, t0.PAYMENT_DATE, t0.PAYMENT_TERM, t0.PAYMENT_TYPE, t0.PRINTED_ORIG, t0.REVERSE_CHARGE_BOO, t0.STATE, t0.STATUS, t0.SUBTYPE, t0.SWIFT, t0.TITLE, t0.TYPE, t0.USER_ID, t0.VERSION, t0.ADDRESS_DATA_ID, t0.ADDRESS_ID, t0.CONTACT_ID, t0.CONTAINER_ID_FROM, t0.CONTAINER_ID_TO, t0.FACILITY_ID_FROM, t0.FACILITY_ID_TO, t0.JOB_ID, t0.PARTNER_DATA_ID, t0.PARTNER_ID, t0.RELATED_FORM_ID, t0.WS_ORDER_ID FROM form t0
WHERE (((((((t0.DIRECTION = ?)
AND (t0.FORM_DATE BETWEEN ? AND ?)) AND ((? IS NULL) OR (t0.MODIFIED_TS >= ?))) AND ((? IS NULL) OR (t0.MODIFIED_TS <= ?))) AND (t0.STATUS > ?)) AND t0.ID IN (SELECT t1.ID FROM inventory_log t3, form_item t2, form t1 WHERE ((((((? IS NULL) OR (t2.FACILITY_ID = ?)) AND (t3.FORM_ITEM_ID = t2.ID.t2.ID)) AND (t2.FORM_ID = t1.ID.t1.ID)) AND (t3.DELETED <> ?)) AND ((t2.DELETED <> ?) AND ((t1.DELETED <> ?) AND (t1.FORM_TYPE = ?)))))) AND ((t0.DELETED <> ?) AND (t0.FORM_TYPE = ?))) ORDER BY t0.FORM_DATE DESC
bind => [16 parameters bound]
Relevant part:
You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '.ID)) AND (t2.FORM_ID = t1.ID.t1.ID)) AND (t3.DELETED <> 'deleted')) AND ((t2.DE' at line 1
Can somebody please help me, what could be the problem?
Thank you.
I have a query which count a given code from 4 tables. I tested this query first in postgresql, it worked as I expected so I tried to translate it to JPQL and I got this error :
java.lang.IllegalArgumentException:
org.hibernate.hql.internal.ast.QuerySyntaxException: unexpected end of
subtree [ select ((select count(*) from *.Record r where
r.codeCampaign =?1 ) + (select count(*) from *.AccountAssociationAction aaa where aaa.codeCampaign = ?1) +
(select count(*) from *.CampaignPayment cp where cp.pk.campaignCode = ?1) + (select count(*) from
*.ActionPeriodDepartment apd
where apd.id.codeCampaign = ?1))]
I can't figure out what is wrong and what does Hibernate mean by "unexpected end of subtree"
The postgresql query :
select (select count(*) from account_association_action aaa where
aaa.code_campaign = 'CAMP01') + (select count(*) from _campaign_payment cp
where cp.campaign_code = 'CAMP01') + (select count(*) from record r where
r.code_campaign ='CAMP01') + (select count(*) from action_period_department apd
where apd.code_campaign = 'CAMP01');
the JPQL query :
#Query(value=" select (select count(*) from Record r where r.codeCampaign =?1 ) + " +
" (select count(*) from AccountAssociationAction aaa where aaa.codeCampaign = ?1) +" +
" (select count(*) from CampaignPayment cp where cp.pk.campaignCode = ?1) +" +
" (select count(*) from ActionPeriodDepartment apd where apd.id.codeCampaign = ?1)")
int countCampaignCodeUses(String campaignCode);
It looks like you need to add nativeQuery=true to #Query annotation otherwise JPA is failing to make sense of a query without from
why do this query works directly on postgres database:
select m from medicine_case m WHERE m.id IN (select m.id FROM medicine_case m LEFT OUTER JOIN patient ON m.patient=patient.id ORDER BY patient.surname ASC )
AND in OpenJpa with the exact corresponding typed query:
String sql = " select m from medicine_case m WHERE m.id IN (select m.id FROM medicine_case m LEFT OUTER JOIN "
+ "patient ON m.patient=patient.id ORDER BY patient.surname ASC )";
TypedQuery<MedicineCase> query = persistenceClient.createQueryMC(sql);
setParameter(query);
query.setFirstResult(first);
query.setMaxResults(count);
gives me:
org.apache.openjpa.persistence.ArgumentException: Encountered "m . id IN ( select m . id FROM medicine_case m LEFT OUTER JOIN patient ON" at character 37, but expected: ["(", ")", "*",.... etc etc
why????? it's so strange and makes me crazy!
the code that creates the query from entity manager:
return entityManager.createQuery(sql, MedicineCase.class);
and the code that executes it:
return query.getResultList().iterator();
You're confusing SQL (which is what PostgreSQL expects) and HQL (which is what EntityManager.createQuery() expects).
Those are two different languages. SQL works with tables and columns, whereas JPQL works with JPA entities, fields/properties and associations, and is translated by your JPA implementation into SQL.
If you want to execute SQL, you must use EntityManager.createNativeQuery().
in jpql it becomes a bit different:
String sql = "select m from " + MedicineCase.class.getSimpleName() + " m WHERE m.id IN :mcList";
TypedQuery<MedicineCase> query = persistenceClient.createQueryMC(sql);
query.setParameter("mcList", persistenceClient.executeQueryMCId(persistenceClient.createQueryMCId(createSql()
+ addOrders())));
setParameter(query);
query.setFirstResult(first);
query.setMaxResults(count);
return persistenceClient.executeQueryMC(query);
where createSql returns:
sql.append("select m.id ").append(" FROM ").append(MedicineCase.class.getSimpleName()).append(" m");
and addOrders:
" LEFT OUTER JOIN m.patient p ORDER BY p.surname ASC
I write
String sql = "select candidate_skill.candidate_id from candidate_skill " +
"inner join skill on skill.id = candidate_skill.skill_id " +
"where skill_id in (:skillIdList) group by candidate_skill.candidate_id " +
"Having count(candidate_skill.candidate_id) = (select count(*) from skill where skill.id in (:skillIdList) )";
sql = sql.replace(":skillIdList", generateSkillIdList(skills));
Query query = session.createSQLQuery(sql);
List<Candidate> candidates = query.list();
It works good
second situation:
String sql = "select candidate_skill.candidate_id from candidate_skill " +
"inner join skill on skill.id = candidate_skill.skill_id " +
"where skill_id in :skillIdList group by candidate_skill.candidate_id " +
"Having count(candidate_skill.candidate_id) = (select count(*) from skill where skill.id in :skillIdList )";
Query query = session.createSQLQuery(sql).setParameterList("skillIdList", skills);
List<Candidate> candidates = query.list()
log:
Hibernate: select candidate_skill.candidate_id from candidate_skill inner join skill on skill.id = candidate_skill.skill_id where skill_id in (?, ?) group by candidate_skill.candidate_id Having count(candidate_skill.candidate_id) = (select count(*) from skill where skill.id in ?, ? )
it doesn't works
and third:
String sql = "select candidate_skill.candidate_id from candidate_skill " +
"inner join skill on skill.id = candidate_skill.skill_id " +
"where skill_id in :skillIdList group by candidate_skill.candidate_id " +
"Having count(candidate_skill.candidate_id) = (select count(*) from skill where skill.id in (:skillIdList) )";
Query query = session.createSQLQuery(sql).setParameterList("skillIdList", skills);
List<Candidate> candidates = query.list();
log:
Hibernate: select candidate_skill.candidate_id from candidate_skill inner join skill on skill.id = candidate_skill.skill_id where skill_id in (?, ?) group by candidate_skill.candidate_id Having count(candidate_skill.candidate_id) = (select count(*) from skill where skill.id in (?, ?) )
it works good
P.S. Pay attention to the Brackets around :skillIdList
if I use setParameterList("argument",value) and argument in query 2 times, then first time hibernate substitutes brackets and in second - none
The syntax of IN requires the brackets.
As of why the 3rd example is working, two guesses:
hibernate has a functionality to automatically append missing brackets, but somehow this doesn't extend to subqueries
the sql server executes the sub-select first, and then the other query becomes redundant with the parameters you've passed, and so is not executed.