简体   繁体   中英

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

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:

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:

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 :

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

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

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. You can see an example in the Spring Reference Guide, in the section titled Retrieving auto-generated keys using SimpleJdbcInsert .

I'm using Spring3.1 + PostgreSQL9.1, and when I use this

    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 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

A solution using NamedParameterJdbcTemplate with 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.

There seems to be some known issues with Keyholder and PostgreSQL. Have a look at the workaround at this link Spring JDBC - Last inserted id

Also do check the database table directly to see if the record is inserted properly(ie with 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:

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.

我遇到了同样的问题,原来我的表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 :

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

Or with 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). If you're using another SQL dialect, then this blog post showing the different native syntaxes or JDBC approaches might help .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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