Использование Spring KeyHolder с программно сгенерированными первичными ключами

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

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)

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


person sma    schedule 12.05.2010    source источник


Ответы (5)


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

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

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

Единственный общедоступный метод update для JdbcTemplate, который принимает KeyHolder, это

int update(PreparedStatementCreator psc, KeyHolder generatedKeyHolder)

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

person skaffman    schedule 12.05.2010
comment
вводит в заблуждение, проверьте ниже ответ Константина - person Konstantin Petrukhnov; 28.06.2012

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

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

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

person Konstantin    schedule 16.03.2011
comment
Спасибо, этот ответ на вопрос лучше, чем предыдущие ответы, а также решить мою проблему. - person Sebastien Lorber; 10.01.2012

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

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

Пример:

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

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

person Espen    schedule 12.05.2010
comment
На самом деле это возвращает ROWID в держателе, а не число! - person supernova; 04.08.2016

Никаких подробностей об ответе @konstantin: вот полностью рабочий пример: предполагается, что база данных — это Oracle, а имя столбца, в котором хранится сгенерированный идентификатор, — «GENERATED_ID» (может быть любым именем). ПРИМЕЧАНИЕ. Я использовал NamedParameterJdbcTemplate.update(....) В этом примере НЕ класс 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 )
                {
        }
        }
person supernova    schedule 06.08.2016

С 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;
}
}
person cdesmetz    schedule 22.01.2019