Использование держателя ключей Spring с программно сгенерированными первичными ключами

Я использую Spring NamedParameterJdbcTemplate для выполнения вставки в таблицу. Таблица использует NEXTVAL в последовательности для получения первичного ключа. Затем я хочу, чтобы этот сгенерированный идентификатор был передан мне. Я использую реализацию KeyHolder Spring следующим образом:

KeyHolder key = new GeneratedKeyHolder();
jdbcTemplate.update(Constants.INSERT_ORDER_STATEMENT, params, key);

однако, когда я запускаю этот оператор, я получаю:

org.springframework.dao.DataRetrievalFailureException: The generated key is not of a supported numeric type. Unable to cast [oracle.sql.ROWID] to [java.lang.Number]
    at org.springframework.jdbc.support.GeneratedKeyHolder.getKey(GeneratedKeyHolder.java:73)

есть идеи, что мне не хватает?

4 ответов


Я думаю, что вы используете неправильный метод на JdbcTemplate. Единственный из update методы, которые, казалось бы, соответствуют вашему фрагменту кода

int update(String sql, Object... args)

если это так, вы передаете params и key как два элемента массива варгс, а JdbcTemplate лечение key как обычные параметры привязки и неверная интерпретация.

единственная общественная update метод on JdbcTemplate что происходит KeyHolder и

int update(PreparedStatementCreator psc, KeyHolder generatedKeyHolder)

таким образом, вам нужно будет перефразировать свой код использовать это.


просто решил аналогичную проблему - с Oracle вам нужно использовать другой метод (от NamedParameterJdbcOperations) -

int update(String sql,
           SqlParameterSource paramSource,
           KeyHolder generatedKeyHolder,
           String[] keyColumnNames)
           throws DataAccessException

С keyColumnNames, содержащими автоматически сгенерированные столбцы, в моем случае просто ["Id"]. В противном случае все, что вы получаете, это ROWID. См.Spring doc для сведения.


вы должны выполнить JdbcTemplate.update(PreparedStatementCreator p, KeyHolder k).

ключ, возвращенный из базы данных, будет введен в KeyHolder объект parameter.

пример:

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);

более подробную информацию можно найти здесь в справочной документации.


нет подробного ответа на @konstantin: вот полностью рабочий пример: Предполагая, что база данных-Oracle, а имя столбца, в котором хранится сгенерированный идентификатор, - "GENERATED_ID" (может быть любым именем). Примечание: я использовал NamedParameterJdbcTemplate.обновление.(...) В этом примере не jdbctemplate класса 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 )
                {
        }
        }