SQL state [99999]; error code [17004]; Invalid column type; nested
exception is java.sql.SQLException: Invalid column
type\",\"error\":\"UncategorizedSQLException\"}"}
My case:
In our DB one user will have one Id but this one Id can can have multiple values. for example I have one userId, for my userID I have 10 health records. so I have to delete these 10 health records in one shot. so I'm passing userId and List for values(data type is Number). raw query works but when I go through java code it is giving Invalid column exception. any suggestions?
Java Implementation method is to make this call is
#Override
public void deleteSampleValue(BiometricPkDTO biometricPkDTO){
update(deleteSampleValueSql,log,biometricPkDTO.getSeriesPk(),biometricPkDTO.getSamplePks());
}
and the SQL query I added in resource folder is
delete from bio_sample
where BIO_SERIES_PK = ?
and BIO_SAMPLE_PK in (?)
Thanks.
As I'm performing multiple deletes action for one specific Id, it is suggested to use NamedParameterJdbcTemplate(getJdbcTemplate().getDataSource()). Instantiating MapSqlParameterSource object and by using the reference variable pass the inputs to sql query. The final Dao implementation method looks like:
#Override
public void deleteSample(BiometricPkDTO biometricPkDTO){
MapSqlParameterSource parameterSource = new MapSqlParameterSource();
parameterSource.addValue("seriesPk",biometricPkDTO.getSeriesPk());
parameterSource.addValue("samplePks", biometricPkDTO.getSamplePks());
namedParameterJdbcTemplate.update(deleteSampleSql,parameterSource);
}
Related
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.
I am very beginner to Spring - JDBC .
I am trying to retrieve the employee_id from a table using the query having bind variables and also with IN condition in it .
I'm getting SQLException that
" invalid column type" - Caused by:
org.springframework.jdbc.UncategorizedSQLException:
PreparedStatementCallback; uncategorized SQLException for SQL [select
employee_id from table_employee where age=:varTwo and marks in
(:varOne) and name =:varThree]; SQL state [99999]; error code [17004];
Invalid column type; nested exception is java.sql.SQLException:
Invalid column type
Can you please tell , where I'm wrong .
I have tried using the types as Long , Integer , String but still i'm getting "invalid column type"
age is - NUMBER
marks is - NUMBER
name is - VARCHAR
MapSqlParameterSource parameters = new MapSqlParameterSource();
parameters.addValue("varOne", varOne);
parameters.addValue("varTwo", Long.parseLong(varTwo));
parameters.addValue("varThree", varThree);
Long employeeId = jdbcTemplate.queryForObject("select employee_id from table_employee where age=:varTwo and marks in (:varOne) and name =:varThree" , Long.class , parameters);
I should be getting the result of this SQL as the "employee id".
I think the type of varOne may be Collection.
When you want to use a variable in SQL query especially with IN, you should make sure the variable is a correct type.
Thanks for your responses. I was able to proceed further by making the list to string using join method.
I have a table issue_assigned having user_id as Integer and issue number as Varchar. The issues can be assigned to multiple users.
Now there is a condition if the issue is previously assigned to 3 users and then admin wants to assign it only to 2 users, The system should delete the 3rd user entry for that I wrote following code
String hql = "delete from issueAssigned where issueNumber=:issueNum AND assignedToUserId not in (:userIds)";
Query query = getHibernateTemplate().getSessionFactory().getCurrentSession().createQuery(hql);
query.setParameter("issueNum", issueNum);
query.setParameter("userIds", userIds);
if(query.executeUpdate() > 0){
return "success";
}else{
return "error";
}
userIds is a string which contain userId seperated by comma(,). When executed I get String cannot be caste to integer error.
How can i keep the required users and delete rest? What changes I will have to do?
userIds is a string which contain userId seperated by comma(,)
It shouldn't be. You should just pass an array or collection of user IDs. Hibernate will create the corresponding SQL prepared statement for you, by adding a parameter placeholder for each user ID.
And the parentheses around :userIds aren't needed either.
Instead of setParameter which take only one parameter you have to use setParameterList which take a Collection of parameters not String :
query.setParameterList("userIds", userIds);
Your query will be
String hql = "delete from issue_assigned where issue_number=:issueNum AND assigned_to_user_id not in :userIds";
and use query.setParameterList as follows
query.setParameterList("userIds", userIds);
hibernate will create the prepared statement using the collection passed.
I'm having trouble when trying to run the following query against an in memory H2 (version 1.4.181) table:
Object result = hibernateSession
.createSQLQuery("show columns from :myTable")
.setString("myTable", "some_table")
.list();
This query causes the following exception:
Caused by: org.h2.jdbc.JdbcSQLException: Syntax error in SQL statement "SHOW COLUMNS FROM ?[*] "; expected "identifier"; SQL statement: show columns from ? [42001-181]
...
...
...
I had done some debbuging and I found that during parse of query, the character "?" is tested to check if it is a valid identififer and it fails, causing the rise of exception (class org.h2.command.Parser, line 3027):
//currentToken is "?" at this point
if (currentTokenType != IDENTIFIER) {
throw DbException.getSyntaxError(sqlCommand, parseIndex,
"identifier");
}
I think it is a bug. What you think?
No, it is quite normal. Hibernate could not possibly make a PreparedStatement of it.
Standard JDBC has many possibilities to query schemata and such, in a database vendor independant way.
DatabaseMetaData dbMeta = connection.getMetaData();
Then getColumns can be used to receive a ResultSet of miscellaneous information.
You can try creating the required query instead of setting table name as named-parameter which won't work.
String sqlQuery = "show columns from " + tableName;
Class<?> entity = Class.forName(entityName);
session.createSQLQuery(sqlQuery);
Get the metadata information & then can retrieve required details from it.
String[] properties =
sessionFactory.getClassMetadata(entityClass).getPropertyNames();
There are several other methods available to get meta information, can refer ClassMetaData
[I haven't checked Criteria API, will update if found anything relevant, you can try it]
I have an auto generated timestamp that is created each time a record is inserted or updated in a mysql table. Is there a way to return this timestamp in a way similar to how I would use a keyholder to return a newly created id?
KeyHolder keyHolder = new GeneratedKeyHolder();
JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
//Insert Contact
jdbcTemplate.update(new PreparedStatementCreator() {
#Override
public PreparedStatement createPreparedStatement(Connection connection) throws SQLException {
PreparedStatement preparedStatement = connection.prepareStatement(SQL_ADD, Statement.RETURN_GENERATED_KEYS);
preparedStatement.setString(1, contact.getFirstName());
preparedStatement.setString(2, contact.getLastName());
preparedStatement.setInt(3, contact.getOrganizationId());
preparedStatement.setString(4, contact.getType());
preparedStatement.setInt(5, contact.getUserId());
return preparedStatement;
}
}, keyHolder);
//Use keyholder to obtain newly created id
contact.setId(keyHolder.getKey().intValue());
Is there some way to also return the new timestamp without having to requery the table? I have been looking for ways to return it along with the id as a key in the keyholder, but it doesn't seem to be returned as a key?
Not very satisfying, but I think "no" is the answer to your question. I don't know any of the Spring stuff, but I think this is due to the basic JDBC that it's wrapping. See http://docs.oracle.com/javase/6/docs/api/java/sql/Statement.html#getGeneratedKeys%28%29
You only option would be to create a stored procedure on MySQL that has an out parameter and call that. See http://dev.mysql.com/doc/refman/5.0/en/call.html.
There are few options for solving this issue on the MySQL database server side. You could start with creating a TRIGGER on the table. As TRIGGER has a restriction and cannot return data, you can set the TIMESTAMP value to a variable:
DEMILITER //
CREATE TRIGGER ai_tbl_name AFTER INSERT ON tbl_name
FOR EACH ROW
BEGIN
SET #TimeStamp = NEW.timestamp_column;
END;//
DELIMITER ;
To retrieve this timestamp value, run the following command:
SELECT #TimeStamp;
Since the variables are stored in the memory, there will be no need to open any tables again.
You go even further. You could create a STORED PROCEDURE in MySQL to automate all the above (sample code, as I do not know your table's details):
DELIMITER //
DROP PROCEDURE IF EXISTS sp_procedure_name //
CREATE PROCEDURE sp_procedure_name (IN col1_val VARCHAR(25),
IN col2_val VARCHAR(25),
IN col3_val INT)
BEGIN
INSERT INTO tbl_name (col1, col2, col3)
VALUES (col1_val, col2_val, col3_val);
SELECT #TimeStamp;
END; //
DELIMITER ;
You can run this procedure with the following code:
CALL sp_procedure_name(col1_val, col2_val, col3_val);
As I'm not familiar with the Java, you'll need to finish it up with your side of code.
It seems that the variable contact is an instance for the newly inserted record. As it contains the newly generated id (primary key) field value, you can execute a new query to return the required timestamp field value for this new id.
The query may look like this:
select timestamp_field from my_table where id=?
Use PreparedStatement to input new id value and execute it to fetch required timestamp field value.
GeneratedKeyHolder also has two methods: getKeyList() that returns Map<String,Object> of generated fields; and getKeyList() that produces a list of generated keys for all affected rows.
See java doc of GeneratedKeyHolder and Spring tutorial of auto generated keys
In addition Spring's SimpleJdbcInsert has methods for generated key retrieval. See also method SimpleJdbcInsert#usingGeneratedKeyColumns
There are 2 methods in java.sql.Connection class causing PreparedStatement execution to return selected key columns :
PreparedStatement prepareStatement(String sql,
int[] columnIndexes)
throws SQLException
PreparedStatement prepareStatement(String sql,
String[] columnNames)
throws SQLException
You don't need to use Spring KeyHolder & JDBCTemplate to do this.
The give hope you could number/name your timestamp column. But the javadoc doesn't require or suggest that any JDBC implementation can return non-key columns, so your out of luck with this approach:
Creates a default PreparedStatement object capable of returning the auto-generated keys
designated by the given array. This array contains the names of the columns in the target
table that contain the auto-generated keys that should be returned.
As suggested in another answer, can switch to a stored procedure that does exactly what you want (CallableStatement is actually a PreparedStatement that executes storedprocedures - i.e. a subclass).
Can populate the timestamp column within the prepared statement via new Timestamp(new Date()) - but you should have in place a mechanism to sync times across your various servers (which is often used in windows and *nix environments). Your trigger could set the timestamp only if a value wasn't already provided.
As part of your app & DB design, you need to commit to a philosophy of where certain operations occur. If the DB derives needed data, the app needs to refresh data - you must pay the price of separate query executions or a combined stored proc that inserts & retrieves.