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.