Использование Spring's KeyHolder с программно сгенерированными первичными ключами - PullRequest
8 голосов
/ 12 мая 2010

Я использую Spring's 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)

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

Ответы [ 5 ]

19 голосов
/ 17 марта 2011

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

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

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

2 голосов
/ 12 мая 2010

Вы должны выполнить 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);

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

0 голосов
/ 22 января 2019

с 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;
}
}
0 голосов
/ 06 августа 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 )
                {
        }
        }
0 голосов
/ 12 мая 2010

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

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

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

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

int update(PreparedStatementCreator psc, KeyHolder generatedKeyHolder)

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...