简体   繁体   English

从 spring 3 / PostgreSQL 8.4.9 中的行插入获取自动生成的密钥

[英]Getting auto-generated key from row insertion in spring 3 / PostgreSQL 8.4.9

I would like to retrieve the auto-generated id from a row insertion, but I get a NullPointerException我想从行插入中检索自动生成的 id,但我得到一个NullPointerException

Here is the code:这是代码:

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

And this is the PostgreSQL table:这是 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 supports auto-generated keys, but I get this exception: PostgreSQL 支持自动生成的密钥,但我得到了这个异常:

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

EDIT: I tried this to get the auto generated key:编辑:我试过这个来获取自动生成的密钥:

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

but I get a PSQLException :但我得到一个PSQLException

the current value (currval) of the sequence compte_idcompte_seq is not defined in this session , although I thought that compte_idcompte_seq.NEXTVAL should have been called when inserting the row the current value (currval) of the sequence compte_idcompte_seq is not defined in this session ,尽管我认为在插入行时应该调用compte_idcompte_seq.NEXTVAL

EDIT:编辑:

The auto-increment value is properly created when a row is inserted插入行时正确创建自动增量值

Any idea?任何想法?

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();

Note that in newer versions of Postgres you need to use 请注意,在较新版本的Postgres中,您需要使用

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

instead of 代替

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

The easiest way to get a key back from an INSERT with Spring JDBC is to use the SimpleJdbcInsert class. 使用Spring JDBC从INSERT获取密钥的最简单方法是使用SimpleJdbcInsert类。 You can see an example in the Spring Reference Guide, in the section titled Retrieving auto-generated keys using SimpleJdbcInsert . 您可以在“Spring参考指南”的“ 使用SimpleJdbcInsert检索自动生成的密钥 ”一节中看到一个示例。

I'm using Spring3.1 + PostgreSQL9.1, and when I use this 我正在使用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();

I got this exception: 我有这个例外:

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

So I changed to : 所以我改为:

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

where "id" is 其中“id”是

id serial not null primary key

And the problem is solved. 问题解决了。 So I supposed that using 所以我认为使用

prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);

is not right here. 不在这里 The official guide is here , Chapter 13.2.8 :Retrieving auto-generated keys 官方指南在这里 ,第13.2.8节:检索自动生成的密钥

A solution using NamedParameterJdbcTemplate with Sequence.nextval : 使用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();

I like this solution because with NamedParameterJdbcTemplate because the parameters are passed by name and the code is more readable and less prone to errors, especially when there are big queries. 我喜欢这个解决方案,因为使用NamedParameterJdbcTemplate,因为参数是按名称传递的,代码更易读,更不容易出错,尤其是在有大查询时。

There seems to be some known issues with Keyholder and PostgreSQL. Keyholder和PostgreSQL似乎存在一些已知问题。 Have a look at the workaround at this link Spring JDBC - Last inserted id 看一下这个链接的解决方法Spring JDBC - Last inserted id

Also do check the database table directly to see if the record is inserted properly(ie with PK). 还要直接检查数据库表以查看是否正确插入了记录(即使用PK)。 This will help to narrow down the problem. 这将有助于缩小问题范围。

Take a look at this post, especially the accepted answer of using the INSERT...RETURNING syntax: 看一下这篇文章,特别是使用INSERT ... RETURNING语法的已接受答案:

How to get a value from the last inserted row? 如何从最后插入的行中获取值?

This the best way to get this value in PostgreSQL as you only make one network round trip and it's done as a single atomic operation on the server. 这是在PostgreSQL中获取此值的最佳方法,因为您只进行一次网络往返,并且它在服务器上作为单个原子操作完成。

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

I faced the similar problem. 我遇到了类似的问题。 I do not know why exactly I faced this problem but good thing is I got to resolve this by using below code: 我不知道为什么我会遇到这个问题,但好的是我必须通过使用下面的代码来解决这个问题:

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

Hope that helps someone. 希望能帮助别人。

setGeneratedKeysColumnNames(new String[]{"column_name"});

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

Why go through all that pain when you can just use native RETURNING :为什么RETURNING在您可以使用本机返回时经历所有痛苦:

INSERT INTO compte (prenom, nom, datenaissance, numtelephone)
VALUES (?, ?, ?, ?)
RETURNING id;

Or with JDBC:或使用 JDBC:

try (PreparedStatement s = c.prepareStatement("""
    INSERT INTO compte (prenom, nom, datenaissance, numtelephone)
    VALUES (?, ?, ?, ?)
    RETURNING id;
    """
)) {
    s.setString(1, a.getSurname());
    s.setString(2, a.getName());
    s.setDate(3, a.getDob());
    s.setString(4, a.getPhone());

    try (ResultSet rs = s.executeQuery()) {
        while (rs.next())
            System.out.println("ID = " + rs.getLong(1));
    }
}

This approach is obviously PostgreSQL specific (though it also works on Firebird, MariaDB).这种方法显然是 PostgreSQL 特定的(尽管它也适用于 Firebird、MariaDB)。 If you're using another SQL dialect, then this blog post showing the different native syntaxes or JDBC approaches might help . 如果您使用的是另一种 SQL 方言,那么这篇展示不同本机语法或 JDBC 方法的博文可能会有所帮助

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM