欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

SSM整合学习 四

程序员文章站 2023-11-08 16:37:40
事务管理 一:初步理解 理解事务之前,先讲一个你日常生活中最常干的事:取钱。 比如你去ATM机取1000块钱,大体有两个步骤:首先输入密码金额,银行卡扣掉1000元钱;然后ATM出1000元钱。这两个步骤必须是要么都执行要么都不执行。如果银行卡扣除了1000块但是ATM出钱失败的话,你将会损失100 ......

事务管理

一:初步理解

理解事务之前,先讲一个你日常生活中最常干的事:取钱。 
比如你去atm机取1000块钱,大体有两个步骤:首先输入密码金额,银行卡扣掉1000元钱;然后atm出1000元钱。这两个步骤必须是要么都执行要么都不执行。如果银行卡扣除了1000块但是atm出钱失败的话,你将会损失1000元;如果银行卡扣钱失败但是atm却出了1000块,那么银行将损失1000元。所以,如果一个步骤成功另一个步骤失败对双方都不是好事,如果不管哪一个步骤失败了以后,整个取钱过程都能回滚,也就是完全取消所有操作的话,这对双方都是极好的。 
事务就是用来解决类似问题的。事务是一系列的动作,它们综合在一起才是一个完整的工作单元,这些动作必须全部完成,如果有一个失败的话,那么事务就会回滚到最开始的状态,仿佛什么都没发生过一样。 
在企业级应用程序开发中,事务管理必不可少的技术,用来确保数据的完整性和一致性。 
事务有四个特性:acid

  • 原子性(atomicity):事务是一个原子操作,由一系列动作组成。事务的原子性确保动作要么全部完成,要么完全不起作用。
  • 一致性(consistency):一旦事务完成(不管成功还是失败),系统必须确保它所建模的业务处于一致的状态,而不会是部分完成部分失败。在现实中的数据不应该被破坏。
  • 隔离性(isolation):可能有许多事务会同时处理相同的数据,因此每个事务都应该与其他事务隔离开来,防止数据损坏。
  • 持久性(durability):一旦事务完成,无论发生什么系统错误,它的结果都不应该受到影响,这样就能从任何系统崩溃中恢复过来。通常情况下,事务的结果被写到持久化存储器中。

spring事务管理包含两种情况,编程式事务、声明式事务。而声明式事务又包括基于注解@transactional和tx+aop的方式。编程式事务管理使用transactiontemplate或者platformtransactionmanager。 编程式事务需要手动在代码中处理事务,项目中使用的较少,也不推荐使用,因为它直接耦合代码,这里就不多介绍了,本文主要讲解申明式事务

 

二:申明式事务

注解@transactional

我们将之前的项目改造,在spring配置文件applicationcontext.xml中屏蔽aop及tx部分

<?xml version="1.0" encoding="utf-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/xmlschema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemalocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd">



<!--加载上下文注解配置-->
<context:annotation-config/>
<context:property-placeholder location="classpath:resources/db.properties"/>
<context:component-scan base-package="com.founderit">
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.controller"/>
</context:component-scan>
<aop:aspectj-autoproxy/>

<!--dbcp数据库连接池-->
<bean id="datasource"
class="org.apache.commons.dbcp2.basicdatasource"
destroy-method="close" >
<property name="driverclassname" value="${driverclass}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
<!--maxactive: 最大连接数量-->
<property name="maxtotal" value="150"/>
<!--minidle: 最小空闲连接-->
<property name="minidle" value="5"/>
<!--maxidle: 最大空闲连接-->
<property name="maxidle" value="20"/>
<!--initialsize: 初始化连接-->
<property name="initialsize" value="30"/>
<!--removeabandonedtimeout: 超时时间(以秒数为单位)-->
<property name="removeabandonedtimeout" value="10"/>
</bean>

<!-- spring和mybatis完美整合 -->
<bean id="sqlsessionfactory" class="org.mybatis.spring.sqlsessionfactorybean">
<property name="datasource" ref="datasource"/>
<!-- 自动扫描mapping.xml文件 -->
<property name="mapperlocations" value="classpath:com/founderit/mapper/*.xml"></property>
</bean>

<!-- dao接口所在包名,spring会自动查找其下的类 -->
<bean class="org.mybatis.spring.mapper.mapperscannerconfigurer">
<property name="basepackage" value="com.founderit.dao"/>
<property name="sqlsessionfactorybeanname" value="sqlsessionfactory"></property>
</bean>




<!--事务管理部分-->
<bean id="transactionmanager"
class="org.springframework.jdbc.datasource.datasourcetransactionmanager">
<property name="datasource" ref="datasource" />
</bean>
<tx:annotation-driven transaction-manager="transactionmanager" proxy-target-class="true" />


<!-- <tx:advice id="txadvice" transaction-manager="transactionmanager">-->
<!-- <tx:attributes>-->
<!-- <tx:method name="save*" propagation="required" />-->
<!-- <tx:method name="insert*" propagation="required" />-->
<!-- <tx:method name="add*" propagation="required" />-->
<!-- <tx:method name="create*" propagation="required" />-->
<!-- <tx:method name="delete*" propagation="required" />-->
<!-- <tx:method name="update*" propagation="required" />-->
<!-- <tx:method name="find*" propagation="supports" read-only="true" />-->
<!-- <tx:method name="select*" propagation="supports" read-only="true" />-->
<!-- <tx:method name="get*" propagation="supports" read-only="true" />-->
<!-- </tx:attributes>-->
<!-- </tx:advice>-->

<!-- <aop:config>-->
<!-- <aop:pointcut id="iccardterm" expression="execution(* com.founderit.service.*.*(..))" />-->
<!-- <aop:advisor pointcut-ref="iccardterm" advice-ref="txadvice" />-->
<!-- </aop:config>-->

</beans>


在service层类上加@transactional注解,并在catch错误中手动回滚事务

@transactional
@override
public int insertobject(object obj) {
int result=0;
try {
for (int i=0;i<4;i++) {
student student=new student();
student.setname("wll");
student.setage(1);
student.setid(100+i);
result=userdao.insertobject(student);
}
}catch (exception e){
transactionaspectsupport.currenttransactionstatus().setrollbackonly();
}
return result;
}

测试

 项目中将插入四条记录,我们在插入第三条时手动报错,如果没有事务,前二条正常的记录将插入成功,运行tomcat,发起hello请求

@transactional
@override
public int insertobject(object obj) {
int result=0;
try {
for (int i=0;i<4;i++) {
student student=new student();
student.setname("wll");
student.setage(1);
student.setid(100+i);
if (i==2){
int error=10/0;
}
result=userdao.insertobject(student);
}
}catch (exception e){
transactionaspectsupport.currenttransactionstatus().setrollbackonly();
}
return result;
}
执行日志如下,可以看到前二条记录正确执行,但是插入第三条时报错,事务进行了回滚,导致一条也没有插入成功

SSM整合学习 四

 

 SSM整合学习 四

 

 

 

基于tx+aop

在spring配置文件applicationcontext.xml中加上aop及tx部分

<?xml version="1.0" encoding="utf-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/xmlschema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemalocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd">



<!--加载上下文注解配置-->
<context:annotation-config/>
<context:property-placeholder location="classpath:resources/db.properties"/>
<context:component-scan base-package="com.founderit">
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.controller"/>
</context:component-scan>
<aop:aspectj-autoproxy/>

<!--dbcp数据库连接池-->
<bean id="datasource"
class="org.apache.commons.dbcp2.basicdatasource"
destroy-method="close" >
<property name="driverclassname" value="${driverclass}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
<!--maxactive: 最大连接数量-->
<property name="maxtotal" value="150"/>
<!--minidle: 最小空闲连接-->
<property name="minidle" value="5"/>
<!--maxidle: 最大空闲连接-->
<property name="maxidle" value="20"/>
<!--initialsize: 初始化连接-->
<property name="initialsize" value="30"/>
<!--removeabandonedtimeout: 超时时间(以秒数为单位)-->
<property name="removeabandonedtimeout" value="10"/>
</bean>

<!-- spring和mybatis完美整合 -->
<bean id="sqlsessionfactory" class="org.mybatis.spring.sqlsessionfactorybean">
<property name="datasource" ref="datasource"/>
<!-- 自动扫描mapping.xml文件 -->
<property name="mapperlocations" value="classpath:com/founderit/mapper/*.xml"></property>
</bean>

<!-- dao接口所在包名,spring会自动查找其下的类 -->
<bean class="org.mybatis.spring.mapper.mapperscannerconfigurer">
<property name="basepackage" value="com.founderit.dao"/>
<property name="sqlsessionfactorybeanname" value="sqlsessionfactory"></property>
</bean>




<!--事务管理部分-->
<bean id="transactionmanager"
class="org.springframework.jdbc.datasource.datasourcetransactionmanager">
<property name="datasource" ref="datasource" />
</bean>
<tx:annotation-driven transaction-manager="transactionmanager" proxy-target-class="true" />


<tx:advice id="txadvice" transaction-manager="transactionmanager">
<tx:attributes>
<tx:method name="save*" propagation="required" />
<tx:method name="insert*" propagation="required" />
<tx:method name="add*" propagation="required" />
<tx:method name="create*" propagation="required" />
<tx:method name="delete*" propagation="required" />
<tx:method name="update*" propagation="required" />
<tx:method name="find*" propagation="supports" read-only="true" />
<tx:method name="select*" propagation="supports" read-only="true" />
<tx:method name="get*" propagation="supports" read-only="true" />
</tx:attributes>
</tx:advice>

<aop:config>
<aop:pointcut id="iccardterm" expression="execution(* com.founderit.service.*.*(..))" />
<aop:advisor pointcut-ref="iccardterm" advice-ref="txadvice" />
</aop:config>

</beans>

service层

@override
public int insertobject(object obj) {
int result=0;
for (int i=0;i<4;i++) {
student student=new student();
student.setname("wll");
student.setage(1);
student.setid(100+i);
result=userdao.insertobject(student);
}
return result;
}

测试

 项目中将插入四条记录,我们在插入第三条时手动报错,如果没有事务,前二条正常的记录将插入成功,运行tomcat,发起hello请求

@override
public int insertobject(object obj) {
int result=0;
for (int i=0;i<4;i++) {
student student=new student();
student.setname("wll");
student.setage(1);
student.setid(100+i);
if (i==2){
int error=10/0;
}
result=userdao.insertobject(student);
}
return result;
}

SSM整合学习 四

 

 SSM整合学习 四

 

 

 

需要注意的是spring aop被拦截的方法需显式抛出异常,并不能经任何处理,这样aop代理才能捕获到方法的异常,才能进行回滚,默认情况下aop只捕获runtimeexception的异常

如service层做了异常处理,事务不会回滚,我们将service层做了try/catch处理,再次测试发现事务并没有回滚而是正常提交了

SSM整合学习 四

 

 SSM整合学习 四

 

 解决方案:

service层处理事务,那么service中的方法中不做异常捕获(推荐),或者在catch语句中最后增加throw new runtimeexception()语句,以便让aop捕获异常再去回滚,并且在service上层(controller)要继续捕获这个异常并处理