spring 学习(五):spring 事务
事务概要
一个数据库事务通常包含了一个序列的对数据库的读/写操作。它的存在包含有以下两个目的:
- 为数据库操作序列提供了一个从失败中恢复到正常状态的方法,同时提供了数据库即使在异常状态下仍能保持一致性的方法。
- 当多个应用程序在并发访问数据库时,可以在这些应用程序之间提供一个隔离方法,以防止彼此的操作互相干扰。
所以事务是用来处理异常和并发问题的。
spring 事务管理
spring 的事务管理我们一般采用声明式事务管理这种方式,有两种方式实现:
- 基于 xml 配置文件实现
- 基于注解实现
一般使用 PlatformTransactionManager
这个事务接口来实现 spring 事务管理。
spring 针对不同的 dao 层框架,提供了接口不同的实现类。
由于现在一般使用 spring JDBC 和 iBatis ,所以我们使用
org.springframework.jdbc.datasource.DataSourceTransactionManager
这个类来实现数据的持久化操作。
spring 实例
我们还是举个栗子来进行 spring 事务操作,以转账操作为例:一人少钱,一人多钱。
1 创建数据库表 account,添加数据,如下图所示:
2 创建 OrdersDao.java 和 OrdersService.java ,实现数据操作:
OrdersDao.java:
package cn.itcast.dao;
import org.springframework.jdbc.core.JdbcTemplate;
public class OrdersDao {
private JdbcTemplate jdbcTemplate;
public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
/*
做对数据库操作的方法,不写业务操作
*/
//少钱的方法
public void lessMoney(){
String sql = "update account set salary=salary+? where username=?";
jdbcTemplate.update(sql, -1000, "小王");
}
//多钱的方法
public void moreMoney(){
String sql = "update account set salary=salary+? where username=?";
jdbcTemplate.update(sql, 1000, "小马");
}
}
OrdersService:
package cn.itcast.service;
import cn.itcast.dao.OrdersDao;
import org.springframework.transaction.annotation.Transactional;
// @Transactional 使用注解时的操作
public class OrdersService {
private OrdersDao ordersDao;
public void setOrdersDao(OrdersDao ordersDao) {
this.ordersDao = ordersDao;
}
/*
业务逻辑层,写转账业务
调用 dao 方法
*/
public void accountMoney(){
//小王少1000
ordersDao.lessMoney();
int i = 10/0; //抛出异常操作
//小马多1000
ordersDao.moreMoney();
}
}
- service 层又叫业务逻辑层
- dao 层,单纯对数据库操作层,在 dao 层不添加业务
我们在编写程序的时候最好按照这种规范操作。
3 创建 applicationContext.xml 文件,完成注入关系,并添加事务模块,出现异常进行回滚操作,配置文件使用 aop 思想配置:
<?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:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
<!-- 配置c3p0连接池 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<!-- 注入属性值 -->
<property name="driverClass" value="com.mysql.jdbc.Driver"></property>
<property name="jdbcUrl" value="jdbc:mysql:///sampledb"></property>
<property name="user" value="root"></property>
<property name="password" value=""></property>
</bean>
<!-- 第一步 配置事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!-- 注入 dataSource -->
<property name="dataSource" ref="dataSource"> </property>
</bean>
<!-- 第二步 配置事务增强 -->
<tx:advice id="txadvice" transaction-manager="transactionManager">
<!-- 做事务操作 -->
<tx:attributes>
<tx:method name="account*" propagation="REQUIRED"/>
</tx:attributes>
</tx:advice>
<!-- 第三步 配置切面 -->
<aop:config>
<!-- 切入点 -->
<aop:pointcut id="pointcut1" expression="execution(* cn.itcast.service.OrdersService.*(..))"/>
<!-- 切面 -->
<aop:advisor advice-ref="txadvice" pointcut-ref="pointcut1"/>
</aop:config>
<bean id="ordersService" class="cn.itcast.service.OrdersService">
<property name="ordersDao" ref="ordersDao"></property>
</bean>
<bean id="ordersDao" class="cn.itcast.dao.OrdersDao">
<property name="jdbcTemplate" ref="jdbcTemplate"></property>
</bean>
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"></property>
</bean>
</beans>
当然我们还可以采用注解的方式进行事务管理,只需修改配置文件:
<?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:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
<!-- 配置c3p0连接池 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<!-- 注入属性值 -->
<property name="driverClass" value="com.mysql.jdbc.Driver"></property>
<property name="jdbcUrl" value="jdbc:mysql:///sampledb"></property>
<property name="user" value="root"></property>
<property name="password" value=""></property>
</bean>
<!-- 第一步 配置事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 第二步 开启事务注解 -->
<tx:annotation-driven transaction-manager="transactionManager"/>
<bean id="ordersService" class="cn.itcast.service.OrdersService">
<property name="ordersDao" ref="ordersDao"></property>
</bean>
<bean id="ordersDao" class="cn.itcast.dao.OrdersDao">
<property name="jdbcTemplate" ref="jdbcTemplate"></property>
</bean>
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"></property>
</bean>
</beans>
然后在要使用事务的方法所在类上面添加注解 @tTransactional
。
4 我们编写一个测试类来查看当抛出异常时数据库能不能进行回滚操作。
创建 TestService.java:
package cn.itcast.service;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class TestService {
@Test
public void testDemo(){
ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml");
OrdersService ordersService = (OrdersService) context.getBean("ordersService");
ordersService.accountMoney();
}
}
运行测试文件,可以看到在控制台抛出异常:
是因为我们在 OrdersService.java 的 accountMoney() 方法下加入了下面这行:
int i = 10/0;
这是不允许的。查看我们数据库中的数据,可以看到内容没有变,所以是进行了回滚操作的,不然按业务逻辑会出现一方少1000,而另一方内容不变。