1. Spring 事务控制说明
- JavaEE 体系进行分层开发,事务处理位于业务层,Spring 提供了分层设计业务层的事务处理解决方案。
- Spring 框架为我们提供了一组事务控制的接口。这组接口是在 spring-tx-5.0.2.RELEASE.jar 中。
- Spring 的事务控制都是基于 AOP 的,它既可以使用编程的方式实现,也可以使用配置的方式实现。我们学习的重点是使用配置的方式实现。
2. Spring 事务控制的 API
2.1 PlatformTransactionManager
-
此接口是 Spring 的事务管理器,提供了如下方法:
- 获取事务状态信息
- TransactionStatus getTransaction(TransactionDefinition definition)
- 提交事务
- void commit(TransactionStatus status)
- 回滚事务
- void rollback(TransactionStatus status)
- 获取事务状态信息
-
我们在实际开发中都是使用它的实现类
- org.springframework.jdbc.datasource.DataSourceTransactionManager
2.2 TransactionDefinition
-
此接口是事务的定义信息对象,提供了如下方法:
- 获取事务对象名称
- String getName()
- 获取事务隔离级别
- int getIsolationLevel()
- 获取事务传播行为
- int getPropagationBehavior()
- 获取事务超时时间
- int getTimeout()
- 获取事务是否只读
- boolean isReadOnly()
- 获取事务对象名称
-
事务的隔离级别
- 默认级别,归属下列某一种(和数据库一致)
- ISOLATION_DEFAULT
- 可以读取未提交数据
- SOLATION_READ_UNCOMMITTED
- 只能读取已提交数据,解决脏读问题(Oracle 默认级别)
- ISOLATION_READ_COMMITTED
- 在一次事务中,不能读到取其他事务提交的数据,保证了重复读数据的一致性,解决不可重复读问题(MySQL 默认级别)
- ISOLATION_REPEATABLE_READ
- 默认级别,归属下列某一种(和数据库一致)
-
事务的传播行为
事务传播行为(propagation behavior)指的就是当一个事务方法被另一个事务方法调用时,这个事务方法应该如何进行。- REQUIRED:如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。一般的选择(默认值)
- SUPPORTS:支持当前事务,如果当前没有事务,就以非事务方式执行(没有事务)
- MANDATORY:使用当前的事务,如果当前没有事务,就抛出异常
- REQUERS_NEW:新建事务,如果当前在事务中,把当前事务挂起。
- NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起
- NEVER:以非事务方式运行,如果当前存在事务,抛出异常
- NESTED:如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行 REQUIRED 类似的操作。
-
事务的超时时间
默认值是-1,没有超时限制。如果有,以秒为单位进行设置
-
是否是只读事务
-
读写型事务配置(一般用于增删改方法)
-
只读型事务配置(一般用于查询方法)
-
2.3 TransactionStatus
- 此接口提供的是事务具体的运行状态,提供了如下方法:
- 刷新事务
- void flush()
- 获取是否存在存储点
- boolean hasSavepoint()
- 获取事务是否完成
- boolean isCompleted()
- 获取事务是否为新的事务
- boolean isNewTransaction()
- 获取事务是否回滚
- boolean isRollbackOnly()
- 设置事务回滚
- void setRollbackOnly()
- 刷新事务
3. 基于 XML 的声明式事务控制
3.1 环境搭配
-
导入依赖
<dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.0.2.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>5.0.2.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-tx</artifactId> <version>5.0.2.RELEASE</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.32</version> </dependency> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.8.13</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>5.0.2.RELEASE</version> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.0.9</version> </dependency> </dependencies>
-
准备数据库和表
create table account( id int primary key auto_increment, name varchar(40), money double )character set utf8 collate utf8_general_ci;
-
账户实体类
/** * 账户实体类 */ public class Account implements Serializable { private Integer id; private String name; private Double money; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Double getMoney() { return money; } public void setMoney(Double money) { this.money = money; } @Override public String toString() { return "Account{" + "id=" + id + ", name='" + name + '\'' + ", money=" + money + '}'; } }
-
编写业务层接口和实现类
AccountService 接口
/** * 账户的业务层接口 */ public interface AccountService { /** * 查询所有 * @return */ List<Account> findAllAccount(); /** * 查询一个 * @param id * @return */ Account findAccountById(Integer id); /** * 保存操作 * @param account */ void saveAccount(Account account); /** * 更新操作 * @param account */ void updateAccount(Account account); /** * 删除操作 * @param id */ void deleteAccount(Integer id); /** * 转账操作 * @param sourceName 转出账户 * @param targetName 转入账户 * @param money 转账金额 */ void transfer(String sourceName, String targetName, double money); }
AccountServiceImpl_OLD
/** * 账户业务层实现类 */ public class AccountServiceImpl_OLD implements AccountService { private AccountDao accountDao; public void setAccountDao(AccountDao accountDao) { this.accountDao = accountDao; } public List<Account> findAllAccount() { return accountDao.findAllAccount(); } public Account findAccountById(Integer id) { return accountDao.findAccountById(id); } public void saveAccount(Account account) { accountDao.saveAccount(account); } public void updateAccount(Account account) { accountDao.updateAccount(account); } public void deleteAccount(Integer id) { accountDao.deleteAccount(id); } public void transfer(String sourceName, String targetName, double money) { // 2.1 查询两个账户信息 Account source = accountDao.findAccountByName(sourceName); Account target = accountDao.findAccountByName(targetName); // 2.2 转出账户减钱,转入帐户加钱 source.setMoney(source.getMoney() - money); target.setMoney(target.getMoney() + money); // 2.3 更新操作 accountDao.updateAccount(source); // 2.4 模拟转账异常 int i = 3 / 0; accountDao.updateAccount(target); } }
-
编写 DAO 接口和实现类
AccountDao 接口
/** * 账户的持久层接口 */ public interface AccountDao { /** * 查询所有 * @return */ List<Account> findAllAccount(); /** * 查询一个 * @param id * @return */ Account findAccountById(Integer id); /** * 根据名称查询账户 * @param accountName * @return */ Account findAccountByName(String accountName); /** * 保存操作 * @param account */ void saveAccount(Account account); /** * 更新操作 * @param account */ void updateAccount(Account account); /** * 删除操作 * @param id */ void deleteAccount(Integer id); }
AccountDaoImpl
/** * 账户的持久层实现类 */ public class AccountDaoImpl implements AccountDao { private JdbcTemplate jdbcTemplate; public void setJdbcTemplate(JdbcTemplate jdbcTemplate) { this.jdbcTemplate = jdbcTemplate; } public List<Account> findAllAccount() { String sql = "select * from account"; List<Account> accounts = jdbcTemplate.query(sql, new BeanPropertyRowMapper<Account>(Account.class)); return accounts; } public Account findAccountById(Integer id) { String sql = "select * from account where id = ?"; Account account = jdbcTemplate.queryForObject(sql, new BeanPropertyRowMapper<Account>(Account.class), id); return account; } public Account findAccountByName(String accountName) { String sql = "select * from account where name = ?"; Account account = jdbcTemplate.queryForObject(sql, new BeanPropertyRowMapper<Account>(Account.class), accountName); return account; } public void saveAccount(Account account) { String sql = "insert into account values(null,?,?)"; jdbcTemplate.update(sql, account.getName(), account.getMoney()); } public void updateAccount(Account account) { String sql = "update account set name = ? , money = ? where id = ?"; jdbcTemplate.update(sql, account.getName(), account.getMoney(), account.getId()); } public void deleteAccount(Integer id) { String sql = "delete from account where id = ?"; jdbcTemplate.update(sql,id); } }
-
配置文件
<?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: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/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <!--配置 IOC --> <!--配置 accountService_OLD 对象--> <bean id="accountService_OLD" class="com.zt.service.impl.AccountServiceImpl_OLD"> <property name="accountDao" ref="accountDao"></property> </bean> <!--配置 dao 对象--> <bean id="accountDao" class="com.zt.dao.impl.AccountDaoImpl"> <property name="jdbcTemplate" ref="jdbcTemplate"></property> </bean> <!--配置 jdbcTemplate 对象--> <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate" scope="prototype" > <property name="dataSource" ref="dataSource"></property> </bean> <!--配置 dataSource 对象--> <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"> <property name="driverClassName" value="com.mysql.jdbc.Driver"></property> <property name="url" value="jdbc:mysql://127.0.0.1:3306/spring_day02"></property> <property name="username" value="root"></property> <property name="password" value="123456"></property> <property name="initialSize" value="5"></property> <property name="maxActive" value="10"></property> <property name="maxWait" value="3000"></property> </bean> </beans>
3.2 配置声明式事务
3.2.1 配置声明式事务的步骤
-
配置事务管理器
使用 bean 标签配置事务管理器,并传入一个数据源
- id:给事务管理器起一个唯一标识
- class:一般使用 DataSourceTransactionManager
-
配置事务的通知
使用 tx:advice 标签配置事务通知- id:给事务通知起一个唯一标识
- transaction-manager:给事务通知提供一个事务管理器引用
-
配置 AOP,配置通用切入点表达式
-
建立事务通知和切入点表达式的对应关系
-
配置事务的属性
是在事务的通知 tx:advice 标签的内部- isolation:用于指定事务的隔离级别。默认值是DEFAULT,表示使用数据库的默认隔离级
- propagation:用于指定事务的传播行为。默认值是REQUIRED,表示一定会有事务,增删改的选择。查询方法可以选择 SUPPORTS。
- read-only:用于指定事务是否只读。只有查询方法才能设置为 true。默认值是 false,表示读写。
- timeout:用于指定事务的超时时间,默认值是 -1,表示永不超时。如果指定了数值,以秒为单位。
- rollback-for:用于指定一个异常,当产生该异常时,事务回滚,产生其他异常时,事务不回滚。没有默认值。表示任何异常都回滚。
- no-rollback-for:用于指定一个异常,当产生该异常时,事务不回滚,产生其他异常时事务回滚。没有默认值。表示任何异常都回滚。
3.2.2 配置声明式事务的实例
<!--配置事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!--配置事务的通知-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<!--配置事务的属性-->
<tx:attributes>
<tx:method name="*" propagation="REQUIRED" read-only="false"></tx:method>
<tx:method name="find*" propagation="SUPPORTS" read-only="true"></tx:method>
</tx:attributes>
</tx:advice>
<!--配置 AOP-->
<aop:config>
<!--配置切入点表达式-->
<aop:pointcut id="pt1" expression="execution(* com.zt.service.impl.*.*(..))"></aop:pointcut>
<!--建立切入点表达式和事务通知的对应关系-->
<aop:advisor advice-ref="txAdvice" pointcut-ref="pt1"></aop:advisor>
</aop:config>
3.3 测试类
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations= {"classpath:bean.xml"})
public class AccountService_OLDTest {
@Autowired
private AccountService accountService;
@Test
public void testTransfer(){
accountService.transfer("aaa","bbb",500);
}
}
4. 基于注解的声明式事务控制
4.1 环境搭建
-
导入依赖
<dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.0.2.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>5.0.2.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-tx</artifactId> <version>5.0.2.RELEASE</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.32</version> </dependency> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.8.13</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>5.0.2.RELEASE</version> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.0.9</version> </dependency> </dependencies>
-
准备数据库和表
create table account( id int primary key auto_increment, name varchar(40), money double )character set utf8 collate utf8_general_ci;
-
账户实体类
/** * 账户实体类 */ public class Account implements Serializable { private Integer id; private String name; private Double money; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Double getMoney() { return money; } public void setMoney(Double money) { this.money = money; } @Override public String toString() { return "Account{" + "id=" + id + ", name='" + name + '\'' + ", money=" + money + '}'; } }
-
编写业务层接口和实现类
AccountService 接口
/** * 账户的业务层接口 */ public interface AccountService { /** * 查询所有 * @return */ List<Account> findAllAccount(); /** * 查询一个 * @param id * @return */ Account findAccountById(Integer id); /** * 保存操作 * @param account */ void saveAccount(Account account); /** * 更新操作 * @param account */ void updateAccount(Account account); /** * 删除操作 * @param id */ void deleteAccount(Integer id); /** * 转账操作 * @param sourceName 转出账户 * @param targetName 转入账户 * @param money 转账金额 */ void transfer(String sourceName, String targetName, double money); }
AccountServiceImpl_OLD
/** * 账户业务层实现类 */ @Service("accountService") public class AccountServiceImpl_OLD implements AccountService { @Autowired private AccountDao accountDao; public List<Account> findAllAccount() { return accountDao.findAllAccount(); } public Account findAccountById(Integer id) { return accountDao.findAccountById(id); } public void saveAccount(Account account) { accountDao.saveAccount(account); } public void updateAccount(Account account) { accountDao.updateAccount(account); } public void deleteAccount(Integer id) { accountDao.deleteAccount(id); } public void transfer(String sourceName, String targetName, double money) { // 2.1 查询两个账户信息 Account source = accountDao.findAccountByName(sourceName); Account target = accountDao.findAccountByName(targetName); // 2.2 转出账户减钱,转入帐户加钱 source.setMoney(source.getMoney() - money); target.setMoney(target.getMoney() + money); // 2.3 更新操作 accountDao.updateAccount(source); // 2.4 模拟转账异常 // int i = 3 / 0; accountDao.updateAccount(target); } }
-
编写 DAO 接口和实现类
AccountDao 接口
/** * 账户的持久层接口 */ public interface AccountDao { /** * 查询所有 * @return */ List<Account> findAllAccount(); /** * 查询一个 * @param id * @return */ Account findAccountById(Integer id); /** * 根据名称查询账户 * @param accountName * @return */ Account findAccountByName(String accountName); /** * 保存操作 * @param account */ void saveAccount(Account account); /** * 更新操作 * @param account */ void updateAccount(Account account); /** * 删除操作 * @param id */ void deleteAccount(Integer id); }
AccountDaoImpl
/** * 账户的持久层实现类 */ @Repository("accountDao") public class AccountDaoImpl implements AccountDao { @Autowired private JdbcTemplate jdbcTemplate; public List<Account> findAllAccount() { String sql = "select * from account"; List<Account> accounts = jdbcTemplate.query(sql, new BeanPropertyRowMapper<Account>(Account.class)); return accounts; } public Account findAccountById(Integer id) { String sql = "select * from account where id = ?"; Account account = jdbcTemplate.queryForObject(sql, new BeanPropertyRowMapper<Account>(Account.class), id); return account; } public Account findAccountByName(String accountName) { String sql = "select * from account where name = ?"; Account account = jdbcTemplate.queryForObject(sql, new BeanPropertyRowMapper<Account>(Account.class), accountName); return account; } public void saveAccount(Account account) { String sql = "insert into account values(null,?,?)"; jdbcTemplate.update(sql, account.getName(), account.getMoney()); } public void updateAccount(Account account) { String sql = "update account set name = ? , money = ? where id = ?"; jdbcTemplate.update(sql, account.getName(), account.getMoney(), account.getId()); } public void deleteAccount(Integer id) { String sql = "delete from account where id = ?"; jdbcTemplate.update(sql,id); } }
-
配置文件
<?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:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:context="http://www.springframework.org/schema/context" 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 http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <!--配置 IOC --> <!--告知 spring 在创建容器时要扫描的包--> <context:component-scan base-package="com.zt"></context:component-scan> <!--配置 jdbcTemplate 对象--> <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate" scope="prototype" > <property name="dataSource" ref="dataSource"></property> </bean> <!--配置 dataSource 对象--> <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"> <property name="driverClassName" value="com.mysql.jdbc.Driver"></property> <property name="url" value="jdbc:mysql://127.0.0.1:3306/spring_day02"></property> <property name="username" value="root"></property> <property name="password" value="123456"></property> <property name="initialSize" value="5"></property> <property name="maxActive" value="10"></property> <property name="maxWait" value="3000"></property> </bean> </beans>
4.2 配置声明式事务
4.2.1 配置声明式事务的步骤
- 配置事务管理器
- 开启 spring 对注解事务的支持
- 在需要事务支持的地方使用 @Transactional 注解
4.2.2 配置声明式事务的实例
<!--配置事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 开启spring对注解事务的支持-->
<tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven>
/**
* 账户业务层实现类
*/
@Service("accountService")
@Transactional(propagation = Propagation.REQUIRED, readOnly = false)
public class AccountServiceImpl_OLD implements AccountService {
@Autowired
private AccountDao accountDao;
@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)
public List<Account> findAllAccount() {
return accountDao.findAllAccount();
}
@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)
public Account findAccountById(Integer id) {
return accountDao.findAccountById(id);
}
public void saveAccount(Account account) {
accountDao.saveAccount(account);
}
public void updateAccount(Account account) {
accountDao.updateAccount(account);
}
public void deleteAccount(Integer id) {
accountDao.deleteAccount(id);
}
public void transfer(String sourceName, String targetName, double money) {
// 2.1 查询两个账户信息
Account source = accountDao.findAccountByName(sourceName);
Account target = accountDao.findAccountByName(targetName);
// 2.2 转出账户减钱,转入帐户加钱
source.setMoney(source.getMoney() - money);
target.setMoney(target.getMoney() + money);
// 2.3 更新操作
accountDao.updateAccount(source);
// 2.4 模拟转账异常
// int i = 3 / 0;
accountDao.updateAccount(target);
}
}
4.3 测试类
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations= {"classpath:bean.xml"})
public class AccountService_OLDTest {
@Autowired
private AccountService accountService;
@Test
public void testTransfer(){
accountService.transfer("aaa","bbb",500);
}
}
5. 基于纯注解的声明式事务控制
5.1 环境搭建
-
导入依赖
<dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.0.2.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>5.0.2.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-tx</artifactId> <version>5.0.2.RELEASE</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.32</version> </dependency> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.8.13</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>5.0.2.RELEASE</version> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.0.9</version> </dependency> </dependencies>
-
准备数据库和表
create table account( id int primary key auto_increment, name varchar(40), money double )character set utf8 collate utf8_general_ci;
-
账户实体类
/** * 账户实体类 */ public class Account implements Serializable { private Integer id; private String name; private Double money; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Double getMoney() { return money; } public void setMoney(Double money) { this.money = money; } @Override public String toString() { return "Account{" + "id=" + id + ", name='" + name + '\'' + ", money=" + money + '}'; } }
-
编写业务层接口和实现类
AccountService 接口
/** * 账户的业务层接口 */ public interface AccountService { /** * 查询所有 * @return */ List<Account> findAllAccount(); /** * 查询一个 * @param id * @return */ Account findAccountById(Integer id); /** * 保存操作 * @param account */ void saveAccount(Account account); /** * 更新操作 * @param account */ void updateAccount(Account account); /** * 删除操作 * @param id */ void deleteAccount(Integer id); /** * 转账操作 * @param sourceName 转出账户 * @param targetName 转入账户 * @param money 转账金额 */ void transfer(String sourceName, String targetName, double money); }
AccountServiceImpl_OLD
/** * 账户业务层实现类 */ @Service("accountService") public class AccountServiceImpl_OLD implements AccountService { @Autowired private AccountDao accountDao; public List<Account> findAllAccount() { return accountDao.findAllAccount(); } public Account findAccountById(Integer id) { return accountDao.findAccountById(id); } public void saveAccount(Account account) { accountDao.saveAccount(account); } public void updateAccount(Account account) { accountDao.updateAccount(account); } public void deleteAccount(Integer id) { accountDao.deleteAccount(id); } public void transfer(String sourceName, String targetName, double money) { // 2.1 查询两个账户信息 Account source = accountDao.findAccountByName(sourceName); Account target = accountDao.findAccountByName(targetName); // 2.2 转出账户减钱,转入帐户加钱 source.setMoney(source.getMoney() - money); target.setMoney(target.getMoney() + money); // 2.3 更新操作 accountDao.updateAccount(source); // 2.4 模拟转账异常 // int i = 3 / 0; accountDao.updateAccount(target); } }
-
编写 DAO 接口和实现类
AccountDao 接口
/** * 账户的持久层接口 */ public interface AccountDao { /** * 查询所有 * @return */ List<Account> findAllAccount(); /** * 查询一个 * @param id * @return */ Account findAccountById(Integer id); /** * 根据名称查询账户 * @param accountName * @return */ Account findAccountByName(String accountName); /** * 保存操作 * @param account */ void saveAccount(Account account); /** * 更新操作 * @param account */ void updateAccount(Account account); /** * 删除操作 * @param id */ void deleteAccount(Integer id); }
AccountDaoImpl
/** * 账户的持久层实现类 */ @Repository("accountDao") public class AccountDaoImpl implements AccountDao { @Autowired private JdbcTemplate jdbcTemplate; public List<Account> findAllAccount() { String sql = "select * from account"; List<Account> accounts = jdbcTemplate.query(sql, new BeanPropertyRowMapper<Account>(Account.class)); return accounts; } public Account findAccountById(Integer id) { String sql = "select * from account where id = ?"; Account account = jdbcTemplate.queryForObject(sql, new BeanPropertyRowMapper<Account>(Account.class), id); return account; } public Account findAccountByName(String accountName) { String sql = "select * from account where name = ?"; Account account = jdbcTemplate.queryForObject(sql, new BeanPropertyRowMapper<Account>(Account.class), accountName); return account; } public void saveAccount(Account account) { String sql = "insert into account values(null,?,?)"; jdbcTemplate.update(sql, account.getName(), account.getMoney()); } public void updateAccount(Account account) { String sql = "update account set name = ? , money = ? where id = ?"; jdbcTemplate.update(sql, account.getName(), account.getMoney(), account.getId()); } public void deleteAccount(Integer id) { String sql = "delete from account where id = ?"; jdbcTemplate.update(sql,id); } }
-
在 java 包下创建 config 包
-
创建 SpringConfiguration
/** * spring 的配置类,相当于 bean.xml */ @Configuration @ComponentScan("com.zt") @Import({JdbcConfig.class}) @PropertySource("druid.properties") public class SpringConfiguration { }
-
创建 JdbcConfig
/** * 连接数据库相关的配置类 */ public class JdbcConfig { @Value("${driverClassName}") private String driverClassName; @Value("${url}") private String url; @Value("${name}") private String name; @Value("${password}") private String password; @Value("${initialSize}") private int initialSize; @Value("${maxActive}") private int maxActive; @Value("${maxWait}") private int maxWait; @Bean("jdbcTemplate") @Scope("prototype") public JdbcTemplate createJdbcTemplate(DataSource dataSource){ return new JdbcTemplate(dataSource); } @Bean("dataSource") public DataSource createDataSource(){ DruidDataSource druidDataSource = new DruidDataSource(); druidDataSource.setDriverClassName(driverClassName); druidDataSource.setUrl(url); druidDataSource.setUsername(name); druidDataSource.setPassword(password); druidDataSource.setInitialSize(initialSize); druidDataSource.setMaxActive(maxActive); druidDataSource.setMaxWait(maxWait); return druidDataSource; } }
-
在 resouces 创建 druid.properties
driverClassName=com.mysql.jdbc.Driver url=jdbc:mysql://127.0.0.1:3306/spring_day02 name=root password=123456 initialSize=5 maxActive=10 maxWait=3000
-
5.2 配置声明式事务
5.2.1 配置声明式事务的步骤
- 配置事务管理器
- 开启 spring 对注解事务的支持
- 在需要事务支持的地方使用 @Transactional 注解
5.2.2 配置声明式事务的实例
-
配置事务管理器
在 config 包下创建 TransactionConfig,并在 SpringConfiguration 类中引入
/** * 和事务相关的配置类 */ public class TransactionConfig { /** * 用于创建事务管理器对象 * @param dataSource * @return */ @Bean(name="transactionManager") public PlatformTransactionManager createTransactionManager(DataSource dataSource){ return new DataSourceTransactionManager(dataSource); } }
-
开启 spring 对注解事务的支持
在 SpringConfiguration 类上添加注解 @EnableTransactionManagement
/** * spring 的配置类,相当于 bean.xml */ @Configuration @ComponentScan("com.zt") @Import({JdbcConfig.class,TransactionConfig.class}) @PropertySource("druid.properties") @EnableTransactionManagement public class SpringConfiguration { }
-
在需要事务支持的地方使用 @Transactional 注解
/** * 账户业务层实现类 */ @Service("accountService") @Transactional(propagation = Propagation.REQUIRED, readOnly = false) public class AccountServiceImpl_OLD implements AccountService { @Autowired private AccountDao accountDao; @Transactional(propagation = Propagation.SUPPORTS, readOnly = true) public List<Account> findAllAccount() { return accountDao.findAllAccount(); } @Transactional(propagation = Propagation.SUPPORTS, readOnly = true) public Account findAccountById(Integer id) { return accountDao.findAccountById(id); } public void saveAccount(Account account) { accountDao.saveAccount(account); } public void updateAccount(Account account) { accountDao.updateAccount(account); } public void deleteAccount(Integer id) { accountDao.deleteAccount(id); } public void transfer(String sourceName, String targetName, double money) { // 2.1 查询两个账户信息 Account source = accountDao.findAccountByName(sourceName); Account target = accountDao.findAccountByName(targetName); // 2.2 转出账户减钱,转入帐户加钱 source.setMoney(source.getMoney() - money); target.setMoney(target.getMoney() + money); // 2.3 更新操作 accountDao.updateAccount(source); // 2.4 模拟转账异常 // int i = 3 / 0; accountDao.updateAccount(target); } }
5.3 测试类
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = SpringConfiguration.class)
public class AccountService_OLDTest {
@Autowired
private AccountService accountService;
@Test
public void testTransfer(){
accountService.transfer("aaa","bbb",500);
}
}