Проблемы с получением JDBCTemplate значения, установленного трюком LAST_INSERT_ID() mySQL

У меня возникли проблемы с использованием mySQL и Spring JDBCTemplate. У меня есть INSERT... ON DUPLICATE KEY UPDATE, который увеличивает счетчик, но использует трюк LAST_INSERT_ID() для возврата нового значения в том же запросе через последний сгенерированный идентификатор. Я использую JDCTemplate.update() с PreparedStatementCreator и GeneratedKeyHolder, но получаю несколько записей в KeyHolder.getKeyList(). Когда я запускаю его вручную в клиенте mySQL, у меня затрагиваются 2 строки (когда он попадает в DUPLICATE KEY UPDATE), а затем SELECT LAST_INSERT_ID(); дает мне правильное значение.

ОБНОВЛЕНИЕ: похоже, что все 3 записи в keyList имеют по 1 записи каждая, и все они являются увеличивающимися значениями. Таким образом, keyList.get(0).get(0) имеет значение, которое должно быть возвращено, а затем keyList.get(0).get(1) является предыдущим значением +1, а затем keyList.get(0).get (2) — предыдущее значение +1. Так, например, если значение, которое должно быть возвращено, равно 4, оно дает мне 4, 5, 6 именно в таком порядке.

Соответствующий код:

CREATE TABLE IF NOT EXISTS `ClickChargeCheck` (
uuid VARCHAR(50), 
count INTEGER Default '0',
CreatedOn Timestamp DEFAULT '0000-00-00 00:00:00',
UpdatedOn Timestamp DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`uuid`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

String CLICK_CHARGE_CHECK_QUERY = "INSERT INTO ClickChargeCheck (uuid,count,CreatedOn) VALUES(?,LAST_INSERT_ID(1),NOW()) ON DUPLICATE KEY UPDATE count = LAST_INSERT_ID(count+1)";
...
...
GeneratedKeyHolder keyHolder = new GeneratedKeyHolder();

centralStatsJdbcTemplate.update(new PreparedStatementCreator() {
    @Override
    public PreparedStatement createPreparedStatement(Connection con) throws SQLException {
        PreparedStatement ps = con.prepareStatement(CLICK_CHARGE_CHECK_QUERY, Statement.RETURN_GENERATED_KEYS);
        ps.setString(1, uuid);
        return ps;
    }}, keyHolder);

int count = keyHolder.getKey().intValue();

person Drizzt321    schedule 03.08.2012    source источник


Ответы (2)


Для меня я столкнулся с той же проблемой. Для INSERT...ON DUPLICATE KEY UPDATE GeneratedKeyHolder генерирует исключение, так как ожидает, что будет только один возвращенный ключ, чего нельзя сказать о MySQL. В основном мне пришлось реализовать новую реализацию KeyHolder, которая может обрабатывать несколько возвращаемых ключей.

public class DuplicateKeyHolder implements KeyHolder {
    private final List<Map<String, Object>> keyList;

    /* Constructors */

    public Number getKey() throws InvalidDataAccessApiUsageException, DataRetrievalFailureException {
        if (this.keyList.isEmpty()) {
            return null;
        }
        Iterator<Object> keyIter = this.keyList.get(0).values().iterator();
        if (keyIter.hasNext()) {
            Object key = keyIter.next();
            if (!(key instanceof Number)) {
                throw new DataRetrievalFailureException(
                        "The generated key is not of a supported numeric type. " +
                        "Unable to cast [" + (key != null ? key.getClass().getName() : null) +
                        "] to [" + Number.class.getName() + "]");
            }
            return (Number) key;
        } else {
            throw new DataRetrievalFailureException("Unable to retrieve the generated key. " +
                    "Check that the table has an identity column enabled.");
        }
    }

    public Map<String, Object> getKeys() throws InvalidDataAccessApiUsageException {
        if (this.keyList.isEmpty()) {
            return null;
        }
        return this.keyList.get(0);
    }

    public List<Map<String, Object>> getKeyList() {
        return this.keyList;
    }
}

Если вы просто используете этот класс вместо GeneratedKeyHolder, все работает. И я обнаружил, что вам даже не нужно добавлять ... ON DUPLICATE KEY UPDATE id = LAST_INSERT_ID(id) ... в оператор вставки.

person bluecollarcoder    schedule 06.12.2013

Оказывается, что PreparedStatement mySQL выполняет этот запрос и говорит, что возвращает 3 строки. Таким образом, вызов getGeneratedKeys() (делегированный в StatementImpl.getGeneratedKeysInternal()), который, поскольку он видит 3 возвращенные строки, захватывает первое значение LAST_INSERT_ID(), а затем увеличивает его до количества строк. Итак, мне нужно будет захватить GeneratedKeyHolder.getKeyList() и захватить первую запись, и получить ее первое значение, и это будет значение, которое мне нужно.

person Drizzt321    schedule 03.08.2012