Using Spring's KeyHolder with programmatically-generated primary keys
I am using Spring's NamedParameterJdbcTemplate to perform an insert into a table. The table uses a NEXTVAL on a sequence to obtain the primary key. I then want this generated ID to be passed back to me. I am using Spring's KeyHolder implementation like this:
KeyHolder key = new GeneratedKeyHolder();
jdbcTemplate.update(Constants.INSERT_ORDER_STATEMENT, params, key);
However, when I run this statement, I am getting:
org.springframework.dao.DataRetrievalFailureException: The generated key is not of a supported numeric type. Unable to cast [or开发者_高级运维acle.sql.ROWID] to [java.lang.Number]
at org.springframework.jdbc.support.GeneratedKeyHolder.getKey(GeneratedKeyHolder.java:73)
Any ideas what I am missing?
Just solved a similar issue - with Oracle you need to use another method (from NamedParameterJdbcOperations
) -
int update(String sql,
SqlParameterSource paramSource,
KeyHolder generatedKeyHolder,
String[] keyColumnNames)
throws DataAccessException
with keyColumnNames containing auto-generated columns, in my case just ["Id"]. Otherwise all you get is ROWID. See Spring doc for details.
You have to execute the JdbcTemplate.update(PreparedStatementCreator p, KeyHolder k)
.
The key returned from the database will be injected into the KeyHolder
parameter object.
An example:
final String INSERT_ORDER_STATEMENT
= "insert into order (product_id, quantity) values(?, ?)";
KeyHolder keyHolder = new GeneratedKeyHolder();
jdbcTemplate.update(new PreparedStatementCreator() {
public PreparedStatement createPreparedStatement(
Connection connection) throws SQLException {
PreparedStatement ps = connection.prepareStatement(
INSERT_ORDER_STATEMENT, new String[] { "id" });
ps.setInt(1, order.getProductId());
ps.setInt(2, order.getQuantity());
return ps;
}
}, keyHolder);
More information can be found here in the reference documentation.
No elaborate on @konstantin answer: Here is a fully working example: Assuming Database is Oracle and column name which store generated Id is "GENERATED_ID" ( Can be any name). NOTE: I used NamedParameterJdbcTemplate.update(....) In this example NOT JdbcTemplate class of Spring.
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 )
{
}
}
With MySQL
CREATE TABLE `vets` (
`id` int(4) unsigned NOT NULL AUTO_INCREMENT,
`first_name` varchar(30) DEFAULT NULL,
`last_name` varchar(30) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `last_name` (`last_name`)
) ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=utf8;
public @Data class Vet {
private int id;
private String firstname;
private String lastname;
}
@Repository
public class VetDaoImpl implements VetDao {
/** Logger. */
private static final Logger LOGGER = LoggerFactory.getLogger(VetDaoImpl.class);
private static final String INSERT_VET = "INSERT INTO vets (first_name, last_name) VALUES (:first_name, :last_name)";
@Autowired
private NamedParameterJdbcTemplate namedParameterJdbcTemplate;
@Override
public Number insertVet(final Vet vet) {
MapSqlParameterSource paramSource = new MapSqlParameterSource();
paramSource.addValue("first_name", vet.getFirstname());
paramSource.addValue("last_name", vet.getLastname());
KeyHolder keyHolder = new GeneratedKeyHolder();
int nbRecord = namedParameterJdbcTemplate.update(INSERT_VET, paramSource, keyHolder, new String[] {"id" });
LOGGER.info("insertVet: id ["+keyHolder.getKey()+"]");
return nbRecord;
}
}
I think you're using the wrong method on JdbcTemplate
. The only one of the update
methods that would seem to match your code fragment is
int update(String sql, Object... args)
If so, you're passing params
and key
as a two-element vargs array, and JdbcTemplate
is treating key
as a normal bind parameters, and mis-interpreting it.
The only public update
method on JdbcTemplate
that takes a KeyHolder
is
int update(PreparedStatementCreator psc, KeyHolder generatedKeyHolder)
So you'll need to rephrase your code to use that.
精彩评论