If I create a table like this:
create table standard (
id NUMBER GENERATED by default on null as IDENTITY PRIMARY KEY,
text varchar2(200)
)
I can insert some data and get the generated id back with a method like this:
Object insertAndReturnStandardId() {
GeneratedKeyHolder keyHolder = new GeneratedKeyHolder();
template.update("insert into standard (text) values('test text')",new MapSqlParameterSource(), keyHolder, new String[]{"id"});
return keyHolder.getKey();
}
template
here is a NamedParameterJdbcTemplate
, but since there are no parameters it should be equivalent to a similar call to JdbcTemplate
.
But if I use a slightly different table with a lower case column name for the id:
create table lowercase (
"id" NUMBER GENERATED by default on null as IDENTITY PRIMARY KEY,
text varchar2(200)
)
Passing the column name id
fails:
Object insertAndReturnLowerCaseId() {
GeneratedKeyHolder keyHolder = new GeneratedKeyHolder();
template.update("insert into lowercase (text) values('test text')",new MapSqlParameterSource(), keyHolder, new String[]{"id"});
return keyHolder.getKey();
}
With (full call stack below)
PreparedStatementCallback; bad SQL grammar [insert into lowercase (text) values('test text')]; nested exception is java.sql.SQLSyntaxErrorException: ORA-00904: "ID": invalid identifier
Passing the column name quoted "id"
fails as well:
private Object insertAndReturnLowerCaseId() {
GeneratedKeyHolder keyHolder = new GeneratedKeyHolder();
template.update("insert into lowercase (text) values('test text')",new MapSqlParameterSource(), keyHolder, new String[]{"\"id\""});
return keyHolder.getKey();
}
With (full call stack below)
PreparedStatementCallback; uncategorized SQLException for SQL [insert into lowercase (text) values('test text')]; SQL state [99999]; error code [17068]; Invalid argument(s) in call; nested exception is java.sql.SQLException: Invalid argument(s) in call
So: How do I get the generated value from an Oracle database when the id column is lower case?
A solution based on the NamedParameterJdbcTemplate
or the JdbcTemplate
are preferred, but I take an answer based on plain JDBC just as well.
org.springframework.jdbc.BadSqlGrammarException: PreparedStatementCallback; bad SQL grammar [insert into lowercase (text) values('test text')]; nested exception is java.sql.SQLSyntaxErrorException: ORA-00904: "ID": invalid identifier
at org.springframework.jdbc.support.SQLErrorCodeSQLExceptionTranslator.doTranslate(SQLErrorCodeSQLExceptionTranslator.java:235) ~[spring-jdbc-5.2.6.RELEASE.jar:5.2.6.RELEASE]
at org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:72) ~[spring-jdbc-5.2.6.RELEASE.jar:5.2.6.RELEASE]
at org.springframework.jdbc.core.JdbcTemplate.translateException(JdbcTemplate.java:1443) ~[spring-jdbc-5.2.6.RELEASE.jar:5.2.6.RELEASE]
at org.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:633) ~[spring-jdbc-5.2.6.RELEASE.jar:5.2.6.RELEASE]
at org.springframework.jdbc.core.JdbcTemplate.update(JdbcTemplate.java:893) ~[spring-jdbc-5.2.6.RELEASE.jar:5.2.6.RELEASE]
at org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate.update(NamedParameterJdbcTemplate.java:349) ~[spring-jdbc-5.2.6.RELEASE.jar:5.2.6.RELEASE]
at de.schauderhaft.dbtestclient.DbtestclientApplication.insertAndReturnLowerCaseId(DbtestclientApplication.java:61) ~[classes/:na]
at de.schauderhaft.dbtestclient.DbtestclientApplication.run(DbtestclientApplication.java:45) ~[classes/:na]
at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:795) ~[spring-boot-2.3.0.RELEASE.jar:2.3.0.RELEASE]
... 5 common frames omitted
Caused by: java.sql.SQLSyntaxErrorException: ORA-00904: "ID": invalid identifier
at oracle.jdbc.driver.T4CTTIoer11.processError(T4CTTIoer11.java:509) ~[ojdbc8-19.3.0.0.jar:19.3.0.0.0]
at oracle.jdbc.driver.T4CTTIoer11.processError(T4CTTIoer11.java:461) ~[ojdbc8-19.3.0.0.jar:19.3.0.0.0]
at oracle.jdbc.driver.T4C8Oall.processError(T4C8Oall.java:1104) ~[ojdbc8-19.3.0.0.jar:19.3.0.0.0]
at oracle.jdbc.driver.T4CTTIfun.receive(T4CTTIfun.java:550) ~[ojdbc8-19.3.0.0.jar:19.3.0.0.0]
at oracle.jdbc.driver.T4CTTIfun.doRPC(T4CTTIfun.java:268) ~[ojdbc8-19.3.0.0.jar:19.3.0.0.0]
at oracle.jdbc.driver.T4C8Oall.doOALL(T4C8Oall.java:655) ~[ojdbc8-19.3.0.0.jar:19.3.0.0.0]
at oracle.jdbc.driver.T4CPreparedStatement.doOall8(T4CPreparedStatement.java:270) ~[ojdbc8-19.3.0.0.jar:19.3.0.0.0]
at oracle.jdbc.driver.T4CPreparedStatement.doOall8(T4CPreparedStatement.java:91) ~[ojdbc8-19.3.0.0.jar:19.3.0.0.0]
at oracle.jdbc.driver.T4CPreparedStatement.executeForRows(T4CPreparedStatement.java:970) ~[ojdbc8-19.3.0.0.jar:19.3.0.0.0]
at oracle.jdbc.driver.OracleStatement.doExecuteWithTimeout(OracleStatement.java:1205) ~[ojdbc8-19.3.0.0.jar:19.3.0.0.0]
at oracle.jdbc.driver.OraclePreparedStatement.executeInternal(OraclePreparedStatement.java:3666) ~[ojdbc8-19.3.0.0.jar:19.3.0.0.0]
at oracle.jdbc.driver.T4CPreparedStatement.executeInternal(T4CPreparedStatement.java:1426) ~[ojdbc8-19.3.0.0.jar:19.3.0.0.0]
at oracle.jdbc.driver.OraclePreparedStatement.executeLargeUpdate(OraclePreparedStatement.java:3756) ~[ojdbc8-19.3.0.0.jar:19.3.0.0.0]
at oracle.jdbc.driver.OraclePreparedStatement.executeUpdate(OraclePreparedStatement.java:3736) ~[ojdbc8-19.3.0.0.jar:19.3.0.0.0]
at oracle.jdbc.driver.OraclePreparedStatementWrapper.executeUpdate(OraclePreparedStatementWrapper.java:1063) ~[ojdbc8-19.3.0.0.jar:19.3.0.0.0]
at org.springframework.jdbc.core.JdbcTemplate.lambda$update$1(JdbcTemplate.java:894) ~[spring-jdbc-5.2.6.RELEASE.jar:5.2.6.RELEASE]
at org.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:617) ~[spring-jdbc-5.2.6.RELEASE.jar:5.2.6.RELEASE]
... 10 common frames omitted
Caused by: oracle.jdbc.OracleDatabaseException: ORA-00904: "ID": invalid identifier
at oracle.jdbc.driver.T4CTTIoer11.processError(T4CTTIoer11.java:513) ~[ojdbc8-19.3.0.0.jar:19.3.0.0.0]
... 26 common frames omitted
org.springframework.jdbc.UncategorizedSQLException: PreparedStatementCallback; uncategorized SQLException for SQL [insert into lowercase (text) values('test text')]; SQL state [99999]; error code [17068]; Invalid argument(s) in call; nested exception is java.sql.SQLException: Invalid argument(s) in call
at org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:89) ~[spring-jdbc-5.2.6.RELEASE.jar:5.2.6.RELEASE]
at org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:81) ~[spring-jdbc-5.2.6.RELEASE.jar:5.2.6.RELEASE]
at org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:81) ~[spring-jdbc-5.2.6.RELEASE.jar:5.2.6.RELEASE]
at org.springframework.jdbc.core.JdbcTemplate.translateException(JdbcTemplate.java:1443) ~[spring-jdbc-5.2.6.RELEASE.jar:5.2.6.RELEASE]
at org.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:633) ~[spring-jdbc-5.2.6.RELEASE.jar:5.2.6.RELEASE]
at org.springframework.jdbc.core.JdbcTemplate.update(JdbcTemplate.java:893) ~[spring-jdbc-5.2.6.RELEASE.jar:5.2.6.RELEASE]
at org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate.update(NamedParameterJdbcTemplate.java:349) ~[spring-jdbc-5.2.6.RELEASE.jar:5.2.6.RELEASE]
at de.schauderhaft.dbtestclient.DbtestclientApplication.insertAndReturnLowerCaseId(DbtestclientApplication.java:61) ~[classes/:na]
at de.schauderhaft.dbtestclient.DbtestclientApplication.run(DbtestclientApplication.java:45) ~[classes/:na]
at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:795) ~[spring-boot-2.3.0.RELEASE.jar:2.3.0.RELEASE]
... 5 common frames omitted
Caused by: java.sql.SQLException: Invalid argument(s) in call
at oracle.jdbc.driver.AutoKeyInfo.getNewSql(AutoKeyInfo.java:189) ~[ojdbc8-19.3.0.0.jar:19.3.0.0.0]
at oracle.jdbc.driver.PhysicalConnection.prepareStatement(PhysicalConnection.java:4656) ~[ojdbc8-19.3.0.0.jar:19.3.0.0.0]
at org.springframework.jdbc.core.PreparedStatementCreatorFactory$PreparedStatementCreatorImpl.createPreparedStatement(PreparedStatementCreatorFactory.java:230) ~[spring-jdbc-5.2.6.RELEASE.jar:5.2.6.RELEASE]
at org.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:615) ~[spring-jdbc-5.2.6.RELEASE.jar:5.2.6.RELEASE]
... 10 common frames omitted
Starting in 21c, the Oracle JDBC thin driver supports double-quoted generated keys. The code snippet below works with the 21c and 23c drivers. With 19c it gives an error (Invalid argument(s) in call
). Note that the lowercase
table is the same as the one mentioned in the question.
String QUERY = "INSERT INTO lowercase (text) VALUES (?)";
try (PreparedStatement statement = conn.prepareStatement(QUERY, new String[] { "\"id\"" })) {
statement.setString(1, "Foo");
int affectedRows = statement.executeUpdate();
assert (affectedRows==1);
try (ResultSet keys = statement.getGeneratedKeys()) {
assert(keys.next());
System.out.println("keys.getLong(1)="+keys.getLong(1));
}
// omitted
}