Prepared Statement failing with DB2 SQL error: SQLCODE: -401, SQLSTATE: 42818 - java

I am working on an application which updates data in a database(IBM DB2 v 9.7) via JDBC . Here's The table schema:
Column name Data Type Length
1)INDEX BIGINT -
2)USER_NAME VARCHAR 30
3)SRC VARCHAR 30
4)STATUS VARCHAR 150
5)RT_COUNT BIGINT -
And my Code is as:
String cmd1="Update ANALYTICS SET RT_COUNT = 1 WHERE USER_NAME = ? AND STATUS = ?";
PreparedStatement process=connection.prepareStatement(cmd1);
process.setString(1, Source);
process.setString(2, Content);
if(process.executeUpdate()==0)
{....
But it fails,can anyone help me ?
Thanks and regards

According to IBM's site, the SQLSTATE of 42818 is 42818 "The operands of an operator or function are not compatible or not comparable.". This means you set the wrong data type to your prepared statement.
You might need to bring the RT_COUNT out and do a setInt or setLong or something to make it work.
Also, I am assuming Source and Content are Strings? If they are not, that may contribute to this as well.
Edit
To do the RT_COUNT thing I was talking about you would have to modify your query to do the following:
String cmd1="Update ANALYTICS SET RT_COUNT = ? WHERE USER_NAME = ? AND STATUS = ?";
PreparedStatement process = connection.prepareStatement(cmd1);
process.setLong(1, new Long(1));
process.setString(2, source);
process.setString(3, content);
if(process.executeUpdate()==0)
{....
And that might actually be your problem, cause I'm not sure how the driver is interpreting the "1". It needs to be Long to map to bigint in DB2. See for what data types in Java map to what data types in DB2.

In addition to #Chris Aldrich's answer of casting in Java, if you know what types the parameters should be, you can also cast them in SQL, like so:
UPDATE ANALYTICS
SET RT_COUNT = CAST(? AS BIGINT)
WHERE USER_NAME = CAST(? AS VARCHAR(30))
AND STATUS = CAST(? AS VARCHAR(150))

Related

BadSqlGrammarException with namedParameterJdbcTemplate but not psql

I'm altering a Postgres column's type from UUID to TEXT with
ALTER TABLE my_table ALTER COLUMN id TYPE TEXT;
But I want to ensure my code works with both column types. i.e. As it's difficult to sync up the alteration of the db with the code which runs on a server.
I'm testing this locally by switching between the types. This can be done by moving back to UUID (while the data is in the correct format) with
ALTER TABLE my_table ALTER COLUMN id TYPE UUID USING id::uuid;
This works. But when switched to a TEXT and running my Java code I noticed some failures in the tests. Specifically whenever a WHERE statement makes use of the switched id. e.g.
UPDATE my_table SET name = 'new_name' WHERE id = '00000000-0000-0000-0001-000000000000'::uuid;
generates
ERROR: operator does not exist: text = uuid
That makes sense to me as the namedParameterJdbcTemplate class I'm using is adding a ::uuid to the end of the id in the SQL query. I'm not certain why it bothers doing that but it can be worked around by converting the passed in parameter from a UUID to a string.
However if I switch the column back to UUID I get the following error with
UPDATE my_table SET name = 'new_name' WHERE id = '00000000-0000-0000-0001-000000000000';
generating
ERROR: operator does not exist: text = uuid
I can accept the first update query causing issues because I can run it on the psql command line and get the same error. However this does not happen with the second command in psql.
Furthermore if I stop using the namedParameterJdbcTemplate's parameters and bake the values into the sql string it works.
String sqlStatement = "UPDATE my_table SET name = :v_new_name WHERE id = :v_id";
MapSqlParameterSource sqlParameterMap = new MapSqlParameterSource();
sqlParameterMap.addValue("v_new_name", "New Name");
sqlParameterMap.addValue("v_id", id.toString());
namedParameterJdbcTemplate.update(sqlStatement, sqlParameterMap);
Generates
ERROR: operator does not exist: uuid = character varying
thrown from SQLStateSQLExceptionTranslator.java.
versus
String sqlStatement = "UPDATE my_table SET name = 'New Name' WHERE id = '" + id.toString() + "'";
MapSqlParameterSource sqlParameterMap = new MapSqlParameterSource();
namedParameterJdbcTemplate.update(sqlStatement, sqlParameterMap);
which works.
Why is that the case? It feels like the namedParameterJdbcTemplate is doing some extra type checking that I cannot find.
Frustrated with the idea of having to bake the variables into the SQL statement I took a guess and tried the following
String sqlStatement = "UPDATE my_table SET name = :v_new_name WHERE id = :v_id";
MapSqlParameterSource sqlParameterMap = new MapSqlParameterSource();
sqlParameterMap.addValue("v_new_name", "New Name");
sqlParameterMap.addValue("v_id", id.toString(), java.sql.Types.OTHER);
namedParameterJdbcTemplate.update(sqlStatement, sqlParameterMap);
To my surprised it worked. Before I had experimented with the differences between Types.BINARY and Types.VARCHAR. But I guess I needed to be less specific. The definition of OTHER states
The constant in the Java programming language that indicates that the SQL type is database-specific and gets mapped to a Java object that can be accessed via the methods getObject and setObject.
Which sounds like an appropriate default value but it seems the library does not use it as such.
I do have to note that this only works with id.toString(). Using id by itself leads to a type error.
Thanks to some insights from #a_horse_with_no_name which helped.

ROW_NUMBER() on informix DB shows BIGINT is not supported

I am trying to add row numbers to a sql query to get a return resultset, but the JDBC does not support BIGINT it says. I look up https://db.apache.org/derby/docs/10.9/ref/rreffuncrownumber.html and https://www.ibm.com/support/knowledgecenter/SSGU8G_11.50.0/com.ibm.jdbc_pg.doc/ids_jdbc_141.htm.
The code:
String query = new StringBuilder("SELECT ROW_NUMBER() OVER() AS id, * FROM "+tableName).toString();
Error:
[Informix JDBC Driver][Informix]The data type bigint is not supported for current client/server configuration.
The IBM solution tells you to use getBigSerial() to get the BIGINT after the insert. However, I want to find a way to be able to add some auto increment numbers when it queries the table without creating an actual column. Is there a way?
You can cast it to VARCHAR in the query.
You can cast that String into BigInteger in Java code if you use it for something more than present it to the client.
Casting is good:
String query = "SELECT CAST(ROW_NUMBER() OVER() AS INT) AS id, * FROM "+tableName;
I test it with my informix database and it works.

When is the targetsqltype parameter required is PreparedStatement.setObject

The PreparedStatement has a
setObject(int parameterIndex,Object x,int targetSqlType)
method. When would one ever require the the third parameter? Can you give a concrete example?
I did find one circumstance where this is useful. The sql server driver sends strings as unicode by default to the server. So let's say you have a table:
create table1 (x varchar(128) primary key);
Now I form a sql:
String sql = select * from table1 where x = ?;
PreparedStatement ps = conn.prepareStatment(sql);
ps.setObject(1, "hello");
The string hello gets sent as unicode as if you issued a
select * from table1 where x = N'hello'
However that is not what is wanted because sql server will not use primary key index on x to search. This happens if the MS JDBC driver has setSendStringParametersAsUnicode set to true. If you set the setSendStringParametersAsUnicode to false the above will work but not for unicode. So there you need to do ps.setObject(1, "hello", Types.NVARCHAR).

using java variable in sql statement

I'm trying to build a web page to better learn Java and SQL. My question is, is there a way in Java to make a generic SQL select statement? For example:
SELECT var1 FROM var2 WHERE var3=var4
or something of the sort.
My idea is to fill the vars with user selected items from the web page. I know this can be done in PHP using the Post method, but I'm not using PHP. Also, I've read about the Prepared Statement in Java, but seems only to work when the used after the comparison operator; ex:
SELECT * FROM table Where attr = ? &
Also, I do know i can do the hard coded version of "SELECT " + var1 + "FROM " + var2 + "WHERE attr = " + var3 + " " but that doesn't seem very generic and prone to a lot of errors.
Incase: I'm trying to build this test page using HTML & JSP.
What you are doing with the ? is parameterizing the query. The query can only be parameterized for values not names of tables or columns.
Every time you run a query. The database has to create a query plan. If you are running the same query again and again, you can reduce this overhead by creating a PreparedStatement.
The first execution of PreparedStatement will generate the query plan. The subsequent executions will reuse the same plan.
Same query here means, it is identical in all respects except values used in where clause, expressions etc.
If you change the Column or Table name or modify the structure of the query, then it is a different query and will require a different query plan. A PreparedStement is not useful in this case and you should stick to the hardcoded version you talked about. Because of this reason you will get an error if you try to parameterize Table or Column names in PreparedStement.
Having said that. It is not advisable to take such a generic approach for queries. If your queries are that simple, you can benefit from ORM tools. You would not have to maintain even a line of SQL. For complex queries you have an option of using ORM specific query language or JPQL or Native SQL. Look for JPA + Hibernate
Your specific usage is not permitted by JDBC. You need to hard code the table name when creating the prepared statement. If you really do want to do that I suggest you use String concatenation to create the SQL statements and then create a PreparedStatement with parameters to handle the where part. In case you are wondering why bother with PreparedStatements in the specific solution, it's to avoid SQL injection.
You can use PreparedStatement to achive your objective.
For example -
String query = "SELECT * FROM table Where attr = ?";
PreparedStatement pt = con.prepareStatement(query);
pt.setString(1, attribete);
pt.executeUpdate();
There is no such direct provision in any of SQL packaged classes or others to replace table, column names along with query parameter values, in a query string, using a single method.
You require to depend on both PreparedStatement and any of String methods replace(...) and replaceFirst(...) to achieve your requirement.
String sql = "Select $1, $2 from $3 where $4=? and $5=?";
sql = sql.replaceFirst( "$1", "col1_name" );
sql = sql.replaceFirst( "$2", "col2_name" );
sql = sql.replaceFirst( "$3", "table_name" );
sql = sql.replaceFirst( "$4", "col4_name" );
sql = sql.replaceFirst( "$5", "col5_name" );
// .. and so on
PreparedStatement pst = con.prepareStatement( sql );
// use relevant set methods to set the query parametrs.
pst.setXXX( 1, value_for_first_query_parameter ); // from a variable or literal
pst.setXXX( 2, value_for_second_query_parameter); // from a variable or literal
// ... and so on
If you are using JDBC, can try this
PreparedStatement statement = connection.prepareStatement("SELECT ? FROM ? WHERE ?=? ");
then
statement.setString(1, "column_name");
statement.setString(2, "table_name");
statement.setString(3, "column_name");
statement.setBigDecimal(4, 123);
If you are using other ORM like Hibernate or JPA, I believe there are also ways to do.

Error with simple Parameterized Query - Java/ SQL

Following on from one of my previous questions to do with method design I was advised to implemented my SQL queries as a parameterized query as opposed to a simple string.
I've never used parameterized queries before so I decided to start with something simple, take the following Select statement:
String select = "SELECT * FROM ? ";
PreparedStatement ps = connection.prepareStatement(select);
ps.setString(1, "person");
This gives me the following error: "[SQLITE_ERROR] SQL error or missing database (near "?": syntax error)"
I then tried a modified version which has additional criteria;
String select = "SELECT id FROM person WHERE name = ? ";
PreparedStatement ps = connection.prepareStatement(select);
ps.setString(1, "Yui");
This version works fine, in the my first example am I missing the point of parameterized queries or am I constructing them incorrectly?
Thanks!
Simply put, SQL binds can't bind tables, only where clause values. There are some under-the-hood technical reasons for this related to "compiling" prepared SQL statements. In general, parameterized queries was designed to make SQL more secure by preventing SQL injection and it had a side benefit of making queries more "modular" as well but not to the extent of being able to dynamically set a table name (since it's assumed you already know what the table is going to be).
If you want all rows from PERSON table, here is what you should do:
String select = "SELECT * FROM person";
PreparedStatement ps = connection.prepareStatement(select);
Variable binding does not dynamically bind table names as others mentioned above.
If you have the table name coming in to your method as a variable, you may construct the whole query as below:
String select = "SELECT * FROM " + varTableName;
PreparedStatement ps = connection.prepareStatement(select);
Parameterized queries are for querying field names - not the table name!
Prepared statements are still SQL and need to be constructed with the appropriate where clause; i.e. where x = y. One of their advantages is they are parsed by the RDMS when first seen, rather than every time they are sent, which speeds up subsequent executions of the same query with different bind values.

Categories

Resources