在 spring 3 / PostgreSQL 8.4.9 中从行插入中获取自动生成的密钥
Posted
技术标签:
【中文标题】在 spring 3 / PostgreSQL 8.4.9 中从行插入中获取自动生成的密钥【英文标题】:Getting auto-generated key from row insertion in 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")
也是空的,所以有什么方法可以检索自动生成的密钥?
【参考方案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);
【讨论】:
我正在以另一种方式对其进行编码。试图从你的方法中学习,但不断出现命中错误。请帮助:***.com/questions/24567633/… 我的查询中缺少 Statement.RETURN_GENERATED_KEYS。添加它使其工作。 这似乎不适用于 HSQL(它返回This function is not supported
)
没有 PreparedStatementCreator 有没有办法做到这一点?我对 PreparedStatement 有一些奇怪的问题。
不适用于 mssql 数据库 ***.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");
不要忘记为自动生成的列设置名称。
【讨论】:
以上是关于在 spring 3 / PostgreSQL 8.4.9 中从行插入中获取自动生成的密钥的主要内容,如果未能解决你的问题,请参考以下文章
Spring Boot 2.2 中的 r2dbc-postgresql 0.8.0.RC1 无法正常工作
无法在 Spring Boot 中将 Flyway 迁移与 postgresQL 连接起来
无法加载驱动程序类:org.postgresql.Driver Spring Boot Parent 2.3.0