Transaction rolled back because it has been marked as rollback-only

Spring’s declarative transaction provides a powerful function to separate business concerns from non business concerns. There is always a price to pay for the use of good things. When declaring a transaction, an exception “transaction rolled back because it has been marked as rollback only” is often encountered. Sometimes I often wonder, “I’ve tried catch. Why is that still the case?”

XML code

<!--0placeHolder-->
<beanclass="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<propertyname="locations">
<list>
<value>files/pro.properties</value>
</list>
</property>
</bean>

<!--1dataSource-->
<beanid="dataSource"class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<propertyname="driverClassName"value="com.mysql.jdbc.Driver"></property>
<propertyname="url"value="${jdbc.mysql.url}"></property>
<propertyname="username"value="${jdbc.username}"></property>
<propertyname="password"value="${jdbc.userpassword}"></property>
</bean>

<!--2jdbcTemplate-->
<beanid="jdbcTemplate"class="org.springframework.jdbc.core.JdbcTemplate">
<propertyname="dataSource"ref="dataSource"></property>
</bean>

<!--3BaseDao-->
<beanid="baseDao"class="transaction.dao.BaseDao"abstract="true">
<propertyname="jdbcTemplate"ref="jdbcTemplate"/>
</bean>

<beanid="aDao"class="transaction.dao.Adao"parent="baseDao">
</bean>

<beanid="bDao"class="transaction.dao.Bdao"parent="baseDao">
</bean>

<!--4service-->
<beanid="aBo"class="transaction.bo.AboImpl">
<propertyname="aDao"ref="aDao"/>
<propertyname="bBo"ref="bBo"/>
</bean>

<beanid="bBo"class="transaction.bo.BboImpl">
<propertyname="bDao"ref="bDao"/>
</bean>

<!--5transaction-->
<beanid="transactionManager"class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<propertyname="dataSource"ref="dataSource"/>
</bean>

<beanid="transactionInterceptor1"class="org.springframework.transaction.interceptor.TransactionInterceptor">
<propertyname="transactionManager"ref="transactionManager"></property>
<propertyname="transactionAttributes">
<props>
<propkey="*">PROPAGATION_REQUIRED</prop>
</props>
</property>
</bean>

<beanid="autoProxy1"class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
<propertyname="beanNames">
<list>
<value>*Bo</value>
</list>
</property>
<propertyname="interceptorNames">
<list>
<!--
<value>transactionInterceptor2</value>
-->
<value>transactionInterceptor1</value>
</list>
</property>
</bean>

The declarative transaction here acts on all methods of all beans with Bo suffix, using the required propagation mode

Java code

publicintinsertA(Aa){
aDao.insertA(a);

Bb=newB();
b.setName("bbb");
try{
bBo.insertB(b);
}catch(Exceptione){
System.out.println("aaa");
}
return0;
}

here, insertA starts a transaction, calls aDao.insertA (a) [a simple database operation], then calls bBo.insertB (b) [bo to adjust Dao, and Dao throws exceptions directly]. The insertb method of BBO also starts a transaction, but the propagation mechanism here is required. OK, merge with inserta’s business. Because BBO. Insertb (b) will throw an exception, here in try catch, I hope ADAO. Inserta (a) can succeed

But the reality is always cruel. There will be a big “transaction rolled back because it has been marked as rollback only”. As a result, you will find that the operation of adao.inserta (a) is not successful

The simple reason why try catch doesn’t work is that you think your try catch is the closest to the exception throw point and the first handler to handle it. In fact, spring tries to catch earlier, sets some flag bits, and throws the catch exception out. This is our try catch. And “transaction rolled back because it has been marked as rollback only” is because when a transaction is submitted, it is found that the flag has been set and should not be submitted, and then it rolls back the call and prompts that you have been set to rollback only

The reason is that, can configuration solve this problem without changing the code?Using propagation_ REQUIRES_ New. For BBO. Insertb (b), open a new transaction. If it fails, roll back the call. It does not affect the external insertas, so it is OK. The simplest case is to add an interceptor transactioninterceptor2 in front of transactioninterceptor1, which only modifies the transaction properties of insertb

XML code

<beanid="autoProxy1"class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
<propertyname="beanNames">
<list>
<value>*Bo</value>
</list>
</property>
<propertyname="interceptorNames">
<list>

<value>transactionInterceptor2</value>

<value>transactionInterceptor1</value>
</list>
</property>
</bean>


<beanid="transactionInterceptor2"class="org.springframework.transaction.interceptor.TransactionInterceptor">
<propertyname="transactionManager"ref="transactionManager"></property>
<propertyname="transactionAttributes">
<props>
<propkey="insertB">PROPAGATION_REQUIRES_NEW</prop>
</props>
</property>
</bean>

Note the location of the elements in interceptornames. First use transactioninterceptor2, then transactioninterceptor1. Because when insertb is called, transactioninterceptor2 starts a new transaction, and then transactioninterceptor1 merges into this transaction. If the order of the two interceptors is reversed, “transaction rolled back because it has been marked as rollback only” will still appear. Because after transactioninterceptor2 generates a transaction rollback, it will still throw the ex to transactioninterceptor1. At this time, the transaction of transactioninterceptor1 and the transaction of inserta are the same. Transaction interceptor1, set the flag, wait until the end of inserta, because the exception is caught by our try catch, spring will find that the transaction to be submitted has a rollback that has been marked. So it was thrown out again

However, if there are many legacy factors in the system that make you dare not modify the configuration file blindly (such as poincut of transaction), then we can add another transaction proxy

XML code

<beanid="autoProxy2"class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
<propertyname="beanNames">
<list>
<value>*Bo</value>
</list>
</property>
<propertyname="interceptorNames">
<list>
<value>transactionInterceptor2</value>
<!--
<value>transactionInterceptor1</value>
-->
</list>
</property>
</bean>

<beanid="autoProxy1"class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
<propertyname="beanNames">
<list>
<value>*Bo</value>
</list>
</property>
<propertyname="interceptorNames">
<list>
<value>transactionInterceptor1</value>
<!--
<value>transactionInterceptor2</value>
-->
</list>
</property>
</bean>

The above configuration will still bring tragedy “transaction rolled back because it has been marked as rollback only”

But if we put autoproxy2 into autoproxy1 or add order to the auto agent… It turns out to be comedy

XML code

<beanid="autoProxy1"class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
<propertyname="beanNames">
<list>
<value>*Bo</value>
</list>
</property>
<propertyname="interceptorNames">
<list>
<value>transactionInterceptor1</value>
<!--
<value>transactionInterceptor2</value>
-->
</list>
</property>
</bean>


<beanid="autoProxy2"class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
<propertyname="beanNames">
<list>
<value>*Bo</value>
</list>
</property>
<propertyname="interceptorNames">
<list>
<value>transactionInterceptor2</value>
<!--
<value>transactionInterceptor1</value>
-->
</list>
</property>
</bean>

The reason for this is the order in which two agents are used

When doing automatic proxy, spring will process beans in the order of postbean processor bean declaration (if the order is not set). If autoproxy2 is before autoproxy1, transactioninterceptor2 will be closer to the call of insertb, and the effect is like

XML code

<beanid="autoProxy1"class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
<propertyname="beanNames">
<list>
<value>*Bo</value>
</list>
</property>
<propertyname="interceptorNames">
<list>
<value>transactionInterceptor1</value>

<value>transactionInterceptor2</value>

</list>
</property>
</bean>

Configuration of

It seems that spring still needs to pay attention to the order of beans, hahaha

Similar Posts: