关于Spring@Transactional 的事务控制问题
发布网友
发布时间:2022-04-07 19:41
我来回答
共2个回答
懂视网
时间:2022-04-08 00:02
bool createB = false;
bool createC = false;
try
{
//这里的操作是创建3个目录
Directory.CreateDirectory("\A");
createA = true;
Directory.CreateDirectory("\B");
createB = true;
Directory.CreateDirectory("\C");
createC = true;
}
catch (System.Exception ex)
{
//这里在捕捉到异常时,根据运行的结果进行回滚
if (createB)
{
Directory.Delete("\B");
Directory.Delete("\A");
}
if (!createB && createA)
{
Directory.Delete("\A");
}
}
但是这里我们是把这3个操作当成一个整体来回滚,及时是简单的创建删除文件夹,我们可以看到catch中的回滚逻辑已经很复杂了。可以想象,如果A,B,C这3步不仅仅是创建目录,还有一些其他的操作,那么回滚的逻辑就非常复杂,此时,我们可以考虑把这一个分成几个小事务,分开回滚。代码可以这样写,这里我们使用了Framework提供的Transactin以及TransactioScope类。
[csharp] view plaincopy
- class Program
- {
- static void Main(string[] args)
- {
- //这里就不需要了try catch,因为scope就已经完成了这个功能,
- //即当有异常发生向外抛的时候,会尝试跳出这个using代码块,
- //CLR会在向代码块外边跑异常之前,分别取调用每个transaction的
- //roolback方法,只要rollback中你定义的逻辑没有问题,那么所有
- //的已发生的操作就会安全的回滚。
- using (var scope = new TransactionScope())
- {
- var A = new OperationA();
- Transaction.Current.EnlistVolatile(A, EnlistmentOptions.None);
- A.DoWork();
-
- OperationB B = new OperationB();
- Transaction.Current.EnlistVolatile(B, EnlistmentOptions.None);
- B.DoWork();
- scope.Complete();
- }
- }
-
-
- }
- }
-
- class OperationA : IEnlistmentNotification
- {
- private bool _isCommitSucceed = false;
-
- public void Commit(Enlistment enlistment)
- {
- enlistment.Done();
- }
-
- public void DoWork()//这是自定义的方法,不是继承IEnlistmentNotification
- {
- Directory.CreateDirectory("\A");
- //这里还有一些其他的关于A的复杂操作
- _isCommitSucceed = true;
- }
-
- public void InDoubt(Enlistment enlistment)
- {
- enlistment.Done();
- }
- public void Prepare(PreparingEnlistment preparingEnlistment)
- {
- preparingEnlistment.Prepared();
- }
- public void Rollback(Enlistment enlistment)
- {
- //这里回滚A的一些操作,当然这里的操作逻辑需要你自己来写。
- //比如说查看是否创建成功,或者有一些其他的信号标记,通过这些
- //标记你来决定是删除目录还是其他的什么回滚操作。
- if (_isCommitSucceed)
- Directory.Delete("\A");
- enlistment.Done();
- }
- }
-
- class OperationB : IEnlistmentNotification
- {
- private bool _isCommitSucceed = false;
-
- public void Commit(Enlistment enlistment)
- {
- enlistment.Done();
- }
-
- public void DoWork()
- {
- //这里是关于B的一些复杂操作
- throw new Exception("test");
- //这里依然有一些操作代码,但是我们模拟的是B操作途中抛出异常,所以这里的代码不会执行
- }
-
- public void InDoubt(Enlistment enlistment)
- {
- enlistment.Done();
- }
- public void Prepare(PreparingEnlistment preparingEnlistment)
- {
- preparingEnlistment.Prepared();
- }
- public void Rollback(Enlistment enlistment)
- {
- if (_isCommitSucceed)
- {
- //这里回滚B的一些已经存在的操作。
- }
- enlistment.Done();
- }
这里的机制是这样的,可能是scope.Complete()方法中有某种特别的操作,去告诉CLR这次所有的操作都顺利完成了,在跳出这个scope的时候就不用调用那些transaction的rollback方法了。如果没有执行到scope.Complete()方法,那么就会在跳出这个scope代码块的时候调用rollback方法。一般造成这个的原因是在某个transaction的逻辑操作中出现异常,造成从此次直接抛出异常跳出这个代码块,没有机会执行下边的代码。可以看出这里的重点也是写rollback的逻辑,但是相对于原来的catch中的逻辑,这里分开为多个小的逻辑,相对来说容易了很多。
这只是个人写的一种使用回滚的逻辑。在使用IEnlistmentNotification的时候,也有人把业务逻辑写入到Commit中。如果想真正理解transaction回滚的机制,建议深入理解一下TransactionScope与Transactiond的实现机制。
关于使用Transaction对于非数据库事务的操作
标签:
热心网友
时间:2022-04-07 21:10
看下下面的代码,不知道能不能解决你的问题:
<beans>
<bean id="dataSource"
class="org.apache.commons.dbcp.BasicDataSource"
destroy-method="close">
<property name="driverClassName">
<value>org.gjt.mm.mysql.Driver</value>
</property>
<property name="url">
<value>jdbc:mysql://localhost/sample</value>
</property>
<property name="username">
<value>user</value>
</property>
<property name="password">
<value>mypass</value>
</property>
</bean>
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTr
ansactionManager">
<property name="dataSource">
<ref local="dataSource" />
</property>
</bean>
<bean id="userDAO" class="net.xiaxin..UserDAO">
<property name="dataSource">
<ref local="dataSource" />
</property>
</bean>
<bean id="userDAOProxy"
class="org.springframework.transaction.interceptor.Tran
sactionProxyFactoryBean">
<property name="transactionManager">
<ref bean="transactionManager" />
</property>
<property name="target">
<ref local="userDAO" />
</property>
<property name="transactionAttributes">
<props>
<prop key="insert*">PROPAGATION_REQUIRED</prop>
<prop key="get*">
PROPAGATION_REQUIRED,readOnly
</prop>
</props>
</property>
</bean>
</beans>
配置中包含了dataSource,transactionManager 等资源定义。这些资源都为
一个名为userDAOProxy 的TransactionProxyFactoryBean 服务, 而
userDAOProxy 则对包含实际数据逻辑的userDAO进行了事务性封装。
可以看到,在userDAOProxy 的"transactionAttributes"属性中,我们定义了
针对userDAO 的事务策略,即将所有名称以insert 开始的方法(如
UserDAO.insertUser方法)纳入事务管理范围。如果此方法中抛出异常,则Spring
将当前事务回滚,如果方法正常结束,则提交事务。
而对所有名称以get 开始的方法(如UserDAO.getUser 方法)则以只读的事务
处理机制进行处理。(设为只读型事务,可以使持久层尝试对数据操作进行优化,如对
于只读事务Hibernate将不执行flush操作,而某些数据库连接池和JDBC 驱动也对
只读型操作进行了特别优化。)