查询Nested Transaction细节时,经典例子 serviceA 调用 serviceB,当SeviceB事务传播属性为NESTED时,serviceA
只要catch住了Exception就能保证调用serviceB之前的操作提交而不受ServiceB异常的影响,但是如果
REQUIRES_NEW时,serviceA和serviceB相对独立,serviceB有异常会不影响serviceA.下面测试。
我用的是(maven dependencies里主要版本号)spring3.11 + jpa2.0 + hibernate4.13.后来运行的时候发现有异常:
JpaDialect does not support savepoints - check your JPA provider's capabilities
private SavepointManager getSavepointManager() {
if (!isSavepointAllowed()) {
throw new NestedTransactionNotSupportedException(
"Transaction manager does not allow nested transactions");
}
SavepointManager savepointManager = getEntityManagerHolder().getSavepointManager();
if (savepointManager == null) {
throw new NestedTransactionNotSupportedException(
"JpaDialect does not support savepoints - check your JPA provider's capabilities");
}
return savepointManager;
}
jpa transaction manager 不支持 nested transaction,但JpaTransactionManager的属性nestedTransactionAllowed默认为true,如下。
public JpaTransactionManager() {
setNestedTransactionAllowed(true);
}
后来才知道Hibernate也不支持Nested Transaction,测试不了,只能用jdbc事务了,使用JdbcTemplate。
换成jdbc后的主要配置:
<context:component-scan base-package="cn.xx.xx" />
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<tx:annotation-driven transaction-manager="transactionManager"/>
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"></property>
</bean>
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://localhost:3306/hibernatetest" />
<property name="username" value="userName" />
<property name="password" value="password" />
</bean>
test case 主要相关代码:
public void testInvoke(){
InvokeService ins = (InvokeService) appContext.getBean("invokeServive");
//ins.invokeTest();
ins.jdbcInvokeTest();
System.out.println("test invoke end");
}
invokeservice:
@Service("invokeServive")
public class InvokeService {
private InvokeServiceA serviceA;
private InvokeServiceB serviceB;
public InvokeServiceA getServiceA() {
return serviceA;
}
@Autowired
public void setServiceA(InvokeServiceA serviceA) {
this.serviceA = serviceA;
}
public InvokeServiceB getServiceB() {
return serviceB;
}
@Autowired
public void setServiceB(InvokeServiceB serviceB) {
this.serviceB = serviceB;
}
@Transactional(propagation=Propagation.REQUIRED)
public void jdbcInvokeTest(){
serviceB.jdbcValidInsert();
try{
serviceA.jdbcExceptionInsert();
}catch (Exception e) {
System.out.println("excetpion hander");
}
System.out.println("jdbc invoke method end.");
}
}
serviceB.jdbcValidInsert():
//@Transactional(propagation=Propagation.REQUIRES_NEW)
@Transactional(propagation=Propagation.REQUIRED)
//@Transactional(propagation=Propagation.NESTED)
public void jdbcValidInsert(){
comDao.jdbcValidInsertTest();
}
serviceA.jdbcExceptionInsert():
//@Transactional(propagation=Propagation.REQUIRED)
//@Transactional(propagation=Propagation.REQUIRES_NEW)
@Transactional(propagation=Propagation.NESTED)
public void jdbcExceptionInsert(){
comDao.jdbcExceptionInsertTest();
}
comDao相关方法( 表User(id,password,userName),这里使之抛运行时异常,因为checked异常没有特殊
指定rollback处理的话,是不会理会异常而导致数据都正常提交。):
public void jdbcExceptionInsertTest(){
jdbcTemplate.execute("insert into User(password,userName) values ('pwd','excepNor')");
jdbcTemplate.execute("insert into User(oopassword,userName) values ('pwd','exception')");
}
public void jdbcValidInsertTest(){
jdbcTemplate.execute("insert into User(password,userName) values ('pwd','valid')");
}
运行结果:
控制台
excetpion hander
jdbc invoke method end.
test invoke end
数据库,一条 (pwd,valid)的记录。
没问题,jdbcExceptionInsertTest() 执行第二条时异常,DataSourceTransactionManager使数据库回滚到第一条执行
以前的savepoint,向上throw exception,被catch住,打印出excetpion hander,接着执行打印jdbc invoke method
end. 这时候事务要提交了,因为 serviceB.jdbcValidInsert()是required,所以一起commit,事务结束。
测试二:
将try catch去掉:
@Transactional(propagation=Propagation.REQUIRED)
public void jdbcInvokeTest(){
serviceB.jdbcValidInsert();
//try{
serviceA.jdbcExceptionInsert();
//}catch (Exception e) {
// System.out.println("excetpion hander");
//}
System.out.println("jdbc invoke method end.");
}
运行结果:
控制台:errors:org.springframework.jdbc.BadSqlGrammarException: StatementCallback; bad SQL grammar
数据库:没有数据
jdbcExceptionInsertTest() 执行第二条时异常,回滚后没有被hold住,导致“jdbc invoke method end”没来及执行直
接退出,事务管理器回滚jdbcValidInsertTest执行的结果,然后commit。
测试三,只将jdbcExceptionInsert改为requires new,注意 try catch还是注释掉的状态:
//@Transactional(propagation=Propagation.REQUIRED)
@Transactional(propagation=Propagation.REQUIRES_NEW)
//@Transactional(propagation=Propagation.NESTED)
public void jdbcExceptionInsert(){
comDao.jdbcExceptionInsertTest();
}
运行结果:
控制台:errors:org.springframework.jdbc.BadSqlGrammarException: StatementCallback; bad SQL grammar
数据库:没有数据
本来我原这么理解requires new,就是总是新启事务,很独立,跟其他人无关,也就是有异常,自己回滚自己的,不影响
他人,如jdbcExceptionInsert回滚自身做的所有操作,但是的的确确它影响到了和他平级的事务,数据库没有数据就是证
明,他在遇到异常后,回滚他的所有操作,commit,然后throw 异常,上层没hold住,jdbcInvokeTest结束执行,故没
有打印输出,事务管理器回滚serviceB.jdbcValidInsert()的操作,然后commit。这也就意味着在没有try catch时他的行
为和 nested时候是一样的。 那catch的时候会怎么样呢?
测试四,打开try catch:
@Transactional(propagation=Propagation.REQUIRED)
public void jdbcInvokeTest(){
serviceB.jdbcValidInsert();
try{
serviceA.jdbcExceptionInsert();
}catch (Exception e) {
System.out.println("excetpion hander");
}
System.out.println("jdbc invoke method end.");
}
运行结果:
控制台:
excetpion hander
jdbc invoke method end.
test invoke end
数据库:一条数据(pwd,valid)
证明和nested相对应的情况仍然一致,按我的理解 requires new 就是一个独立王国,如果有异常,即使上层不catch应该
也不影响上层的正常操作的提交,事实却不是,是不是感觉 那要requires new 有什么用呢,这种情况requires new 和
nested确实是可以互相替换。但是如果想把InvokeServiceB的jdbcValidInsert首先提交不受后续操作影响,REQUIRES
NEW就很管用,此时 即使后续操作异常不catch,前面操作因先commit了而不受影响,(pwd,valid)会正常插入。
测试五,将jdbcExceptionInsert()改成required。
@Transactional(propagation=Propagation.REQUIRED)
//@Transactional(propagation=Propagation.REQUIRES_NEW)
//@Transactional(propagation=Propagation.NESTED)
public void jdbcExceptionInsert(){
comDao.jdbcExceptionInsertTest();
}
运行结果:
控制台:errors,org.springframework.transaction.UnexpectedRollbackException: Transaction rolled back
because it has been marked as rollback-only
excetpion hander
jdbc invoke method end.
数据库:没有数据。
required说明jdbcExceptionInsert是上层的一部分,回滚的时候会回滚到serviceB.jdbcValidInsert()执行前的
savepoint,抛出的异常被捕获,输出"excetpion hander",jdbcInvokeTest()接着执行,输出"jdbc invoke
method end." 事务commit,因为事务被底层标志rollback-only,应该是此时回滚(也许是异常发生时)并throw
出异常,所以testInvoke()里的"test invoke end"没机会执行。这里transaction manager貌似个过滤器,最后
根据是否是受检查异常去决定是否回滚,根据是否回滚决定是否再抛出异常。
由测试知道上述情况下nested和requires new可以互换,并且requires new 是又用的,那么又问题又回来了,nested
存在的意义呢,它能在什么情况下不可替代呢,按我现在理解是不是这个只有在某个极端情况下出现呢,虽然没做多少年
开发但是真的没遇到过这种情况,是不是hibernate 不支持也能说明此情况呢?
分享到:
相关推荐
如果存在事务则挂起)、NOT_SUPPORTED(总是运行在非事务环境中,如果存在事务则挂起)、NEVER(总是运行在非事务环境中,如果存在事务则抛出异常)、NESTED(如果存在事务,则在嵌套事务内运行,否则新建事务)。...
PROPAGATION_NESTED--如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则进行与PROPAGATION_REQUIRED类似的操作。 Spring事务管理创造性的解决了很多以前要用重量级的应用服务器才能解决的事务问题,那么...
7. **PROPAGATION_NESTED**:如果当前存在事务,则在一个嵌套事务内执行;如果不存在,则行为与PROPAGATION_REQUIRED相同。 在"Spring事务传播Demo"中,我们可能看到如何在不同传播行为下,一个事务方法被另一个...
在这个名为"spring-nested-aop.zip"的压缩包中,包含了一个关于Spring AOP的嵌套事务处理的示例程序。这通常涉及到在一个事务方法内部调用另一个事务方法的情况,两个方法都需要在同一事务范围内运行,以便确保数据...
- `PROPAGATION_NESTED`:如果存在事务,则在嵌套事务中运行;否则新建。 6. **其他注意事项**: - 事务管理器和事务策略的命名应具有模块化,方便维护。 - 配置中可能需要根据实际情况调整事务的隔离级别、超时...
SUPPORTED(不支持事务,如果有事务则挂起)、NEVER(不允许存在事务,如果有则抛出异常)、NESTED(嵌套事务,如果当前事务存在,则在嵌套事务内执行,否则新建事务)。 4. **Spring的回滚规则**:默认情况下,...
- `NESTED`:如果当前存在事务,则在一个嵌套事务内执行;如果没有事务,则按`REQUIRED`行为执行。 4. **事务的隔离级别** - `READ_UNCOMMITTED`:最低级别,可能导致脏读、不可重复读和幻读。 - `READ_...
否则抛异常)、NOT_SUPPORTED(始终不开启事务)、NEVER(如果有事务则抛异常,否则不开启事务)、NESTED(如果已有事务,则在嵌套事务中运行,否则按REQUIRED处理)。 4. **回滚规则** - 默认情况下,如果在事务...
- `NESTED`: 如果当前存在事务,那么嵌套事务将在当前事务内运行;如果没有,则按REQUIRED行为进行。 3. **事务隔离机制**:事务的隔离级别定义了不同事务之间如何共享数据,防止脏读、不可重复读和幻读等问题。...
- **PROPAGATION_NESTED**:如果当前存在事务,则在一个嵌套事务中运行;否则,行为类似于PROPAGATION_REQUIRED。 7. **Spring Boot自动配置** - Spring Boot通过@EnableTransactionManagement注解启用事务管理,...
NEW`(总是新建一个事务,即使在已有事务中也如此)、`PROPAGATION_NOT_SUPPORTED`(不支持事务,如果存在事务则挂起)、`PROPAGATION_NEVER`(永不运行在事务中)和`PROPAGATION_NESTED`(如果当前存在事务,则在...
无论当前是否存在事务)、NOT_SUPPORTED(当前事务被挂起,不使用事务)、NEVER(永不运行在事务中,如果当前存在事务,则抛异常)、NESTED(如果当前存在事务,则在嵌套事务内执行)。 6. **JTA 与分布式事务**: ...
SUPPORTED`(总是运行在非事务环境中,如果存在事务则挂起)、`NEVER`(总是运行在非事务环境中,如果存在事务则抛出异常)和`NESTED`(如果存在事务,则在嵌套事务内执行,否则行为类似于`REQUIRED`)。 4. **事务...
7. **PROPAGATION_NESTED**:如果当前存在事务,那么在嵌套事务内执行,如果当前没有事务,行为类似于PROPAGATION_REQUIRED。这种传播行为需要数据库支持保存点,使得可以在事务内部创建子事务,回滚子事务不会影响...
- `PROPAGATION_NESTED`: 如果当前存在事务,则在嵌套事务内执行;如果当前不存在事务,则进行与`PROPAGATION_REQUIRED`类似的操作。 #### 三、示例代码分析 以下是一个使用 Hibernate 进行事务管理的例子: ```...
- `NESTED`:如果当前存在事务,则新建一个嵌套事务;如果不存在,则新建一个事务。 ### 四、事务的其他属性 除了传播行为外,`@Transactional`还可以配置隔离级别(`isolation`)、回滚规则(`rollbackFor`、`...
NEW`(总是新建事务,即使当前存在事务也会挂起)、`NOT_SUPPORTED`(永不启动事务,如果当前存在事务,就挂起)、`NEVER`(永不启动事务,如果当前存在事务,则抛出异常)、`NESTED`(如果当前存在事务,则在一个...
结合给出的标签"源码"和"工具",我们可以进一步理解,本文可能会深入到源码层面解析Spring AOP和Hibernate事务管理的实现原理,或者提供一些实用的工具或技巧来帮助开发者更好地管理和调试事务。 通过阅读博文...
- PROPAGATION_NESTED:如果已有事务,就在当前事务中创建一个嵌套事务;没有则新建一个。 在实际开发中,例如在删除客户和订单的场景下,可以使用PROPAGATION_REQUIRES_NEW,确保订单删除失败时,不会影响客户...
7. PROPAGATION_NESTED:如果存在事务,则在嵌套事务中执行,否则按REQUIRED行为执行。 总的来说,Spring的事务管理机制为开发者提供了强大而灵活的工具,能够在复杂的并发环境中保证数据的一致性和完整性。通过...