【问题标题】:Getting auto-generated key from row insertion in spring 3 / PostgreSQL 8.4.9在 spring 3 / PostgreSQL 8.4.9 中从行插入中获取自动生成的密钥
【发布时间】:2012-05-22 18:32:21
【问题描述】:

我想从行插入中检索自动生成的 id,但我得到了 NullPointerException

代码如下:

long result = 0;
        final String SQL = "INSERT INTO compte (prenom, nom, datenaissance, numtelephone) "
                            + " VALUES(?,?,?,?)";
        KeyHolder keyHolder = new GeneratedKeyHolder();
        int row= this.jdbcTemplate.update(new PreparedStatementCreator(){
            public PreparedStatement createPreparedStatement(Connection connection)
                throws SQLException {
                PreparedStatement ps =connection.prepareStatement(SQL);
                ps.setString(1, a.getSurname());
                ps.setString(2, a.getName());
                ps.setDate(3, a.getDob());
                ps.setString(4, a.getPhone());
                return ps;
            }
        },keyHolder);

        if (row > 0)
            result = keyHolder.getKey().longValue(); //line 72

这是 PostgreSQL 表:

CREATE TABLE compte
(
  idcompte serial NOT NULL,
  prenom character varying(25) NOT NULL,
  nom character varying(25) NOT NULL,
  datenaissance date NOT NULL,
  numtelephone character varying(15) NOT NULL,
  CONSTRAINT pk_compte PRIMARY KEY (idcompte )
);

PostgreSQL 支持自动生成的密钥,但我得到了这个异常:

java.lang.NullPointerException
    at com.tante.db.JDBCUserAccountDAO.insertAccount(JDBCUserAccountDAO.java:72)

编辑: 我试过这个来获取自动生成的密钥:

result = jdbcTemplate.queryForLong("select currval('compte_idcompte_seq')");

但我得到了PSQLException

the current value (currval) of the sequence compte_idcompte_seq is not defined in this session,虽然我认为compte_idcompte_seq.NEXTVAL 应该在插入行时调用

编辑:

插入行时会正确创建自增值

有什么想法吗?

【问题讨论】:

  • 我很想知道行号 JDBCUserAccountDAO.java:72
  • 第 72 行是哪一行?哪个指针为空?
  • 对不起,第72行指的是keyHolder.getKey().longValue()
  • 但是keyHolder.getKeys().get("idcompte") 也是空的,所以有什么方法可以检索自动生成的密钥?

标签: java spring postgresql jdbc auto-generate


【解决方案1】:
KeyHolder holder = new GeneratedKeyHolder();

getJdbcTemplate().update(new PreparedStatementCreator() {           

                @Override
                public PreparedStatement createPreparedStatement(Connection connection)
                        throws SQLException {
                    PreparedStatement ps = connection.prepareStatement(sql.toString(),
                        Statement.RETURN_GENERATED_KEYS); 
                    ps.setString(1, person.getUsername());
                    ps.setString(2, person.getPassword());
                    ps.setString(3, person.getEmail());
                    ps.setLong(4, person.getRole().getId());
                    return ps;
                }
            }, holder);

Long newPersonId = holder.getKey().longValue();

请注意,在较新版本的 Postgres 中,您需要使用

connection.prepareStatement(sql.toString(), 
    new String[] { "idcompte" /* name of your id column */ })

而不是

connection.prepareStatement(sql.toString(), 
    Statement.RETURN_GENERATED_KEYS);

【讨论】:

  • 我正在以另一种方式对其进行编码。试图从你的方法中学习,但不断出现命中错误。请帮助:stackoverflow.com/questions/24567633/…
  • 我的查询中缺少 Statement.RETURN_GENERATED_KEYS。添加它使其工作。
  • 这似乎不适用于 HSQL(它返回 This function is not supported
  • 没有 PreparedStatementCreator 有没有办法做到这一点?我对 PreparedStatement 有一些奇怪的问题。
  • 不适用于 mssql 数据库 stackoverflow.com/questions/61236730/…
【解决方案2】:

使用 Spring JDBC 从 INSERT 中获取密钥的最简单方法是使用 SimpleJdbcInsert 类。您可以在 Spring 参考指南中的标题为 Retrieving auto-generated keys using SimpleJdbcInsert 的部分中查看示例。

【讨论】:

    【解决方案3】:

    我用的是Spring3.1 + PostgreSQL9.1,当我用这个时

        KeyHolder keyHolder = new GeneratedKeyHolder();
        jdbcTemplate.update(new PreparedStatementCreator() {
            public PreparedStatement createPreparedStatement(Connection connection)
                    throws SQLException {
                PreparedStatement ps = 
                    connection.prepareStatement(youSQL, 
                        Statement.RETURN_GENERATED_KEYS);
                ps.setString(1, post.name_author);
                ...
                return ps;
            }
        }, keyHolder);
        long id = keyHolder.getKey().longValue();
    

    我遇到了这个异常:

     org.springframework.dao.InvalidDataAccessApiUsageException: 
    The getKey method should only be used when a single key is returned.  
    The current key entry contains multiple keys: ...
    

    所以我改为:

    PreparedStatement ps = 
    connection.prepareStatement(youSQL, new String[]{"id"});
    

    “id”在哪里

    id serial not null primary key
    

    问题就解决了。所以我认为使用

    prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);
    

    不在这里。官方指南为here,第 13.2.8 章:检索自动生成的密钥

    【讨论】:

      【解决方案4】:

      使用 NamedParameterJdbcTemplate 和 Sequence.nextval 的解决方案:

              MapSqlParameterSource parameters = new MapSqlParameterSource();
              parameters.addValue("prenom", prenom);
              parameters.addValue("nom", nom);
              parameters.addValue("datenaissance", datenaissance);
              parameters.addValue("numtelephone", numtelephone);
      
          final String SQL = "INSERT INTO compte (idcompte,  prenom, nom, datenaissance, numtelephone) "
                              + " VALUES(compte_idcompte_seq.NEXTVAL, :prenom, :nom, :datenaissance, :numtelephone)";
      
              KeyHolder keyHolder = new GeneratedKeyHolder();
              NamedParameterJdbcTemplate namedJdbcTemplate = new NamedParameterJdbcTemplate(txManager.getDataSource());
              int nb = namedJdbcTemplate.update(SQL, parameters, keyHolder, new String[]{"idcompte"});
              Long generatedId = keyHolder.getKey().longValue();
      

      我喜欢这个解决方案,因为使用了 NamedParameterJdbcTemplate,因为参数是按名称传递的,而且代码更具可读性且不易出错,尤其是在有大查询时。

      【讨论】:

        【解决方案5】:

        Keyholder 和 PostgreSQL 似乎存在一些已知问题。看看这个链接Spring JDBC - Last inserted id的解决方法

        还请直接检查数据库表以查看记录是否正确插入(即使用 PK)。这将有助于缩小问题范围。

        【讨论】:

          【解决方案6】:

          看看这篇文章,尤其是使用 INSERT...RETURNING 语法的公认答案:

          How to get a value from the last inserted row?

          这是在 PostgreSQL 中获取此值的最佳方式,因为您只需进行一次网络往返,并且在服务器上作为单个原子操作完成。

          【讨论】:

            【解决方案7】:

            我遇到了同样的问题,结果我的表 ID 没有自动递增。

            【讨论】:

              【解决方案8】:

              我遇到了类似的问题。我不知道我究竟为什么会遇到这个问题,但好在我必须使用下面的代码来解决这个问题:

              final KeyHolder holder = new GeneratedKeyHolder();
              int status = myTemplate.update(yourInsertSQL, namedParameters, holder, new String[]{"PrimaryKeyColumnName"});
              

              希望对某人有所帮助。

              【讨论】:

                【解决方案9】:
                setGeneratedKeysColumnNames(new String[]{"column_name"});
                

                不要忘记为自动生成的列设置名称。

                【讨论】:

                  猜你喜欢
                  • 2013-09-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 2013-03-19
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 2011-02-15
                  相关资源
                  最近更新 更多