1

我在使用 mySQL 和 Spring JDBCTemplate 时遇到了一些问题。我有一个 INSERT ... ON DUPLICATE KEY UPDATE 增加一个计数器,但使用 LAST_INSERT_ID() 技巧通过最后生成的 id 在同一查询中返回新值。我将 JDCTemplate.update() 与 PreparedStatementCreator 和 GeneratedKeyHolder 一起使用,但我在 KeyHolder.getKeyList() 中获得了多个条目。当我在 mySQL 客户端中手动运行它时,有 2 行受到影响(当它点击 DUPLICATE KEY UPDATE 时),然后它SELECT LAST_INSERT_ID();给了我正确的值。

更新:看起来 keyList 中的 3 个条目每个都有 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();
4

2 回答 2

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) ...在你的插入语句中添加。

于 2013-12-06T18:00:12.600 回答
0

结果是 mySQL 的 PreparedStatement 运行该查询,并说它返回 3 行。因此 getGeneratedKeys() 调用(委托给 StatementImpl.getGeneratedKeysInternal()),因为它看到返回 3 行,所以获取第一个 LAST_INSERT_ID() 值,然后从那里递增到行数。所以,我必须获取 GeneratedKeyHolder.getKeyList() 并获取第一个条目,并获取它的第一个值,这将是我需要的值。

于 2012-08-03T15:43:05.107 回答