45

我想从行插入中检索自动生成的 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 应该在插入行时调用

编辑 :

插入行时正确创建自动增量值

任何的想法 ?

4

9 回答 9

62
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);
于 2013-04-17T13:29:32.797 回答
22

使用 Spring JDBC 从 INSERT 中获取密钥的最简单方法是使用SimpleJdbcInsert该类。您可以在 Spring 参考指南的标题为使用 SimpleJdbcInsert 检索自动生成的键的部分中看到一个示例。

于 2012-05-15T16:42:42.447 回答
14

我正在使用 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);

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

于 2013-11-27T05:32:51.847 回答
8

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

        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 因为参数是按名称传递的,并且代码更具可读性且不易出错,尤其是在有大查询时。

于 2015-11-25T09:11:32.027 回答
1

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

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

于 2012-05-15T09:33:55.507 回答
1

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

如何从最后插入的行中获取值?

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

于 2012-05-15T16:23:34.610 回答
1

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

于 2017-03-22T20:48:37.050 回答
1

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

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

希望对某人有所帮助。

于 2017-04-25T18:56:55.287 回答
0
setGeneratedKeysColumnNames(new String[]{"column_name"});

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

于 2018-11-26T04:27:07.930 回答