Spring提供了2种事务管理html
新建包com.chy.dao,包下新建接口AccountDao、实现类AccountDaoImpl:mysql
public interface AccountDao { //查询用户帐户上的余额 public double queryMoney(int id); //减小用户帐户上的余额 public void reduceMoney(int id, double amount); //增长用户帐户上的余额 public void addMoney(int id, double amount); }
@Repository public class AccountDaoImpl extends JdbcDaoSupport implements AccountDao { @Override public double queryMoney(int id) { String sql = "select money from account_tb where id=?"; JdbcTemplate jdbcTemplate = super.getJdbcTemplate(); double money = jdbcTemplate.queryForObject(sql, double.class,id); return money; } @Override public void reduceMoney(int id, double account) { double money = queryMoney(id); money -= account; if (money>=0){ String sql = "update account_tb set money=? where id=?"; JdbcTemplate jdbcTemplate = super.getJdbcTemplate(); jdbcTemplate.update(sql, money, id); } //此处省略余额不足时的处理 } @Override public void addMoney(int id, double account) { double money = queryMoney(id); money += account; String sql = "update account_tb set money=? where id=?"; JdbcTemplate jdbcTemplate = super.getJdbcTemplate(); jdbcTemplate.update(sql, money, id); } }
新建包com.chy.service,包下新建接口TransferService、实现类TransferServiceImpl:spring
public interface TransferService { public void transfer(int from,int to,double account); }
@Service public class TransferServiceImpl implements TransferService { private AccountDao accountDao; @Autowired public void setAccountDao(AccountDao accountDao) { this.accountDao = accountDao; } @Override public void transfer(int from, int to, double account) { accountDao.reduceMoney(from,account); // System.out.println(1/0); accountDao.addMoney(to,account); } }
src下新建db.properties:sql
#mysql数据源配置 url=jdbc:mysql://localhost:3306/my_db?serverTimezone=GMT user=chy password=abcd
<?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" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/util https://www.springframework.org/schema/util/spring-util.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd"> <!-- 引入数据库配置信息 --> <context:property-placeholder location="db.properties" /> <!-- 使用包扫描--> <context:component-scan base-package="com.chy.dao,com.chy.service" /> <!-- 配置数据源,此处使用jdbc数据源 --> <bean name="jdbcDataSource" class="com.mysql.cj.jdbc.MysqlDataSource"> <property name="url" value="${url}" /> <property name="user" value="${user}" /> <property name="password" value="${password}" /> </bean> <!-- 配置AccountDaoImpl,注入数据源 --> <bean name="accountDaoImpl" class="com.chy.dao.AccountDaoImpl"> <!--注入数据源--> <property name="dataSource" ref="jdbcDataSource" /> </bean> </beans>
新建包com.chy.test,包下新建主类Test:数据库
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-config.xml"); TransferServiceImpl transferServiceImpl = applicationContext.getBean("transferServiceImpl", TransferServiceImpl.class); transferServiceImpl.transfer(1,2,1000);
以上是未使用事务管理的,将service层的这句代码取消注释,会出现钱转丢的状况(对方收不到钱)。express
// System.out.println(1/0);
编程式事务管理,顾名思义须要本身写代码。编程
本身写代码很麻烦,spring把代码封装在了TransactionTemplate类中,方便了不少。app
@Service public class TransferServiceImpl implements TransferService { private AccountDao accountDao; private TransactionTemplate transactionTemplate; @Autowired public void setAccountDao(AccountDao accountDao) { this.accountDao = accountDao; } @Autowired public void setTransactionTemplate(TransactionTemplate transactionTemplate) { this.transactionTemplate = transactionTemplate; } @Override public void transfer(int from, int to, double account) { transactionTemplate.execute(new TransactionCallbackWithoutResult() { @Override protected void doInTransactionWithoutResult(TransactionStatus transactionStatus) { accountDao.reduceMoney(from, account); // System.out.println(1 / 0); accountDao.addMoney(to, account); } }); } }
transactionTemplate.execute(new TransactionCallbackWithoutResult() { });
使用匿名内部类传入一个TransactionCallbackWithoutResult接口类型的变量,只需实现一个方法:把原来处理业务的代理都放到这个方法中。ide
<!-- 配置事务管理器,此处使用JDBC的事务管理器--> <bean name="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <!--注入数据源--> <property name="dataSource" ref="jdbcDataSource" /> </bean> <!-- 配置事务管理模板--> <bean class="org.springframework.transaction.support.TransactionTemplate"> <!-- 注入事务管理器 --> <property name="transactionManager" ref="transactionManager" /> </bean>
把下面这句代码取消注释,运行,不会出现钱转丢的状况(执行失败,自动回滚):测试
// System.out.println(1/0);
声明式事务管理,无论是基于xml,仍是基于注解,都有如下3个点要注意:
TransferService transferServiceImpl = applicationContext.getBean("transferServiceImpl", TransferService.class);
红色标出的2处只能使用接口。
<!-- 配置事务管理器,此处使用JDBC的事务管理器--> <bean name="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <!--注入数据源--> <property name="dataSource" ref="jdbcDataSource" /> </bean> <!-- 配置加强--> <!-- 此处只能用id,不能用name。transaction-manager指定要引用的事务管理器 --> <tx:advice id="txAdvice" transaction-manager="transactionManager"> <tx:attributes> <!-- 配置要添加事务的方法,直接写方法名便可。能够有多个method元素,可使用通配符* --> <tx:method name="transfer" /> </tx:attributes> </tx:advice> <!-- 配置aop--> <aop:config> <!-- 配置切入点--> <aop:pointcut id="transferPointCut" expression="execution(* com.chy.service.TransferServiceImpl.transfer(..))"/> <!--指定要引用的<tx:advice>,指定切入点。切入点能够point-ref引用,也能够pointcut现配。能够有多个advisor元素。 --> <aop:advisor advice-ref="txAdvice" pointcut-ref="transferPointCut"/> </aop:config>
<tx:method name="transfer" propagation="REQUIRED" isolation="DEFAULT" read-only="false" />
propagation 指定事务的传播行为,默认为REQUIRED
isolation 指定事务的隔离级别
read-only 是否只读,读=>查询,写=>增删改。默认为false——读写。
这4个属性通常都不用设置,使用默认值便可。固然,只查的时候,能够设置read-only="true"。
<tx:method name="transfer" />
<tx:method name="*" />
<tx:method name="save*" /> <tx:method name="find*" read-only="false"/> <tx:method name="update*" /> <tx:method name="delete*" />
由于要配合aop使用,筛选范围是切入点指定的方法,不是项目中全部的方法。
好比
<tx:method name="save*" />
<aop:advisor advice-ref="txAdvice" pointcut="execution(* com.chy.service.TransferServiceImpl.*(..))" />
切入点是TransferServiceImpl类中的全部方法,是在TransferServiceImpl类全部方法中找到以save开头的全部方法,给其添加事务。
<!-- 配置事务管理器,此处使用JDBC的事务管理器--> <bean name="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <!--注入数据源--> <property name="dataSource" ref="jdbcDataSource" /> </bean> <!--启用事务管理的注解,并指定要使用的事务管理器 --> <tx:annotation-driven transaction-manager="transactionManager" />
@Service // @Transactional public class TransferServiceImpl implements TransferService { private AccountDao accountDao; @Autowired public void setAccountDao(AccountDao accountDao) { this.accountDao = accountDao; } @Override @Transactional public void transfer(int from, int to, double account) { accountDao.reduceMoney(from, account); // System.out.println(1 / 0); accountDao.addMoney(to, account); } }
能够标注在业务层的实现类上,也能够标注在处理业务的方法上。
标注在类上,会给这个全部的业务方法都添加事务;标注在业务方法上,则只给这个方法添加事务。
一样能够设置传播行为、隔离级别、是否只读:
@Transactional(propagation = Propagation.REQUIRED,isolation = Isolation.DEFAULT,readOnly = false)
基于注解的声明式事务管理是最简单的,推荐使用。
缘由:IDEA自动引入的约束不对。
IDEA会自动引入所需的约束,但spring事务管理所需的约束,IDEA引入的不对。
<?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/cache" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/util https://www.springframework.org/schema/util/spring-util.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache.xsd">
看我红色标出的部分(需拖动滚动条查看最右边),这是IDEA自动引入的tx的约束,这是旧版本的约束,已经无效了。
新版本的约束:
xmlns:tx="http://www.springframework.org/schema/tx"
xsi里对应的部分也要修改:
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
能够在错误约束的基础上改,也能够添加。
若是没有修改xsi中对应的部分,会报下面的错:
通配符的匹配很全面, 但没法找到元素 'tx:advice' 的声明。
通配符的匹配很全面, 但没法找到元素 'tx:annotation-driven' 的声明。
上面提供的约束是目前版本的约束,之后可能会变。最好在官方文档中找,有2种方式:
(1)http://www.springframework.org/schema/tx/spring-tx.xsd 可修改此url查看其它约束
(2)若是下载了spring的压缩包,可在schema文件夹下查看约束。
spring压缩包的下载可参考:http://www.javashuo.com/article/p-yscxladi-ko.html