Spring事务控制我们要明确的:
1.JavaEE体系进行分层开发,事务处理位于业务层,所以,一般情况下我们使用事务代理,一般放在分层设计业务层。
2.spring框架为我们提供了一组事务控制的应用程序接口(API)。
3.spring的事务控制都是基于AOP的,它既可以使用编程的方式实现,也可以使用配置的方式实现。所以我们学习的重点是使用配置的方式实现。
出现以下错误可能是Service层代码的AccountServiceImpl下,的真正业务代码里没有添加@Transactional(readOnly=false)
applicationContext.xml:
1 <?xml version="1.0" encoding="UTF-8"?> 2 <beans xmlns="http://www.springframework.org/schema/beans" 3 xmlns:p="http://www.springframework.org/schema/p" 4 xmlns:context="http://www.springframework.org/schema/context" 5 xmlns:tx="http://www.springframework.org/schema/tx" 6 xmlns:aop="http://www.springframework.org/schema/aop" 7 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 8 xsi:schemaLocation="http://www.springframework.org/schema/beans 9 http://www.springframework.org/schema/beans/spring-beans.xsd 10 http://www.springframework.org/schema/context 11 http://www.springframework.org/schema/context/spring-context.xsd 12 http://www.springframework.org/schema/tx 13 http://www.springframework.org/schema/tx/spring-tx.xsd 14 http://www.springframework.org/schema/aop 15 http://www.springframework.org/schema/aop/spring-aop.xsd 16 "> 17 18 <!-- 配置组件包扫描的位置 --> 19 <context:component-scan base-package="top.abcdit.spring"/> 20 21 <!-- 读取db.properties配置文件到Spring容器中 --> 22 <context:property-placeholder location="classpath:db.properties"/> 23 24 <!-- 配置 阿里巴巴的 druid 数据源(连接池) --> 25 <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close"> 26 27 <!-- SpringEL 语法 ${key} --> 28 29 <property name="driverClassName" value="${jdbc.driverClassName}"/> 30 <property name="url" value="${jdbc.url}"/> 31 32 <!-- 33 ${username}如果key是username,name 默认spring框架调用的当前操作系统的账号 34 解决方案:可以统一给key加一个前缀 35 --> 36 37 <property name="username" value="${jdbc.username}"/> 38 <property name="password" value="${jdbc.password}"/> 39 <property name="maxActive" value="${jdbc.maxActive}"/> 40 </bean> 41 42 <!-- 配置jdbcTemplate 模板类 --> 43 44 <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> 45 46 <!-- 注入数据源(连接池),set方法注入 --> 47 <property name="dataSource" ref="dataSource"/> 48 <!-- 构造器注入 --> 49 <!-- <constructor-arg name="dataSource" ref="dataSource"/> --> 50 51 </bean> 52 53 <!-- 54 开始Spring的事务配置 55 配置思路 56 1.配置事务管理器 57 2.配置事务细节 58 把事务管理器配置到事务管理器中 59 3.把事务管理器 使用aop切入Service层中 60 --> 61 62 <!-- 配置事务管理器 --> 63 <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> 64 <!-- 注入数据源(连接池) --> 65 <property name="dataSource" ref="dataSource"/> 66 67 </bean> 68 69 <!-- 开启事务注解驱动 70 transaction-manager: 是万物管理器 71 --> 72 <tx:annotation-driven transaction-manager="transactionManager"/> 73 74 </beans>
AccountDao:
1 package top.abcdit.spring.dao; 2 3 public interface AccountDao { 4 5 /** 6 * 转出方法 7 * @param OutId 8 * @param money 9 */ 10 void transOut(Integer outId,Integer money); 11 12 /** 13 * 转入方法 14 * @param inId 15 * @param money 16 */ 17 void transInt(Integer inId,Integer money); 18 19 20 21 }
AccountDaoImpl:
1 package top.abcdit.spring.dao.impl; 2 3 import org.springframework.beans.factory.annotation.Autowired; 4 import org.springframework.jdbc.core.JdbcTemplate; 5 import org.springframework.stereotype.Repository; 6 7 import top.abcdit.spring.dao.AccountDao; 8 9 @Repository 10 public class AccountDaoImpl implements AccountDao { 11 12 13 @Autowired //默认按类型注入 14 private JdbcTemplate jdbcTemplate; 15 16 17 public void transOut(Integer outId, Integer money) { 18 String sql = "update s_account set balance = balance - ? where id = ?"; 19 jdbcTemplate.update(sql, money, outId); 20 21 } 22 23 public void transInt(Integer inId, Integer money) { 24 String sql = "update s_account set balance = balance + ? where id = ?"; 25 jdbcTemplate.update(sql, money, inId); 26 27 } 28 29 }
AccountService:
1 package top.abcdit.spring.service; 2 3 public interface AccountService { 4 5 /** 6 * 这是转账的业务方法 7 * @param outId 转出账号 8 * @param inId 转入账号 9 * @param money 金额 10 */ 11 void trans(Integer outId,Integer inId, Integer money); 12 13 void selectByPrimaryKey (Integer id); 14 15 void selectList(); 16 }
AccountServiceImpl:
1 package top.abcdit.spring.service.impl; 2 3 import org.springframework.beans.factory.annotation.Autowired; 4 import org.springframework.stereotype.Service; 5 import org.springframework.transaction.annotation.Isolation; 6 import org.springframework.transaction.annotation.Propagation; 7 import org.springframework.transaction.annotation.Transactional; 8 9 import top.abcdit.spring.dao.AccountDao; 10 import top.abcdit.spring.service.AccountService; 11 12 13 /* 14 * 15 * @Transactional 16 * 开启注解事务配置,一个 @Transactional只能对当前类有效,当前类下面所有方法配置一样 17 * isolation=Isolation.REPEATABLE_READ, 隔离级别 18 propagation=Propagation.REQUIRED, 传播行为(规则) 19 timeout=5, 超时时间 20 readOnly=true 是否是只读事务 21 * 22 */ 23 24 @Service 25 @Transactional(isolation=Isolation.REPEATABLE_READ, 26 propagation=Propagation.REQUIRED, 27 timeout=5, 28 readOnly=true 29 ) 30 31 public class AccountServiceImpl implements AccountService{ 32 33 @Autowired 34 private AccountDao accountDao; 35 36 37 /** 38 * 这是真正的业务方法 39 * 一个业务方法有多个 功能 (多个dao层方法) 40 * 41 */ 42 @Transactional(readOnly=false) //注解配置时,这里也要添加 43 public void trans(Integer outId, Integer inId, Integer money) { 44 45 accountDao.transOut(outId, money); 46 //System.out.println(1/0); 47 accountDao.transInt(inId, money); 48 49 } 50 51 @Transactional(readOnly=true) 52 public void selectByPrimaryKey(Integer id) { 53 54 } 55 56 @Transactional(readOnly=true) 57 public void selectList() { 58 59 } 60 61 62 }
db.properties配置文件
jdbc.driverClassName=com.mysql.cj.jdbc.Driver jdbc.url=jdbc:mysql://localhost:3306/spring_jdbc?useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT jdbc.username=root jdbc.password=myroot jdbc.maxActive=10
测试代码:
1 package top.abcdit.spring.test; 2 3 import static org.junit.Assert.*; 4 5 import org.junit.Test; 6 import org.junit.runner.RunWith; 7 import org.springframework.beans.factory.annotation.Autowired; 8 import org.springframework.test.context.ContextConfiguration; 9 import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; 10 11 import top.abcdit.spring.service.AccountService; 12 13 14 15 @RunWith(SpringJUnit4ClassRunner.class) 16 @ContextConfiguration("classpath:applicationContext.xml") 17 public class AccountServiceTest { 18 19 @Autowired 20 private AccountService service; 21 22 @Test 23 public void testName() throws Exception { 24 service.trans(10001, 10002, 3000); 25 } 26 27 }