我正在用 Spring JDBC 编写自己的
DAO
层。
我有一个名为 T insert(T entity);
的方法,它插入一个实体并将更新后的版本返回给用户。在我的代码中,逻辑由两部分组成,调用 NamedParameterJdbcTemplate.update
保存实体,然后调用 NamedParameterJdbcTemplate.queryForObject
从数据库中检索更新的实体。
第一部分执行类似这样的查询:
INSERT INTO client (email_address, company_name)
VALUES ('[email protected]', 'some company name');
第二部分执行类似这样的:
SELECT * FROM client t
WHERE t.id = currval('client_id_seq');
我正在使用 Postgres,其中
client
实际上是一个可更新的视图,而不是一个表,而 client_id_seq
是一个手动定义的序列,使 client
表现得像一个表。
它工作正常,除了这两个语句不是事务性的。理论上,在执行第一条语句和第二条语句之间,可以执行另一个并行语句,这会改变
currval('client_id_seq')
,使我的方法第二部分的结果出错。
有没有办法用Spring Jdbc同时执行和获取数据?我试过多语句查询,司机抱怨有多个
ResultSet
,我也试过这样写事务查询:
BEGIN;
INSERT INTO client (email_address, company_name)
VALUES ('[email protected]', 'some company name');
SELECT * FROM client t
WHERE t.id = currval('client_id_seq');
COMMIT;
司机抱怨说
ResultSet
是空的。
如何确保这两次获取之间的一致性?
如果我理解@Mar-Z的答案是正确的,如果
client_id_seq
的值为0,SESSION 1调用nextval('client_id_seq')
,然后SESSION 2调用nextval('client_id_seq')
,client_id_seq
的ACTUAL值为现在 2。但是,如果我在 SESSION 1 中调用 currval('client_id_seq')
,我会得到 1,如果我在 SESSION 2 中调用 currval('client_id_seq')
,我会得到 2。这看起来很有希望。
我可能唯一担心的另一件事是,我知道 Spring Boot 使用连接池(我认为默认情况下是 HikariCP),以及 1 个经过身份验证的连接 = 1 个 postgres 会话。有没有可能,假设我们在连接池中有一些连接
SESS_1
。插入 update
的第一个 client
调用通过 SESS_1
提交给数据库。在我的 insert()
方法的第一次和第二次调用之间,另一个进程也通过 SESS_1
向数据库提交一些查询,因此将 currval('client_id_seq')
更改为 SESS_1
,然后我的方法的第二部分运行 SELECT
再次查询SESS_1
,所以最后,我得到了错误的数据。或者更糟糕的是,如果连接池决定关闭 SESS_1
并为我的 SESS_2
方法的第二部分提供一个不同的连接,比如说 insert()
怎么办?
Spring JDBC 是否有一些东西可以确保不会发生这种情况?我需要配置什么吗?
您无需担心事务和并行更改序列。通过调用 currval('client_id_seq'),您将获得仅在 YOUR 当前会话中使用 nextval('client_id_seq') 获得的序列值。其他会话将使用不同的值。这是由数据库保证的。
更多细节在这里:https://www.postgresql.org/docs/current/functions-sequence.html