文章目录
1. 本文概述
本文以A给B转账100元为例子 , 使用Spring中声明事务进行设置.
重点在记录配置文件的写法 .
2. 环境搭建
2.1 导包
- spring基础包 :
spring-context
- jdbc包 :
spring-jdbc
- SpringAOP部分的包 :
spring-tx
aspectjweaver
- MySQL数据库驱动包 :
mysql-connector-java
- 单元测试包 :
junit
2.2 准备数据库 / 相关类 / 接口 / 方法
2.2.1 准备数据库
要求包含数据 : 用户名称 , 账号金额
id | username | money |
---|---|---|
1 | A | 1000 |
2 | B | 1000 |
2.2.2 准备相关类和接口
-
准备表的实体类 Account
-
在Service层和Dao层准备接口和实现类
名称 | 类型 | 说明 |
---|---|---|
AccountDao | 接口 | Dao层接口 |
AccountDaoImpl | 实现类 | Dao层接口实现类 |
AccountService | 接口 | Service层接口 |
AccountServiceImpl | 实现类 | Service层接口实现类 |
2.2.3 实现类中的方法说明
- Dao层实现类 (AccountDaoImpl) 准备2个方法 :
* 查询账号信息的方法 :queryByName (Account account)
* 修改账号信息的方法 :update (Account account)
- Service层实现类 (AccountServiceImpl) 准备1个方法 :
transfer(String AName , String BName , float money )
* 转账的完整逻辑 :
* 查询A/B账号信息 ----- 给A账号减少钱 ----- 给B账号加钱 (不用考虑事务问题)
3. 配置Spring.xml文件
3.1 用bean配置实例业务对象
用<bean>
配置创建AccountServiceImpl , AccountDaoImpl , JdbcTemplate对象
<!--创建JdbcTemplate对象-->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource" />
</bean>
<!--创建Dao层实现类对象-->
<bean id="accountDao" class="com.itheima.dao.impl.AccountDaoImpl">
<property name="jdbcTemplate" ref="jdbcTemplate" />
</bean>
<!--创建Service层实现类对象-->
<bean id="accountService" class="com.itheima.service.impl.AccountServiceImpl">
<property name="accountDao" ref="accountDao" />
</bean>
<!-- 配置数据源 -->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="${jdbc.driverClass}"></property>
<property name="url" value="${jdbc.url}"></property>
<property name="username" value="${jdbc.username}"></property>
<property name="password" value="${jdbc.password}"></property>
</bean>
3.2 配置事务
3.2.1 配置事务管理器
谁来执行事务
<bean id="transactionManager" class="org.springframework.jdbc.datasourceManager">
<property name="datasource" ref="datasource"></property>
</bean>
3.2.2 配置AOP声明式事务
通知事务管理器 需要事务的方法所在位置
<aop:config>
<!--配置事务通知 -->
<aop:advisor advice-ref="txAdvice" pointcut="execution(* com.itheima.service.impl.*.*(..))" />
</aop:config>
3.2.2 配置事务的规则
一般不会设置里面的属性 , 均采用默认的设置
<!--配置事务规则-->
<tx:advice id="txAdvice" transaction-manager="txtManager">
<tx:attributes>
<tx:method name="transfer" rollback-for="java.lang.Exception"/>
</tx:attributes>
</tx:advice>
rollback-for : 表示发生异常事务回滚
name : 指定参与事务管理的方法 , 即 哪个方法需要事务
复杂版本的配置:
<!--配置事务规则-->
<tx:advice id="txAdvice" transaction-manager="txtManager">
<tx:attributes>
<tx:method name="transfer"
timeout="300"
isolation="DEFAULT"
propagation="REQUIRED"
rollback-for="java.lang.Exception"/>
<tx:method name="add*"
timeout="40"
isolation="DEFAULT"
propagation="REQUIRED"
rollback-for="java.lang.Exception"/>
<!--如果方法以save开始,隔离级别为default,该方法必须在事务下执行 -->
<tx:method name="save*"
isolation="DEFAULT"
propagation="REQUIRED"/>
<!--如果方法以get开始,则只能读取数据库数据,不能修改-->
<tx:method name="get*" read-only="true" />
</tx:attributes>
</tx:advice>
参数含义 :
no-rollback-for = "java.lang.ArithmeticException"
rollback-for = "java.lang.Exception"
: 表示发生异常事务回滚
name
: 指定参与事务管理的方法
timeout
: 表示当前事务执行,SQL语句响应时间
isolation
: 事务隔离级别 , READ_UNCOMMITTED会产生脏读
read-only="true"
: 表示只能读取数据库数据 , 没有修改权限(增删改)
propagation : REQUIRED
: 表示当前方法必须在事务下执行
timeout
: 持有数据库连接的最大时间 , 超过该时间 , 操作数据库的连接会被回收 , 事务回滚
4. 功能测试
@Test
public void testTransfer(){
ApplicationContext act = new ClassPathXmlApplicationContext("spring.xml");
accountService = (AccountService) act.getBean("accountService");
accountService.transfer("A","B",100f);
}
5. 纯注解改造
5.1 改造bean配置的实例业务对象
以Service层的实例对象做例子 :
@Service(value = "accountService")
public class AccountServiceImpl implements AccountService {
@Autowired
private AccountDao accountDao;
------略写后面内容-------
}
5.2 指出要使用事务的类 / 方法
用 @Transactional
写在类或者方法上
@Transactional
@Service(value = "accountService")
public class AccountServiceImpl implements AccountService {
@Autowired
private AccountDao accountDao;
//转账方法
@Transactional(readOnly = true)
@Override
public void transfer(String targetName, String sourceName, Float money) {
Account targetAccount = accountDao.getByName(targetName);
Account sourceAccount = accountDao.getByName(sourceName);
targetAccount.setMoney(targetAccount.getMoney()-money);
sourceAccount.setMoney(sourceAccount.getMoney()+money);
int count = accountDao.update(targetAccount);
System.out.println("受影响行数:"+count);
accountDao.update(sourceAccount);
}
}
该注解里可以设置参数 , 不过一般不会设置
@Transactional (
propagation= Propagation.REQUIRED, //当前方法必须在事务下执行
isolation = Isolation.DEFAULT, //事务的隔离级别
timeout = -1 //超时配置
)`
5.3 将剩下的配置放入配置类SpringConfig.java
@Configuration //将该类指认为配置类
@ComponentScan(basePackages = "com.itheima") //设置注解扫描范围
@EnableTransactionManagement //开启注解事务
public class SpringConfig {
//创建事务管理器
@Bean
public DataSourceTransactionManager transactionManager(DataSource dataSource){
return new DataSourceTransactionManager(dataSource);
}
//创建数据源对象
@Bean
public DataSource dataSource(){
try {
ComboPooledDataSource dataSource = new ComboPooledDataSource();
dataSource.setDriverClass("com.mysql.jdbc.Driver");
dataSource.setJdbcUrl("jdbc:mysql://127.0.0.1:3306/spring5");
dataSource.setUser("root");
dataSource.setPassword("itcast");
return dataSource;
} catch (PropertyVetoException e) {
throw new RuntimeException(e);
}
}
//创建JdbcTemplate
@Bean
public JdbcTemplate jdbcTemplate(DataSource dataSource){
return new JdbcTemplate(dataSource);
}
}