BadSqlGrammarException: PreparedStatementCallback; bad SQL grammar, when I give a timestamp argument - java

In my Java application, I used to have a sql query such as below :
INSERT INTO "KPI_MEASURE" (
id,
created_at,
kpi_project_id,
kpi_frequency_id,
kpi_metric_id,
branch,
value
)
SELECT
nextval('"KPI_MEASURE_ID_seq"'::regclass),
now(),
kpi_measure.kpi_project_id,
kpi_measure.kpi_frequency_id,
kpi_measure.kpi_metric_id ,
kpi_measure.branch ,
sum(kpi_measure.value)
FROM "KPI_MEASURE" kpi_measure
INNER JOIN "KPI_METRIC" kpi_metric ON kpi_measure.kpi_metric_id = kpi_metric.id
INNER JOIN "KPI_PROJECT" kpi_project ON kpi_measure.kpi_project_id = kpi_project.id
INNER JOIN "KPI_AGGREGATION_PROJECT" kpi_agg_project ON kpi_project.name = kpi_agg_project.child_project_name
WHERE kpi_metric.aggregated = false
GROUP BY kpi_measure.branch, kpi_measure.kpi_metric_id, kpi_measure.kpi_project_id, kpi_project.name, kpi_measure.kpi_frequency_id;
I executed that sql, with jdbcTemplate.update, and it worked.
But recently I changed the value now() by an argument: :today_date, I give that argument with my code:
Map<String, Object> parameters = new HashMap<String, Object>();
parameters.put(TODAY_DATE, today); // TODAY_DATE = today_date
jdbcTemplate.update(sql, parameters);
But now, it creates an error:
org.springframework.jdbc.BadSqlGrammarException: PreparedStatementCallback; bad SQL grammar
...nested exception is org.postgresql.util.PSQLException: No hstore extension installed.
I don't see why it's an error, since it's what I was told to do, when we want to add argument with the jdbc query.
Edit:
The column data type is timestamp without time zone, while the today variable is of String type.
I guess, I should have used Timestamp, so I changed my code:
Map<String, Object> parameters = new HashMap<String, Object>();
parameters.put(TODAY_DATE, new Timestamp(System.currentTimeMillis())); // TODAY_DATE = today_date
jdbcTemplate.update(sql, parameters);
Now I only have the the error
"PSQLException: No hstore extension installed."
Maybe, I need to add an hstore, but I can't seem to understand why.

You are using JdbcTenmplate with named parameters, but JdbcTemplate supports only positioned parameters (?). Replace JdbcTemplate with NamedParameterJdbcTemplate or rewrite your query as:
INSERT INTO "KPI_MEASURE" (
id,
created_at,
kpi_project_id,
kpi_frequency_id,
kpi_metric_id,
branch,
value
)
SELECT
nextval('"KPI_MEASURE_ID_seq"'::regclass),
?,
kpi_measure.kpi_project_id,
kpi_measure.kpi_frequency_id,
kpi_measure.kpi_metric_id ,
kpi_measure.branch ,
sum(kpi_measure.value)
FROM "KPI_MEASURE" kpi_measure
INNER JOIN "KPI_METRIC" kpi_metric ON kpi_measure.kpi_metric_id = kpi_metric.id
INNER JOIN "KPI_PROJECT" kpi_project ON kpi_measure.kpi_project_id = kpi_project.id
INNER JOIN "KPI_AGGREGATION_PROJECT" kpi_agg_project ON kpi_project.name = kpi_agg_project.child_project_name
WHERE kpi_metric.aggregated = false
GROUP BY kpi_measure.branch, kpi_measure.kpi_metric_id, kpi_measure.kpi_project_id, kpi_project.name, kpi_measure.kpi_frequency_id;
and call jdbcTemplate.update(sql, new Timestamp(System.currentTimeMillis()));

Related

java.sql.SQLException: Invalid column type for update query with In clause in jdbcTemplate

I am trying to update the Oracle table row using jdbcTemplate as follows
String sql = "update EVENTS set status = null where TEMP_ID IN (select TEMP_ID from EVENTS where STATUS = 'COMPLETE' and EXCHANGE_ID IN (?) )";
Map<String, Object> paramMap = new HashMap<String, Object>();
List<Long> longValues = new ArrayList<Long>();
longValues.add(1);
longValues.add(2);
paramMap.put("EXCHANGE_ID", longValues);
int rowsAffected = this.jdbcTemplate.update(sql,paramMap,Long.class);
where EXCHANGE_ID is a column in EVENTS table with data type NUMBER(6,0).
When I try to run the above program it is throwing an exception as follows
PreparedStatementCallback; uncategorized SQLException for SQL [update
EVENTS set status = null where TEMP_ID IN (select TEMP_ID from EVENTS
where STATUS = 'COMPLETE' and EXCHANGE_ID= ? )]; SQL state [99999];
error code [17004]; Invalid column type; nested exception is
java.sql.SQLException: Invalid column type
Edit : Number of Parameters in In clause are not fixed. so number of Parameters can be 2 ,3 depending upon the user.
for simplicity I have added following lines in code
longValues.add(1);
longValues.add(2);
In reality I am receiving Parameters from the form. I have just added part of code in my question.
Due to some constraints I can only use ? at my Parameter place in my sql query not :EXCHANGE_ID
Updated: You need to use :EXCHANGE_ID in the SQL sentence:
final String sql = "update EVENTS set status = null where TEMP_ID IN (select TEMP_ID from EVENTS where STATUS = 'COMPLETE' and EXCHANGE_ID = :EXCHANGE_ID)";
final MapSqlParameterSource params = new MapSqlParameterSource();
params.put("EXCHANGE_ID", Long.valueOf(1));
int rowsAffected = this.jdbcTemplate.update(sql, params);

Translating HQL Query to executable SQL Query with list parameter

I am writing a util function to get the total record count based on any HQL that I get passed in without loading all data. The passed in HQL might be pretty complex with lots of selects, joins, where conditions, groupings and sortings. For that I want to wrap the query with a SELECT COUNT(*) FROM (<ORIGINAL_QUERY>) x. I found out, that this is not possible with HQL, because Hibernate does not allow subqueries in FROM elements.
Now, I am trying to translate this random HQL query which might have some named parameters (some of them might be simple parameters, some might be lists) to an executable SQL statement without inlining the parameter values. This seems to work with simple parameters, but does not work with list parameters.
The hqlString and the namedParameterMap comes from somewhere outside:
final String hqlString = "...";
final Map<String, Object> namedParametersMap = ...;
Code to translate HQL to SQL (and ParameterTranslations:
final QueryTranslatorFactory translatorFactory = new ASTQueryTranslatorFactory();
final SessionFactoryImplementor factory = getSessionFactory();
final QueryTranslator translator = translatorFactory.createQueryTranslator(hqlString, hqlString, Collections.EMPTY_MAP, factory, null);
translator.compile(Collections.EMPTY_MAP, false);
final String sqlString = translator.getSQLString();
final ParameterTranslations parameterTranslations = translator.getParameterTranslations());
Code to create a SQLQuery, set the Parameters and execute the Query:
SQLQuery sqlQuery = getCurrentSession().createSQLQuery(sqlString);
((Set<?>) parameterTranslations.getNamedParameterNames()).forEach(parameterName -> {
for (int position : parameterTranslations.getNamedParameterSqlLocations(parameterName)) {
sqlQuery.setParameter(position, namedParametersMap.get(parameterName));
}
});
final Long rowCount = ((BigInteger) query.uniqueResult()).longValueExact();
This query works an expected:
final String hqlString = "SELECT p.firstname, p.surname FROM Person p WHERE p.id = :param1";
final Map<String, Object> namedParametersMap = Maps.newHashMap(ImmutableMap.<String, Object>of("param1", Integer.valueOf(1));
This query does not work:
String hqlString = "SELECT p.firstname, p.surname FROM Person p WHERE p.id IN (:param1)";
final Map<String, Object> namedParametersMap = Maps.newHashMap(ImmutableMap.<String, Object>of("param1", Lists.newArrayList(Integer.valueOf(1)));
Exception:
org.hibernate.exception.SQLGrammarException: could not extract ResultSet at org.hibernate.exception.internal.SQLStateConversionDelegate.convert(SQLStateConversionDelegate.java:106) at org.hibernate.exception.internal.StandardSQLExceptionConverter.convert(StandardSQLExceptionConverter.java:42) at org.hibernate.engine.jdbc.spi.SqlExceptionHelper.convert(SqlExceptionHelper.java:111) at org.hibernate.engine.jdbc.spi.SqlExceptionHelper.convert(SqlExceptionHelper.java:97) at org.hibernate.engine.jdbc.internal.ResultSetReturnImpl.extract(ResultSetReturnImpl.java:79) at org.hibernate.loader.Loader.getResultSet(Loader.java:2313) at org.hibernate.loader.Loader.executeQueryStatement(Loader.java:2096) at org.hibernate.loader.Loader.executeQueryStatement(Loader.java:2072) at org.hibernate.loader.Loader.doQuery(Loader.java:941) at org.hibernate.loader.Loader.doQueryAndInitializeNonLazyCollections(Loader.java:352) at org.hibernate.loader.Loader.doList(Loader.java:2813) at org.hibernate.loader.Loader.doList(Loader.java:2796) at org.hibernate.loader.Loader.listIgnoreQueryCache(Loader.java:2625) at org.hibernate.loader.Loader.list(Loader.java:2620) at org.hibernate.loader.custom.CustomLoader.list(CustomLoader.java:322) at org.hibernate.internal.SessionImpl.listCustomQuery(SessionImpl.java:1996) at org.hibernate.internal.AbstractSessionImpl.list(AbstractSessionImpl.java:322) at org.hibernate.internal.SQLQueryImpl.list(SQLQueryImpl.java:125) at org.hibernate.internal.AbstractQueryImpl.uniqueResult(AbstractQueryImpl.java:966) at HQLQueryUtils.getCount(HQLQueryUtils.java:350)
Caused by: org.postgresql.util.PSQLException: ERROR: operator does not exist: integer = bytea HINT: No operator matches the given name and argument type(s).
You might need to add explicit type casts. Position: 301 at org.postgresql.core.v3.QueryExecutorImpl.receiveErrorResponse(QueryExecutorImpl.java:2433) at org.postgresql.core.v3.QueryExecutorImpl.processResults(QueryExecutorImpl.java:2178) at org.postgresql.core.v3.QueryExecutorImpl.execute(QueryExecutorImpl.java:306) at org.postgresql.jdbc.PgStatement.executeInternal(PgStatement.java:441) at org.postgresql.jdbc.PgStatement.execute(PgStatement.java:365) at org.postgresql.jdbc.PgPreparedStatement.executeWithFlags(PgPreparedStatement.java:155) at org.postgresql.jdbc.PgPreparedStatement.executeQuery(PgPreparedStatement.java:118) at org.hibernate.engine.jdbc.internal.ResultSetReturnImpl.extract(ResultSetReturnImpl.java:70) ... 49 more
Environment:
Java 8
Hibernate 5.1.8
Postgres-JDBC 42.2.2
PostgreSQL Server 10.5
In HQL you can use query parameter and set Collection with setParameterList method.
Query q = session.createQuery("SELECT entity FROM Entity entity WHERE name IN (:names)");
q.setParameterList("names", names);
You can use direct sql
String sql="your query"
Query query = sessionFactory.getCurrentSession().createSQLQuery(sql);
query.setParameter("paramterName", parameterValue);
List<Object[]> resultSet = query.list();
List<YouClass > data= new ArrayList<>();
for (Object[] row : resultSet) {
YouClass yourObject=new YouClass ();
yourObject.setDate((Date) row[0]);
yourObject.setAmount((BigDecimal) row[1]);
data.add(yourObject);
}
Suppose Yourclass has two element date and amount

Parameterized IN clause using multiple columns

I have a query along these lines, where I am trying to filter the result set by comparing tuples (like SQL multiple columns in IN clause):
select *
from mytable
where (key, value) in (values
('key1', 'value1'),
('key2', 'value2'),
...
);
This is valid syntax and works fine on my PostgreSQL 9.3 database.
I want to invoke this query through Spring JDBC where the in value pairs come from a List<Map<String, String>>.
It would be nice to do something like this:
List<Map<String, String>> valuesMap = ...;
String sql = "select * from mytable where (key, value) in (values :valuesMap)";
SqlParameterSource params = new MapSqlParameterSource("valuesMap", valuesMap);
jdbcTemplate.query(sql, params, rowMapper);
When I try this, I get:
org.postgresql.util.PSQLException: No hstore extension installed.
at org.postgresql.jdbc2.AbstractJdbc2Statement.setMap(AbstractJdbc2Statement.java:1707) ~[postgresql-9.3-1101-jdbc41.jar:na]
at org.postgresql.jdbc2.AbstractJdbc2Statement.setObject(AbstractJdbc2Statement.java:1910) ~[postgresql-9.3-1101-jdbc41.jar:na]
at org.postgresql.jdbc3g.AbstractJdbc3gStatement.setObject(AbstractJdbc3gStatement.java:36) ~[postgresql-9.3-1101-jdbc41.jar:na]
at org.postgresql.jdbc4.AbstractJdbc4Statement.setObject(AbstractJdbc4Statement.java:47) ~[postgresql-9.3-1101-jdbc41.jar:na]
at org.springframework.jdbc.core.StatementCreatorUtils.setValue(StatementCreatorUtils.java:427) ~[spring-jdbc-4.2.3.RELEASE.jar:4.2.3.RELEASE]
at org.springframework.jdbc.core.StatementCreatorUtils.setParameterValueInternal(StatementCreatorUtils.java:235) ~[spring-jdbc-4.2.3.RELEASE.jar:4.2.3.RELEASE]
at org.springframework.jdbc.core.StatementCreatorUtils.setParameterValue(StatementCreatorUtils.java:150) ~[spring-jdbc-4.2.3.RELEASE.jar:4.2.3.RELEASE]
at org.springframework.jdbc.core.PreparedStatementCreatorFactory$PreparedStatementCreatorImpl.setValues(PreparedStatementCreatorFactory.java:287) ~[spring-jdbc-4.2.3.RELEASE.jar:4.2.3.RELEASE]
at org.springframework.jdbc.core.PreparedStatementCreatorFactory$PreparedStatementCreatorImpl.createPreparedStatement(PreparedStatementCreatorFactory.java:244) ~[spring-jdbc-4.2.3.RELEASE.jar:4.2.3.RELEASE]
at org.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:623) ~[spring-jdbc-4.2.3.RELEASE.jar:4.2.3.RELEASE]
I've looked at the the hstore extension it mentions. It doesn't seem relevant to my problem.
Is there a way to accomplish this without dynamically building the SQL and parameter list?
All you have to do is to pass a list of arrays, where each array contains a key and value, like this:
HashMap<String , String > map = new HashMap<>();
map.put("key0", "value0");
map.put("key1", "value1");
Set<String> keys = map.keySet();
List<String[]> valuesMap = new ArrayList<>();
for(String key:keys){
String[] entry = {key,map.get(key)};
valuesMap.add(entry);
}
String sql = "select * from mytable where (key, value) in (values :valuesMap)";
SqlParameterSource params = new MapSqlParameterSource("valuesMap", valuesMap);
jdbcTemplate.query(sql, params, rowMapper);
This is mentioned in the Spring documentation: http://docs.spring.io/spring-framework/docs/current/spring-framework-reference/html/jdbc.html#jdbc-in-clause
Unfortunately, there isn't any easy way to bind a nested collection bind variable to PostgreSQL. You could generate the following SQL string instead
SELECT *
FROM mytable
WHERE (key, value) IN (
(?, ?),
(?, ?),
...
);
That's a bit of work to keep the SQL string and the variable bindings in sync.
You could, however, encode the map as JSON as such:
SELECT *
FROM mytable
WHERE (key, value) IN (
SELECT
t->>'key',
t->>'value'
FROM json_array_elements(?) AS t(v)
)
E.g.
SELECT *
FROM mytable
WHERE (key, value) IN (
SELECT
t->>'key',
t->>'value'
FROM json_array_elements(
'[{"key":"key1","value":"value1"},
{"key":"key2","value":"value2"}]'
) AS t(v)
)
In that case, you would only ever need a single VARCHAR bind variable
If you can't get your solution to work, you could also just concatenate the key and value.
Perhaps JDBC has less problems with this more basic syntax:
select *
from mytable
where (key||value) in (
('key1value1'),
('key2value2'),
...
);
For this to work, you'd need to first convert your Java Map to a List with the key and values concatenated as well.
It might not be an issue with the query, it might be that you don't have any hstore created/installed.
I suggest the following steps to debug your problem:
Try a very simple query, without any parameters.
If you get the same issue, check how to create extensions: http://www.postgresql.org/docs/9.1/static/sql-createextension.html
Otherwise, if the query executed correctly, try to use a simple parameter (with =?)
Finally, try named queries. Something like:
ParsedSql parsedSql = NamedParameterUtils.parseSqlStatement(namedSql);
List<Integer> parameters = new ArrayList<Integer>();
for (A a : paramBeans)
parameters.add(a.getId());
MapSqlParameterSource parameterSource = new MapSqlParameterSource();
parameterSource.addValue("placeholder1, parameters);
// create SQL with ?'s
String sql = NamedParameterUtils.substituteNamedParameters(parsedSql, parameterSource);
return sql;
Also check this discussion, I find it useful: How to execute IN() SQL queries with Spring's JDBCTemplate effectivly?

Is there a way to extract primary key(or ROWID) using NamedParameterJdbcTemplate and GeneratedKeyHolder?

I am trying to extract ROWID or the primary key using Spring's NamedParameterJdbcTemplate and GeneratedKeyHolder.
I am trying to do something like this.
MapSqlParameterSource parameters = new MapSqlParameterSource()
.addValue("param1", value1)
.addValue("param2", value2);
KeyHolder keyHolder = new GeneratedKeyHolder();
namedParameterJdbcTemplate.update("INSERT INTO TABLE(ID, col1, col2)"
+ "VALUES(TABLE.TABLE_SEQ.NEXTVAL, :param1, :param2)",
parameters, keyHolder);
After executing above query when I try to do keyHolder.getKey().longValue() it is throwing below exception.
HTTP Status 500 - Request processing failed; nested exception is org.springframework.dao.DataRetrievalFailureException: The generated key is not of a supported numeric type. Unable to cast [oracle.sql.ROWID] to [java.lang.Number]
When I went through this http://docs.oracle.com/cd/B28359_01/java.111/b31224/datacc.htm I understand (i hope i did) that ojdbc is not mapping oracle RowId to java RowId.
Can any one suggest is there any way to extract the key? (Yes it can be done using PreparedStatement but it is making my code bit ugly to read and manipulate on some conditions). Your suggestions are much appreciated.
You have to use this
namedParameterJdbcTemplate.update("INSERT INTO TABLE(ID, col1, col2)"
+ "VALUES(TABLE.TABLE_SEQ.NEXTVAL, :param1, :param2)",
parameters, keyHolder, new String[]{"ID"});
Here is a fully working example: Assuming Database is Oracle and column name which store generated Id is "GENERATED_ID" ( Can be any name)
public Integer insertRecordReturnGeneratedId(final MyObject obj)
{
final String INSERT_QUERY = "INSERT INTO MY_TABLE VALUES(GENERATED_ID_SEQ.NEXTVAL, :param1, :param2)";
try
{
MapSqlParameterSource parameters = new MapSqlParameterSource().addValue( "param1", obj.getField1() ).addValue( "param2", obj.getField1() ) ;
final KeyHolder holder = new GeneratedKeyHolder();
this.namedParameterJdbcTemplate.update( INSERT_QUERY, parameters, holder, new String[] {"GENERATED_ID" } );
Number generatedId = holder.getKey();
// Note: USING holder.getKey("GENERATED_ID") IS ok TOO.
return generatedId.intValue();
}
catch( DataAccessException dataAccessException )
{
}
}

Order by attribute of foreign entity in ORMLite

How can I build a query in ORMLite so that I can use the orderBy function (using either the one with the raw string or the parametrized one) referencing an attribute of a different entity than the one of the dao I'm building the query from? My query is built like that:
// Inner query for performances
QueryBuilder<Performance, String> performancesQB = performanceDao.queryBuilder();
performancesQB.selectColumns("performance_id");
SelectArg performanceSelectArg = new SelectArg();
performancesQB.where().lt("date", performanceSelectArg);
// Outer query for Order objects, where the id matches in the performance_id
// from the inner query
QueryBuilder<Order, String> ordersQB = orderDao.queryBuilder();
ordersQB.where().isNull("user_id").and().in("performance_id", performancesQB);
ordersQB.orderByRaw("performances.date DESC");
pastOrdersQuery = ordersQB.prepare();
And the exception I'm getting whenever I try to execute this query is:
android.database.sqlite.SQLiteException: no such column: performances.date:,
while compiling: SELECT * FROM `orders` WHERE
(`user_id` IS NULL AND `performance_id` IN
(SELECT `performance_id` FROM `performances` WHERE `date` < ? ) )
ORDER BY performances.date DESC
The only solution I see here is writing a raw query myself using a JOIN instead of a nested select. May this be a good solution?
ORMLite now supports simple JOIN queries. Here the docs on the subject:
http://ormlite.com/docs/join-queries
So your query would now look something like:
QueryBuilder<Performance, String> performancesQB = performanceDao.queryBuilder();
SelectArg performanceSelectArg = new SelectArg();
performancesQB.where().lt("date", performanceSelectArg);
performancesQB.orderBy("date", false);
// query for Order objects, where the id matches
QueryBuilder<Order, String> ordersQB = orderDao.queryBuilder();
ordersQB.join(performancesQB).where().isNull("user_id");
pastOrdersQuery = ordersQB.prepare();

Categories