sql-server - 直到使用 PlatformTransactionManager 完成事务后才提交插入
问题描述
我需要确保在执行存储过程期间提交插入。使用 Spring Boot Starter Data JPA 1.5,这是JpaTransactionManager
. 使用 Spring Boot 2.x,在存储过程完成执行之前不会提交插入。
因此,使用 Spring Boot 2.x 事务管理调用此存储过程,1 分钟后不会插入:
CREATE procedure [dbo].[test_insert]
as
begin
insert into myTempTable(col1)
values ("1");
WAITFOR DELAY '00:01';
end
GO
但是使用 Spring Boot 1.5 transaction mgmt 调用它会立即插入,这正是我所需要的。
这是代码。交易经理:
@Bean
public PlatformTransactionManager transactionManager(EntityManagerFactory entityManagerFactory) {
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(entityManagerFactory);
return transactionManager;
}
回购:
public interface TestRepository extends Repository<String, Long> {
@Procedure("test_insert")
void testInsert();
}
测试用例:
@RunWith(SpringRunner.class)
@SpringBootTest
@Transactional(transactionManager="transactionManager")
public class InsertRepositoryTest {
@Autowired
private TestRepository testRepo;
@Test
public void testInsert() throws SQLException {
testRepo.testInsert();
}
}
我使用的是 SQL Server 2016,其默认事务级别设置为 READ COMMITTED。
如何确保在执行期间立即提交存储过程中的插入?
更新
经过一番研究,我发现使用 Spring 的事务管理器,我处于隐式事务模式。但是当我不使用它时,我处于自动提交状态。换句话说,select @@OPTIONS & 2
返回2
有 Spring 的事务管理器,0
没有它。所以,我认为这解释了为什么我在插入提交时看到了差异。如果那是正确的,那么我只需要知道如何设置自动提交。必须使用 Spring 或 SQL Server jdbc 驱动程序更改默认模式(我使用的是 4.2,现在使用的是 8.2.0.jre8)。
更新 2
为什么在存储过程完成之前其他进程可以看到插入 mytempTable 很重要?因为在生产存储过程中,它执行插入,调用某个服务(我相信从该行中选择数据并将数据插入另一个表),然后 spwaitfor
数据出现在服务插入的表中。因此,在服务看不到插入的情况下,存储过程会挂起,等待服务插入到另一个表中。
我认为解决方案是不使用显式事务,而让 SQL Server 默认自动提交。不幸的是,如果存储过程出现问题,这也意味着不会回滚。但是鉴于存储过程和服务是我无法更改的第三方组件,我想这是我唯一的选择。仍然想知道为什么 Spring Boot 1.5 和 2.x 或 JDBC 驱动程序之间的行为不同。
更新 3
事实证明,Spring Boot 1.5 和 2.x 或 JDBC 驱动程序版本之间的行为没有区别。也就是说,插入永远不会发生,直到事务完成之后。我观察到行为差异的原因与我在旧项目中使用 Spring Boot 1.5 进行测试的事实有关,并且旧项目是一个复合项目,具有两个数据源和两个事务管理器,与我的 Spring Boot 2 不同。 x 项目。不知道为什么会在旧项目中立即发生插入,但确实如此。
无论如何,查看 SQL Server 日志,我可以确认无论 Spring / JDBC 版本如何,它都会在自动提交模式打开时立即插入。
解决方案
如果执行存储过程遵循标准实体操作的“事务后写”模式,则正常行为是 SQL 语句排队,直到事务完成。
Spring Data 存储库默认情况下是事务性的,因此即使@Transactional(transactionManager="transactionManager")
在您的测试中没有明确的事务,事务也在运行。
https://vladmihalcea.com/a-beginners-guide-to-jpahibernate-flush-strategies/(假设您在这里使用 Hibernate,但在其他 JPA 实现中可能有类似的概念)。
试试这个来触发立即刷新:
@RunWith(SpringRunner.class)
@SpringBootTest
@Transactional(transactionManager="transactionManager")
public class InsertRepositoryTest {
@Autowired
private TestRepository testRepo;
@PersistenceContext
primate EntityManager em;
@Test
public void testInsert() throws SQLException {
testRepo.testInsert();
em.flush(); //write all buffered SQL statements
}
}
推荐阅读
- c# - 在 dotnet 函数中使用 HttpClient 时出错
- oracle - 如何创建与另一个数据库具有相同结构但没有数据的新 oracle 数据库?
- vue.js - 在 HTML 字符串中注入 Vue 组件
- php - 如何使用 php、html 和 ajax 将 webhook 集成到网站中?
- r - CRAN 包提交:“错误:C 堆栈使用量太接近限制”
- python - Python CodeSkulptor 暂停从 For 循环内部绘图
- php - Laravel - 如何翻译验证值?不可能?
- ios - 是否允许通过静默推送通知发送位置数据?
- arrays - 将匹配特定日期的文件从服务器/文件夹数组移动到本地文件夹(Powershell)
- python - 如何同时与优势数据库服务器创建多个连接?