【问题标题】:insert row and get generated ID插入行并获取生成的 ID
【发布时间】:2013-10-06 10:06:17
【问题描述】:

我正在尝试使用 Spring 的 JdbcTemplate 类在名为 transaction 的 MySQL 表中插入一行并获取生成的 ID。相关代码为:

public Transaction insertTransaction(final Transaction tran) {

    // Will hold the ID of the row created by the insert
    KeyHolder keyHolder = new GeneratedKeyHolder();

    getJdbcTemplate().update(new PreparedStatementCreator() {
        public PreparedStatement createPreparedStatement(Connection connection) throws SQLException {

            PreparedStatement ps = connection.prepareStatement(INSERT_TRAN_SQL);
            ps.setString(1, tran.getTransactionType().toString());

            Date sqlDate = new Date(tran.getDate().getTime());
            ps.setDate(2, sqlDate);
            ps.setString(3, tran.getDescription());

            return ps;
        }
    }, keyHolder);

    tran.setId(keyHolder.getKey().longValue());
    return tran;
}

但是调用getJdbcTemplate().update会抛出以下异常

java.sql.SQLException:未请求生成的密钥。 您需要指定 Statement.RETURN_GENERATED_KEYS 以 Statement.executeUpdate() 或 Connection.prepareStatement()。

我可以在不放弃JdbcTemplate的情况下插入行并获取生成的ID吗?我正在使用 Spring 2.5、MySQL 5.5.27 和 MySQL Connector 5.1.26。

【问题讨论】:

  • 对于 MySQL,我认为您需要致电 last_insert_id()。见here。

标签: java jdbctemplate spring-jdbc


【解决方案1】:

只需按如下方式准备您的Statement

PreparedStatement ps = connection.prepareStatement(
                           INSERT_TRAN_SQL, Statement.RETURN_GENERATED_KEYS);

底层 JDBC 驱动程序(此处通过 Spring 的 JdbcTemplate 间接使用)需要提示您要检索生成的密钥。这可以在准备PreparedStatement 时完成

connection.prepareStatement(strSQL, Statement.RETURN_GENERATED_KEYS);

或者,在执行Statement 时

statement.executeUpdate(strSQL, Statement.RETURN_GENERATED_KEYS);

这也是您的java.sql.SQLException 所指的。

【讨论】:

  • 你好,问一个问题,如果查询成功,它会返回生成的ID,但是如果查询执行失败,它会返回什么,以便我可以在我的代码中优雅地处理错误?
【解决方案2】:

有一种更简单的方法来获得这种行为:

protected JdbcTemplate            jdbcTemplate;
private SimpleJdbcInsert          insert;

    this.jdbcTemplate = new JdbcTemplate(this.databaseSetup.getDataSource());
    this.insert = new SimpleJdbcInsert(this.jdbcTemplate).withTableName(this.tableName).usingGeneratedKeyColumns(this.pkColumn);

然后您创建一个名为 parameters 的 Map,其中包含表中每个列名的值并插入如下记录:

    final Map<String, Object> parameters = new HashMap<>();
    parameters.put("empName", employee.getName()); // store the String name of employee in the column empName
    parameters.put("dept", employee.getDepartment()); // store the int (as Integer) of the employee in the column dept
    final Number key = this.insert.executeAndReturnKey(parameters);
    final long pk = key.longValue();

【讨论】:

  • 如果您在答案中提供了映射值的示例,那就太好了。从当前的答案来看,如果不进一步研究外部文档以及 Key 和 Value 所代表的内容,则使用 Map 并不明显。如果该信息可用,这将值得赞成。
  • 我认为很明显,如果我提到包含每个列名的值的 Map 参数,正如我提到的列名,应该很明显键是字符串,而未确定的值可以是任何对象映射到 sql 类型。顺便说一句:检查文档永远不会有什么坏处,永远不要相信任何特定的答案,也不要仔细检查。
  • 这很明显要检查文档而不是相信特定的答案。同样明显的是,关于 Stackoverflow 的一个好问题提供了所有必要的细节,并且不会留下任何歧义。很明显,第一次使用 Java JDBC 堆栈的人不会认为这些类型的事情很明显;)
【解决方案3】:

你可以像第1步那样检索下一个序列号,然后它可以像第2步那样在插入语句中传递:

1-

Integer nextSeq = (Integer) getJdbcTemplate().queryForObject(
        "select SEQ_CUSTOMER_ID.nextVal from dual", new Object[] {}, Integer.class);

2-

getJdbcTemplate().update(
        "INSERT INTO customer "
        + "(CUST_ID, NAME, UPDATED) VALUES (?, ?, ?)",
        new Object[] { nextSeq ,customer.getName(),
                customer.getUpdated() });

【讨论】:

    猜你喜欢
    • 2011-07-21
    • 2011-04-03
    • 2019-08-04
    • 2017-11-05
    • 2010-11-23
    • 2012-01-23
    • 1970-01-01
    • 2012-05-18
    • 1970-01-01
    相关资源
    最近更新 更多