jpa postgresql No Dialect mapping for JDBC type: 1111 - spring

My Spring boot 2.7.5 app with PostgreSQL gives back an error when I try to run a nativeQuery:
#Query(value = """select import, export from
(select cast(max(sd.value) - min(sd.value) as float8)
from solar_data sd
where sd.device_address = :deviceAddress
and sd.tstamp between :from and now()) as import,
(select cast(max(sd.value) - min(sd.value) as float8)
from solar_data sd
where sd.device_address = :deviceAddress
and sd.tstamp between :from and now ()) as export
""", nativeQuery=true)
fun getMainScreenData(#Param("deviceAddress") deviceAddress:Long, #Param("from")
from:LocalDateTime) : List<Impex>
It gives back two floats as import and export.
My result interface is:
interface Impex {
fun getImport():Float
fun getExport():Float
}
Jpa error message is:
org.hibernate.MappingException: No Dialect mapping for JDBC type: 1111
org.hibernate.dialect.TypeNames.get(TypeNames.java:71)
org.hibernate.dialect.TypeNames.get(TypeNames.java:103)
org.hibernate.dialect.Dialect.getHibernateTypeName(Dialect.java:741)
I use dialect as
spring:
jpa:
databasePlatform: org.hibernate.dialect.PostgreSQL10Dialect
I tried casting as float and float8 in sql but the result is same.
How can I execute this native query?

Finally I found it.
The problem was that, postgresql result contains bracket for every value like this:
(3764.81494140625),(2461.824951171875)
That's why Hibernate couldn't parse it. I have to change sql like this:
select ri.val as import,
re.val as export
from
(select max(sd.value) - min(sd.value) as val
from solar_data sd
where sd.device_address=:deviceAddress
and sd.tstamp between :f and now ()
) ri ,
(select max(sd.value) - min(sd.value) as val
from solar_data sd
where sd.device_address=:deviceAddress
and sd.code_id= (select id from codes where code= '1-0:1.8.0')
and sd.tstamp between :f and now ()
) re

Related

SpringBootTest in error with h2 v2.1.214 because of PageRequest

I have some tests in error after upgrading from Spring boot 2.5.6 to 2.7.3.
For information we use Oracle for the database and h2 for tests.
I have some tests in failure with the following error:
Caused by: org.h2.jdbc.JdbcSQLSyntaxErrorException
In fact, the version of h2 was 1.4.200 before and is 2.1.214 now and a lot of things seem to have changed. The reason of the error is not always the same according to the test in error. Sometimes it is an error with a table not found (not solved yet), sometimes it is an error with "Values of types "BOOLEAN" and "INTEGER" are not comparable" (solved by updating a query where a comparison was done with a boolean column like this myBoolean = 0 and it has been updated to myBoolean = false) and I also have an error on a query done with a PageRequest.
For this last case, I have a Controller like this:
public Page<MyEntity> doSomething() {
final Sort sort = Sort.by(Order.desc("column1"));
final PageRequest pageRequest = PageRequest.of(0, 1000, sort);
return myEntityRepository.findAll(pageRequest);
}
But I have an error like that:
Caused by: org.h2.jdbc.JdbcSQLSyntaxErrorException: Syntax error in SQL statement "select myentity0_.id as id1_47_, myentity0_.column1 as column1_47_, myentity0_.column2 as column2_47_ from my_table myentity0_ order by myentity0_.column1 desc [*]limit ?"; SQL statement:
select myentity0_.id as id1_47_, myentity0_.column1 as column1_47_, myentity0_.column2 as column2_47_ from my_table myentity0_ order by myentity0_.column1 desc limit ? [42000-214]
at org.h2.message.DbException.getJdbcSQLException(DbException.java:502)
at org.h2.message.DbException.getJdbcSQLException(DbException.java:477)
at org.h2.message.DbException.get(DbException.java:223)
at org.h2.message.DbException.get(DbException.java:199)
at org.h2.message.DbException.getSyntaxError(DbException.java:247)
at org.h2.command.Parser.getSyntaxError(Parser.java:898)
at org.h2.command.Parser.prepareCommand(Parser.java:572)
at org.h2.engine.SessionLocal.prepareLocal(SessionLocal.java:631)
at org.h2.engine.SessionLocal.prepareCommand(SessionLocal.java:554)
at org.h2.jdbc.JdbcConnection.prepareCommand(JdbcConnection.java:1116)
at org.h2.jdbc.JdbcPreparedStatement.<init>(JdbcPreparedStatement.java:92)
at org.h2.jdbc.JdbcConnection.prepareStatement(JdbcConnection.java:288)
at com.zaxxer.hikari.pool.ProxyConnection.prepareStatement(ProxyConnection.java:337)
at com.zaxxer.hikari.pool.HikariProxyConnection.prepareStatement(HikariProxyConnection.java)
at org.hibernate.engine.jdbc.internal.StatementPreparerImpl$5.doPrepare(StatementPreparerImpl.java:149)
at org.hibernate.engine.jdbc.internal.StatementPreparerImpl$StatementPreparationTemplate.prepareStatement(StatementPreparerImpl.java:176)
... 205 more
If I change the Controller like this,the test is in success:
public Page<MyEntity> doSomething() {
List<MyEntity> result = myEntityRepository.findAll();
return new PageImpl<MyEntity>(result);
}
So It seems that the problem was due by the use of PageRequest.
Do you have an idea please?
Java persistence libraries are usually tested only with default Regular mode of H2 and may not work well with other modes.
Oracle doesn't support MySQL/PostgreSQL-style LIMIT, and H2 doesn't allow it in Oracle compatibility mode, but some libraries produce LIMIT instead of standard OFFSET / FETCH for H2.
Spring Data JDBC (spring-data-relational) added support of custom compatibility modes of H2 only about a month ago and version 2.4.3 with this fix isn't released yet.
Hibernate ORM 6.*.* should work well, but Hibernate ORM 5.6.* has a known issue:
https://hibernate.atlassian.net/jira/software/c/projects/HHH/issues/HHH-15318
You can enable LIMIT in Oracle compatibility mode of H2 as a temporary workaround. To do that, you need to execute the following Java code during initialization of your application:
org.h2.engine.Mode mode = org.h2.engine.Mode.getInstance("ORACLE");
mode.limit = true;

Upgraded Spring boot: Now getting "bad SQL grammar" errors from JDBC and JOOQ

So I upgraded Spring Boot starter to 2.3.1 from 2.2.8.
I kept JOOQ at its original 3.11.5 version, as I do not want to go to version 3.12 as it does not support MySQL 5_7 as a free option.
I am now getting bad SQL grammar errors from existing queries, I don't even know where to start debugging this as what library upgrade from the starter is causing the issue?
Full error is
`Caused by: java.sql.SQLSyntaxErrorException: 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 '-1) as `alias_13136459` on \
`ideas_service`.`challenge`.`id` = `id_alias` left ou' at line 1
at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:120)
at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:97)
at com.mysql.cj.jdbc.exceptions.SQLExceptionsMapping.translateException(SQLExceptionsMapping.java:122)
at com.mysql.cj.jdbc.ClientPreparedStatement.executeInternal(ClientPreparedStatement.java:953)
at com.mysql.cj.jdbc.ClientPreparedStatement.execute(ClientPreparedStatement.java:370)
at com.zaxxer.hikari.pool.ProxyPreparedStatement.execute(ProxyPreparedStatement.java:44)
at com.zaxxer.hikari.pool.HikariProxyPreparedStatement.execute(HikariProxyPreparedStatement.java)
at org.jooq.tools.jdbc.DefaultPreparedStatement.execute(DefaultPreparedStatement.java:209)
at org.jooq.impl.Tools.executeStatementAndGetFirstResultSet(Tools.java:3483)
at org.jooq.impl.AbstractResultQuery.execute(AbstractResultQuery.java:268)
at org.jooq.impl.AbstractQuery.execute(AbstractQuery.java:350)
... 122 common frames omitted`
Here is the query that gives bad SQL error:
public List<ChallengeDetails> findAllChallengesDetails(
#NonNull Integer offset, #NonNull Integer limit, Integer competitionId) {
log.info(
"Find ChallengeEntity(s) (join ChallengeQuestionsEntity) with offset: {}, limit: {}, competitionId: {}",
offset,
limit,
competitionId);
var challengeIdAlias = CHALLENGE.ID.as("id_alias");
var filterSelect =
dsl.select(challengeIdAlias)
.from(CHALLENGE)
.where(findAllChallengesCondition(competitionId))
.orderBy(CHALLENGE.NAME.asc())
.limit(limit)
.offset(offset);
Result<Record> result =
dsl.select(CHALLENGE.fields())
.select(CHALLENGE_QUESTIONS.fields())
.from(CHALLENGE)
.innerJoin(filterSelect)
.on(CHALLENGE.ID.eq(challengeIdAlias))
.leftOuterJoin(CHALLENGE_QUESTIONS)
.on(CHALLENGE.ID.eq(CHALLENGE_QUESTIONS.CHALLENGE_ID))
.orderBy(CHALLENGE.ID.asc(), CHALLENGE_QUESTIONS.ID.asc())
.fetch();
return intoChallengeDetailsGroups(result)
.entrySet()
.stream()
.map(entry -> new ChallengeDetails(entry.getKey(), entry.getValue()))
.collect(toList());
}
private Condition findAllChallengesCondition(Integer competitionId) {
Condition condition = CHALLENGE.TENANT.eq(getCurrentTenant());
if (competitionId != null) {
condition = condition.and(CHALLENGE.COMPETITION_ID.eq(competitionId));
}
return condition;
}
The error can be reproduced in MySQL directly:
select *
from (select 1 a limit 2, -1) t
It produces:
[1064] [42000]: 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 '-1) t' at line 2
You shouldn't pass negative numbers to LIMIT or OFFSET

Write custom H2 DB function Java

I am trying to run the below code using H2DB (via junit test), while doing so i get error message as below. I understand that, there are no function available as "days" in H2. So i am trying to write a custom function, but it does not work out, can any one help on writing this function.
SQLBuilder class code:
public String dummy() {
return new StringBuilder(new SQL() {
{
SELECT("date(CREATE_TMS)");
SELECT("CASE WHEN date(CREATE_TMS) >= (CURRENT DATE - cast('1' AS integer) days) THEN 'Y' ELSE 'N' END NEW_B");
FROM("Q.DUMMY");
}
}.toString().concat(" FOR READ ONLY WITH UR")).toString();
}
Error message:
org.springframework.jdbc.BadSqlGrammarException:
### Error querying database. Cause: org.h2.jdbc.JdbcSQLSyntaxErrorException: Syntax error in SQL statement "SELECT DATE(CREATE_TMS), CASE WHEN DATE(CREATE_TMS) >= (CURRENT DATE - CAST('1' AS INTEGER) DAYS[*]) THEN 'Y' ELSE 'N' END NEW_BILLING
FROM Q.DUMMY FOR READ ONLY WITH UR "; expected "[, ::, *, /, %, +, -, ||, ~, !~, NOT, LIKE, ILIKE, REGEXP, IS, IN, BETWEEN, AND, OR, ,, )"; SQL statement:
SELECT date(CREATE_TMS), CASE WHEN date(CREATE_TMS) >= (CURRENT DATE - cast('1' AS integer) days) THEN 'Y' ELSE 'N' END NEW_BILLING
FROM Q.DUMMY FOR READ ONLY WITH UR [42001-199]
For some reason days are converted to DAYS[*], we can see that in error message.
Customer method i tried in schema-db2.sql:
drop ALIAS if exists days;
CREATE ALIAS days as '
import java.lang.String;
#CODE
java.lang.String days() throws Exception {
return "days";
}
';
applicaiton.properties:
spring.datasource.url=jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=false;Mode=DB2
DAYS is not a function and is not a something that other databases support. Db2 also uses non-standard interval literals.
If you can build H2 from its current sources, you can use cast('1' AS integer) day in it (not the days) and such construction is also supported by Db2. You can also simply use 1 DAY, it is supported by current H2 and Db2 too.
(CURRENT_DAY - 1 DAY)
Sources of H2 are available on GitHub:
https://github.com/h2database/h2database
Building instructions are here:
https://h2database.com/html/build.html#building
You need a jar target.
To compile H2 from the current sources you need JDK 8, 9, 10, 11, or 12. Compiled jar will be compatible with more recent versions.

Spring JPA with Postgres ltree extension

I am trying to make ltree indexing work with Spring JPA. I have an entity that contains the path like this
#Type(type = "com.thomaster.ourcloud.model.filesystem.LTreeType")
#Column(columnDefinition="ltree")
private String relativePath;
The LTreeType class is a copy paste from here Getting error when mapping PostgreSQL LTREE column in hibernate.
The index is properly done in the database, the ltree extension is added. Still when I run the following query
#Query(value = "SELECT * FROM file_system_element WHERE relative_path ~ lquery(:pathToSearch)", nativeQuery = true)
List<FileSystemElement> findAllByPath(#Param("pathToSearch") String pathToSearch);
I get
org.hibernate.MappingException: No Dialect mapping for JDBC type: 1111.
When I change the query to
SELECT * FROM file_system_element WHERE relative_path ~ CAST(:pathToSearch AS lquery)
#OR TO THIS
SELECT * FROM file_system_element WHERE relative_path #> CAST(:pathToSearch AS ltree)
#OR TO THIS
SELECT * FROM file_system_element WHERE relative_path #> ltree(:pathToSearch)
#OR TO THIS
SELECT * FROM file_system_element WHERE ltree(relative_path) ~ lquery(:pathToSearch)
I get the same error.
For
SELECT * FROM file_system_element WHERE relative_path #> lquery(:pathToSearch)
I get
org.postgresql.util.PSQLException: ERROR: operator does not exist: ltree #> lquery
And for:
SELECT * FROM file_system_element WHERE ltree(relative_path) ~ ltree(:pathToSearch)
I get:
ERROR: operator does not exist: ltree ~ ltree
As you can see, I tried almost every combination, and still no result. I also tried the same queries as native queries on the entity manager, but it makes no difference. What am I missing here?
Solved it, it was my mistake. The query parameter was
"folder.anotherFolder"
instead of
"folder.anotherFolder.*"

Spring JDBC lob insertion fails: Invalid column type

The code:
jdbcTemplate.update("MERGE INTO app_role_data x USING (select ? name, ? xml FROM dual) d ON (x.app_name = d.name) WHEN MATCHED THEN UPDATE SET x.xml_blob = d.xml WHEN NOT MATCHED THEN INSERT(app_name, xml_blob) VALUES(d.name, d.xml)",
new AbstractLobCreatingPreparedStatementCallback(lobHandler) {
protected void setValues(PreparedStatement ps, LobCreator lobCreator) throws SQLException {
ps.setString(1, appName);
lobCreator.setClobAsString(ps, 2, xmlBlob);
}
});
lobHandler is an instance of OracleLobHandler that's injected in the context.
The exception:
Caused by: java.sql.SQLException: Invalid column type
at oracle.jdbc.driver.DatabaseError.throwSqlException(DatabaseError.java:112)
at oracle.jdbc.driver.DatabaseError.throwSqlException(DatabaseError.java:146)
at oracle.jdbc.driver.DatabaseError.throwSqlException(DatabaseError.java:208)
at oracle.jdbc.driver.OraclePreparedStatement.setObjectCritical(OraclePreparedStatement.java:9231)
at oracle.jdbc.driver.OraclePreparedStatement.setObjectInternal(OraclePreparedStatement.java:8812)
at oracle.jdbc.driver.OraclePreparedStatement.setObjectInternal(OraclePreparedStatement.java:9534)
at oracle.jdbc.driver.OraclePreparedStatement.setObject(OraclePreparedStatement.java:9517)
at org.springframework.jdbc.core.StatementCreatorUtils.setValue(StatementCreatorUtils.java:351)
at org.springframework.jdbc.core.StatementCreatorUtils.setParameterValueInternal(StatementCreatorUtils.java:216)
at org.springframework.jdbc.core.StatementCreatorUtils.setParameterValue(StatementCreatorUtils.java:144)
at org.springframework.jdbc.core.ArgPreparedStatementSetter.doSetValue(ArgPreparedStatementSetter.java:65)
at org.springframework.jdbc.core.ArgPreparedStatementSetter.setValues(ArgPreparedStatementSetter.java:46)
at org.springframework.jdbc.core.JdbcTemplate$2.doInPreparedStatement(JdbcTemplate.java:815)
at org.springframework.jdbc.core.JdbcTemplate$2.doInPreparedStatement(JdbcTemplate.java:1)
at org.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:586)
This is Spring framework 3.0.5.RELEASE.
Our oracle driver dependency is
<dependency>
<groupId>com.oracle</groupId>
<artifactId>ojdbc14</artifactId>
<version>10.2.0.3.0</version>
</dependency>
The table:
SQL> desc app_role_data
Name Null? Type
----------------------------------------- -------- ----------------------------
APP_NAME VARCHAR2(64)
XML_BLOB CLOB
I'm not even sure what other information I can add, but I'll be happy to do so if I've left something out.
Turns out, execute() takes a PreparedStatementCallback, while update() takes a PreparedStatementSetter. But both have overloads that take Object, so the compiler doesn't complain.
Grumble. That's a day of my life I'll never get back.
I am being a little skeptical about the column names here
Can you try something like below in your MERGE
select ? as name, ? as xml FROM dual
But I am skeptical of that too since the way you can have dynamic column names is by creating a dynamic sql and executing it with EXECUTE IMMEDIATE.
Or may be something like this (not best in terms of Oracle)-
jdbcTemplate.update("MERGE INTO app_role_data x USING dual d ON (x.app_name = ?) WHEN MATCHED THEN UPDATE SET x.xml_blob = ? WHEN NOT MATCHED THEN INSERT(app_name, xml_blob) VALUES(?, ?)",......

Resources