Spring_four
Spring_four
基于XML的AOP实现事务控制
坐标xml
<dependencies><dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>5.0.2.RELEASE</version></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-test</artifactId><version>5.0.2.RELEASE</version></dependency><dependency><groupId>commons-dbutils</groupId><artifactId>commons-dbutils</artifactId><version>1.4</version></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>5.1.6</version></dependency><dependency><groupId>c3p0</groupId><artifactId>c3p0</artifactId><version>0.9.1.2</version></dependency><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.12</version></dependency><dependency><groupId>org.aspectj</groupId><artifactId>aspectjweaver</artifactId><version>1.8.7</version></dependency></dependencies>

删除AccountServiceTest测试类上的@Qualifier的注解,不产生代理对象
/*** 使用Junit单元测试:测试我们的配置*/@RunWith(SpringJUnit4ClassRunner.class)@ContextConfiguration(locations = "classpath:applicationContext.xml")public class AccountServiceTest {@Autowiredprivate AccountService as;@Testpublic void testTransfer(){as.transfer("aaa","bbb",100f);}}
此时事务没有控制住
【配置文件】添加spring的aop
applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans"xmlns:aop="http://www.springframework.org/schema/aop"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/aophttp://www.springframework.org/schema/aop/spring-aop.xsd"><!-- 配置Service --><bean id="accountService" class="com.it.service.impl.AccountServiceImpl"><!-- 注入dao --><property name="accountDao" ref="accountDao"></property><!--注入事务管理器<property name="txManager" ref="txManager"></property>--></bean><!--配置Dao对象--><bean id="accountDao" class="com.it.dao.impl.AccountDaoImpl"><!-- 注入QueryRunner --><property name="runner" ref="runner"></property><!-- 注入ConnectionUtils --><property name="connectionUtils" ref="connectionUtils"></property></bean><!--配置QueryRunner--><bean id="runner" class="org.apache.commons.dbutils.QueryRunner" scope="prototype"><!--<constructor-arg name="ds" ref="dataSource"></constructor-arg>--></bean><!-- 配置数据源 --><bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"><!--连接数据库的必备信息--><property name="driverClass" value="com.mysql.jdbc.Driver"></property><property name="jdbcUrl" value="jdbc:mysql://localhost:3306/itcastspring"></property><property name="user" value="root"></property><property name="password" value="root"></property></bean><!-- 配置Connection的工具类 ConnectionUtils --><bean id="connectionUtils" class="com.it.utils.ConnectionUtils"><!-- 注入数据源--><property name="dataSource" ref="dataSource"></property></bean><!-- 配置事务管理器--><bean id="txManager" class="com.it.utils.TransactionManager"><!-- 注入ConnectionUtils --><property name="connectionUtils" ref="connectionUtils"></property></bean><aop:config><aop:pointcut id="pc" expression="execution(* com.it.service..*.*(..))"></aop:pointcut><aop:aspect ref="txManager"><!--配置前置通知:开启事务--><aop:before method="beginTransaction" pointcut-ref="pc"></aop:before><!--配置后置通知:提交事务--><aop:after-returning method="commitTransaction" pointcut-ref="pc"></aop:after-returning><!--配置异常通知:回滚事务--><aop:after-throwing method="rollbackTransaction" pointcut-ref="pc"></aop:after-throwing><!--配置最终通知:释放连接--><aop:after method="closeTransaction" pointcut-ref="pc"></aop:after></aop:aspect></aop:config></beans>
【注解】添加spring的aop
可以使用【前置通知】、【后置通知】、【异常通知】、【最终通知】
坐标
<dependencies><dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>5.0.2.RELEASE</version></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-test</artifactId><version>5.0.2.RELEASE</version></dependency><dependency><groupId>commons-dbutils</groupId><artifactId>commons-dbutils</artifactId><version>1.4</version></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>5.1.6</version></dependency><dependency><groupId>c3p0</groupId><artifactId>c3p0</artifactId><version>0.9.1.2</version></dependency><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.12</version></dependency><dependency><groupId>org.aspectj</groupId><artifactId>aspectjweaver</artifactId><version>1.8.7</version></dependency></dependencies>
配置AccountServiceImpl.java的注解
@Servicepublic class AccountServiceImpl implements AccountService {@Autowiredprivate AccountDao accountDao;}
配置AccountDaoImpl.java的注解
@Repositorypublic class AccountDaoImpl implements AccountDao {@Autowiredprivate QueryRunner runner;@Autowiredprivate ConnectionUtils connectionUtils;}
配置ConnectionUtils.java的注解
@Componentpublic class ConnectionUtils {private ThreadLocal<Connection> tl = new ThreadLocal<Connection>();@Autowiredprivate DataSource dataSource;}
配置TransactionManager.java的注解
@Component@Aspect // 切面public class TransactionManager {@Autowiredprivate ConnectionUtils connectionUtils;@Pointcut(value = "execution(* com.it.service..*.*(..))") // 切入点public void pc(){};/*** 开启事务*/@Before(value = "pc()")public void beginTransaction(){try {System.out.println("前置通知");connectionUtils.getThreadConnection().setAutoCommit(false);}catch (Exception e){e.printStackTrace();}}/*** 提交事务*/@AfterReturning(value = "pc()")public void commit(){try {System.out.println("后置通知");connectionUtils.getThreadConnection().commit();}catch (Exception e){e.printStackTrace();}}/*** 回滚事务*/@AfterThrowing(value = "pc()")public void rollback(){try {System.out.println("异常通知");connectionUtils.getThreadConnection().rollback();}catch (Exception e){e.printStackTrace();}}/*** 释放连接*/@After(value = "pc()")public void release(){try {System.out.println("最终通知");connectionUtils.getThreadConnection().close();//把连接还回连接池中connectionUtils.removeConnection();}catch (Exception e){e.printStackTrace();}}}
配置spring容器
配置applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans"xmlns:aop="http://www.springframework.org/schema/aop"xmlns:context="http://www.springframework.org/schema/context"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/aophttp://www.springframework.org/schema/aop/spring-aop.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsd"><!--开启注解支持的组件扫描--><context:component-scan base-package="com.it"></context:component-scan><!--开启aop的注解支持--><aop:aspectj-autoproxy></aop:aspectj-autoproxy><!--配置QueryRunner--><bean id="runner" class="org.apache.commons.dbutils.QueryRunner" scope="prototype"><!--<constructor-arg name="ds" ref="dataSource"></constructor-arg>--></bean><!-- 配置数据源 --><bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"><!--连接数据库的必备信息--><property name="driverClass" value="com.mysql.jdbc.Driver"></property><property name="jdbcUrl" value="jdbc:mysql://localhost:3306/itcastspring"></property><property name="user" value="root"></property><property name="password" value="root"></property></bean></beans>
text
@RunWith(SpringJUnit4ClassRunner.class)@ContextConfiguration(locations = "classpath:applicationContext.xml")public class AccountServiceTest {@Autowiredprivate AccountService as;@Testpublic void testTransfer(){as.transfer("aaa","bbb",100f);}}
但是发现,抛出异常。

因为注解的方式执行顺序是【前置通知】、【最终通知】、【异常通知】/【后置通知】

我们需要使用环绕通知解决问题。
配置TransactionManager.java
@Component@Aspectpublic class TransactionManager {@Autowiredprivate ConnectionUtils connectionUtils;@Pointcut(value = "execution(* com.it.service..*.*(..))")public void pc(){};/*** 开启事务*///@Before(value = "pc()")public void beginTransaction(){try {System.out.println("前置通知");connectionUtils.getThreadConnection().setAutoCommit(false);}catch (Exception e){e.printStackTrace();}}/*** 提交事务*///@AfterReturning(value = "pc()")public void commit(){try {System.out.println("后置通知");connectionUtils.getThreadConnection().commit();}catch (Exception e){e.printStackTrace();}}/*** 回滚事务*///@AfterThrowing(value = "pc()")public void rollback(){try {System.out.println("异常通知");connectionUtils.getThreadConnection().rollback();}catch (Exception e){e.printStackTrace();}}/*** 释放连接*///@After(value = "pc()")public void release(){try {System.out.println("最终通知");connectionUtils.getThreadConnection().close();//把连接还回连接池中connectionUtils.removeConnection();}catch (Exception e){e.printStackTrace();}}@Around(value="pc()")public Object around(ProceedingJoinPoint joinPoint){Object returnValue = null;try {this.beginTransaction(); // 开启事务returnValue = joinPoint.proceed(joinPoint.getArgs());this.commit(); // 提交事务} catch (Throwable throwable) {throwable.printStackTrace();this.rollback(); // 回滚事务}finally {this.release(); // 释放资源}return returnValue;}}
Spring中的JdbcTemplate
1\dbcTemplate概述
它是spring框架中提供的一个对象,是对原始Jdbc API对象的简单封装。spring框架为我们提供了很多的操作模板类。
操作关系型数据的:
JdbcTemplate (操作JDBC,操作数据库)
HibernateTemplate (操作hibernate,操作数据库)
操作nosql数据库的: RedisTemplate(操作Redis,非关系型数据库)
操作消息队列(MQ)的: JmsTemplate (操作ActiveMQ,消息队列)
* 短信平台
* 邮件平台
操作索引库的:ElasticSearchTemplate(操作ElasticSearch,全文检索)
我们今天的主角在spring-jdbc-5.0.2.RELEASE.jar中,我们在导包的时候,除了要导入这个jar包外,还需要导入一个spring-tx-5.0.2.RELEASE.jar(它是和事务相关的)。
坐标xml
<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.6</version></dependency></dependencies>
创建类Account.java
/*** 账户的实体类*/public class Account implements Serializable {private Integer id;private String name;private Float money;}
创建类JdbcTemplateDemo1.java
使用spring提供的数据源
/*** JdbcTemplate的最基本用法*/public class JdbcTemplateDemo1 {public static void main(String[] args) {//准备数据源:spring的内置数据源DriverManagerDataSource ds = new DriverManagerDataSource();ds.setDriverClassName("com.mysql.jdbc.Driver");ds.setUrl("jdbc:mysql://localhost:3306/itcastspring");ds.setUsername("root");ds.setPassword("root");//1.创建JdbcTemplate对象JdbcTemplate jt = new JdbcTemplate();//给jt设置数据源jt.setDataSource(ds);//2.执行操作jt.execute("insert into account(name,money)values('ccc',1000)");}}
2\使用spring容器创建数据源和JdbcTemplate
配置applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsd"><!--配置JdbcTemplate--><bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"><property name="dataSource" ref="dataSource"></property></bean><!-- 配置数据源--><bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"><property name="driverClassName" value="com.mysql.jdbc.Driver"></property><property name="url" value="jdbc:mysql://localhost:3306/itcastspring"></property><property name="username" value="root"></property><property name="password" value="root"></property></bean></beans>
创建JdbcTemplateDemo2.java
/*** JdbcTemplate的最基本用法*/public class JdbcTemplateDemo2 {public static void main(String[] args) {//1.获取容器ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");//2.获取对象JdbcTemplate jt = ac.getBean("jdbcTemplate",JdbcTemplate.class);//3.执行操作jt.execute("insert into account(name,money)values('eee',2222)");/* //准备数据源:spring的内置数据源DriverManagerDataSource ds = new DriverManagerDataSource();ds.setDriverClassName("com.mysql.jdbc.Driver");ds.setUrl("jdbc:mysql://localhost:3306/eesy");ds.setUsername("root");ds.setPassword("1234");//1.创建JdbcTemplate对象JdbcTemplate jt = new JdbcTemplate();//给jt设置数据源jt.setDataSource(ds);//2.执行操作jt.execute("insert into account(name,money)values('ccc',1000)");*/}}
3\使用JdbcTemplate操作数据库的CRUD
创建测试类JdbcTemplateDemo3.java
/*** JdbcTemplate的CRUD操作*/public class JdbcTemplateDemo3 {public static void main(String[] args) {//1.获取容器ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");//2.获取对象JdbcTemplate jt = ac.getBean("jdbcTemplate",JdbcTemplate.class);//3.执行操作//保存// jt.update("insert into account(name,money)values(?,?)","eee",3333f);//更新// jt.update("update account set name=?,money=? where id=?","test",4567,7);//删除// jt.update("delete from account where id=?",3);//查询所有// List<Account> accounts = jt.query("select * from account where money > ?",new AccountRowMapper(),1000f);// List<Account> accounts = jt.query("select * from account where money > ?",new BeanPropertyRowMapper<Account>(Account.class),1000f);// for(Account account : accounts){// System.out.println(account);// }//查询一个// List<Account> accounts = jt.query("select * from account where id = ?",new BeanPropertyRowMapper<Account>(Account.class),1);// System.out.println(accounts.isEmpty()?"没有内容":accounts.get(0));// Account account = jt.queryForObject("select * from account where id = ?", new BeanPropertyRowMapper<Account>(Account.class),1);// System.out.println(account);//查询返回一行一列(使用聚合函数)// Long count = jt.queryForObject("select count(*) from account where money > ?",Long.class,1000f);// System.out.println(count);}}/*** 定义Account的封装策略(解决实体的属性和数据库的字段名称不一致)*/class AccountRowMapper implements RowMapper<Account> {/*** 把结果集中的数据封装到Account中,然后由spring把每个Account加到集合中* @param rs* @param rowNum* @return* @throws SQLException*/public Account mapRow(ResultSet rs, int rowNum) throws SQLException {Account account = new Account();account.setId(rs.getInt("id"));account.setName(rs.getString("name"));account.setMoney(rs.getFloat("money"));return account;}}
4\ JdbcTemplate操作Dao
创建AccountDao接口
/*** 账户的持久层接口*/public interface AccountDao {/*** 根据Id查询账户* @param accountId* @return*/Account findAccountById(Integer accountId);/*** 根据名称查询账户* @param accountName* @return*/Account findAccountByName(String accountName);/*** 更新账户* @param account*/void updateAccount(Account account);}
创建接口的实现类AccountDaoImpl.java
/*** 账户的持久层实现类*/public class AccountDaoImpl implements AccountDao {JdbcTemplate jdbcTemplate;public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {this.jdbcTemplate = jdbcTemplate;}public Account findAccountById(Integer accountId) {List<Account> accounts = jdbcTemplate.query("select * from account where id = ?",new BeanPropertyRowMapper<Account>(Account.class),accountId);return accounts.isEmpty()?null:accounts.get();}public Account findAccountByName(String accountName) {List<Account> accounts = jdbcTemplate.query("select * from account where name = ?",new BeanPropertyRowMapper<Account>(Account.class),accountName);if(accounts.isEmpty()){return null;}if(accounts.size()>){throw new RuntimeException("结果集不唯一");}return accounts.get();}public void updateAccount(Account account) {jdbcTemplate.update("update account set name=?,money=? where id=?",account.getName(),account.getMoney(),account.getId());}}
applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsd"><!-- 配置账户的持久层--><bean id="accountDao" class="com.it.dao.impl.AccountDaoImpl"><property name="jdbcTemplate" ref="jdbcTemplate"></property></bean><!--配置JdbcTemplate--><bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"><property name="dataSource" ref="dataSource"></property></bean><!-- 配置数据源--><bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"><property name="driverClassName" value="com.mysql.jdbc.Driver"></property><property name="url" value="jdbc:mysql://localhost:3306/itcastspring"></property><property name="username" value="root"></property><property name="password" value="root"></property></bean></beans>
test
/*** JdbcTemplate的最基本用法*/public class JdbcTemplateDemo4 {public static void main(String[] args) {//1.获取容器ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");//2.获取对象AccountDao accountDao = ac.getBean("accountDao",AccountDao.class);Account account = accountDao.findAccountById();System.out.println(account);account.setMoney(30000f);accountDao.updateAccount(account);}}
5\ Spring提供JdbcDaoSupport的使用
使用AccountDaoImpl继承JdbcDaoSupport,实现AccountDao
/*** 账户的持久层实现类*/public class AccountDaoImpl extends JdbcDaoSupport implements AccountDao {public Account findAccountById(Integer accountId) {List<Account> accounts = super.getJdbcTemplate().query("select * from account where id = ?",new BeanPropertyRowMapper<Account>(Account.class),accountId);return accounts.isEmpty()?null:accounts.get();}public Account findAccountByName(String accountName) {List<Account> accounts = super.getJdbcTemplate().query("select * from account where name = ?",new BeanPropertyRowMapper<Account>(Account.class),accountName);if(accounts.isEmpty()){return null;}if(accounts.size()>){throw new RuntimeException("结果集不唯一");}return accounts.get();}public void updateAccount(Account account) {super.getJdbcTemplate().update("update account set name=?,money=? where id=?",account.getName(),account.getMoney(),account.getId());}}
applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsd"><!-- 配置账户的持久层--><bean id="accountDao" class="com.it.dao.impl.AccountDaoImpl"><!--<property name="jdbcTemplate" ref="jdbcTemplate"></property>--><property name="dataSource" ref="dataSource"></property></bean><!--配置JdbcTemplate<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"><property name="dataSource" ref="dataSource"></property></bean>--><!-- 配置数据源--><bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"><property name="driverClassName" value="com.mysql.jdbc.Driver"></property><property name="url" value="jdbc:mysql://localhost:3306/itcastspring"></property><property name="username" value="root"></property><property name="password" value="root"></property></bean></beans>
test
/*** JdbcTemplate的最基本用法*/public class JdbcTemplateDemo5 {public static void main(String[] args) {//1.获取容器ApplicationContext ac = newClassPathXmlApplicationContext("applicationContext.xml");//2.获取对象AccountDao accountDao = ac.getBean("accountDao",AccountDao.class);Account account = accountDao.findAccountById();System.out.println(account);account.setMoney(30000f);accountDao.updateAccount(account);}}
6\ 使用JdbcDaoSupport的注解开发
创建AccountDao接口
/*** 账户的持久层接口*/public interface AccountDao {/*** 根据Id查询账户* @param accountId* @return*/Account findAccountById(Integer accountId);/*** 根据名称查询账户* @param accountName* @return*/Account findAccountByName(String accountName);/*** 更新账户* @param account*/void updateAccount(Account account);}
创建AccountDaoImpl2实现AccountDao
/*** 账户的持久层实现类*/@Repository("accountDao")public class AccountDaoImpl2 extends JdbcDaoSupport implements AccountDao {@Autowired // 放置到属性上,也可以放置到set方法上(将spring容器中创建的对象,通过set方法的形参传递给该方法)public void setDi(DataSource dataSource){super.setDataSource(dataSource);}public Account findAccountById(Integer accountId) {List<Account> accounts = jdbcTemplate.query("select * from account where id = ?",new BeanPropertyRowMapper<Account>(Account.class),accountId);return accounts.isEmpty()?null:accounts.get();}public Account findAccountByName(String accountName) {List<Account> accounts = jdbcTemplate.query("select * from account where name = ?",new BeanPropertyRowMapper<Account>(Account.class),accountName);if(accounts.isEmpty()){return null;}if(accounts.size()>){throw new RuntimeException("结果集不唯一");}return accounts.get();}public void updateAccount(Account account) {jdbcTemplate.update("update account set name=?,money=? where id=?",account.getName(),account.getMoney(),account.getId());}}
applicationContext-anno.xml
重新创建applicationContext-anno.xml演示,表示使用注解
<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans"xmlns:context="http://www.springframework.org/schema/context"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsd"><context:component-scan base-package="com.it"></context:component-scan><!-- 配置数据源--><bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"><property name="driverClassName" value="com.mysql.jdbc.Driver"></property><property name="url" value="jdbc:mysql://localhost:3306/itcastspring"></property><property name="username" value="root"></property><property name="password" value="root"></property></bean></beans>
test
/*** JdbcTemplate的最基本用法,使用注解*/public class JdbcTemplateDemo5 {public static void main(String[] args) {//1.获取容器ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext-anno.xml");//2.获取对象AccountDao accountDao = ac.getBean("accountDao",AccountDao.class);Account account = accountDao.findAccountById();System.out.println(account);account.setMoney(30000f);accountDao.updateAccount(account);}}
Spring中的事务控制
1.1 Spring事务控制我们要明确的
第一:JavaEE体系进行分层开发,事务处理位于业务层,Spring提供了分层设计业务层的事务处理解决方案。
第二:spring框架为我们提供了一组事务控制的接口 。具体在后面的第二小节介绍。这组接口是在spring-tx-5.0.2.RELEASE.jar中。
第三:spring的事务控制都是基于AOP的,它既可以使用编程的方式实现,也可以使用配置的方式(声明式)实现。我们学习的重点是使用配置(声明式事务处理)的方式实现。
1.2 Spring中事务控制的API介绍
1.2.1 PlatformTransactionManager
此接口是spring的事务管理器,它里面提供了我们常用的操作事务的方法,如下图:

我们在开发中都是使用它的实现类:
真正管理事务的对象
org.springframework.jdbc.datasource.DataSourceTransactionManager 使用Spring JDBC或myBatis 进行持久化数据时使用
org.springframework.orm.hibernate5.HibernateTransactionManager 使用Hibernate版本进行持久化数据时使用
JpaTransactionManager,使用Jpa操作持久化数据时使用
1.2.2 TransactionDefinition
它是事务的定义信息对象,里面有如下方法:

1:事务的隔离级别
2
2:事务的传播行为
REQUIRED:如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。一般的选择(默认值)
SUPPORTS:支持当前事务,如果当前没有事务,就以非事务方式执行(没有事务)
MANDATORY:使用当前的事务,如果当前没有事务,就抛出异常
REQUERS_NEW:新建事务,如果当前在事务中,把当前事务挂起,从新开启一个新的事务。
NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起
NEVER:以非事务方式运行,如果当前存在事务,抛出异常
NESTED:如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行REQUIRED类似的操作。
3:事务超时时间
默认值是-1,没有超时限制。如果有,以秒为单位、会进行设置,在事务提交/回滚后多长时间,事务失效。
4:是否是只读事务
建议查询时设置为只读。
增删改设置可写。
1.2.3TransactionStatus(了解)
此接口提供的是事务具体的运行状态,方法介绍如下图:

Spring的编程式事务处理
坐标xml
<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>org.springframework</groupId><artifactId>spring-test</artifactId><version>5.0.2.RELEASE</version></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>5.1.6</version></dependency><dependency><groupId>org.aspectj</groupId><artifactId>aspectjweaver</artifactId><version>1.8.7</version></dependency><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.12</version></dependency></dependencies>
AccountDao.java
/*** 账户的持久层接口*/public interface AccountDao {/*** 根据Id查询账户* @param accountId* @return*/Account findAccountById(Integer accountId);/*** 根据名称查询账户* @param accountName* @return*/Account findAccountByName(String accountName);/*** 更新账户* @param account*/void updateAccount(Account account);}
AccountDaoImpl.java
/*** 账户的持久层实现类*/public class AccountDaoImpl extends JdbcDaoSupport implements AccountDao {public Account findAccountById(Integer accountId) {List<Account> accounts = this.getJdbcTemplate().query("select * from account where id = ?",new BeanPropertyRowMapper<Account>(Account.class),accountId);return accounts.isEmpty()?null:accounts.get();}public Account findAccountByName(String accountName) {List<Account> accounts = this.getJdbcTemplate().query("select * from account where name = ?",new BeanPropertyRowMapper<Account>(Account.class),accountName);if(accounts.isEmpty()){return null;}if(accounts.size()>){throw new RuntimeException("结果集不唯一");}return accounts.get();}public void updateAccount(Account account) {this.getJdbcTemplate().update("update account set name=?,money=? where id=?",account.getName(),account.getMoney(),account.getId());}}
AccountService.java
/*** 账户的业务层接口*/public interface AccountService {/*** 根据id查询账户信息* @param accountId* @return*/Account findAccountById(Integer accountId);/*** 转账* @param sourceName 转成账户名称* @param targetName 转入账户名称* @param money 转账金额*/void transfer(String sourceName, String targetName, Float money);}
AccountServiceImpl.java
/*** 账户的业务层实现类** 事务控制应该都是在业务层*/public class AccountServiceImpl implements AccountService {private AccountDao accountDao;public void setAccountDao(AccountDao accountDao) {this.accountDao = accountDao;}public Account findAccountById(Integer accountId) {return accountDao.findAccountById(accountId);}public void transfer(String sourceName, String targetName, Float money) {System.out.println("transfer....");//2.1根据名称查询转出账户Account source = accountDao.findAccountByName(sourceName);//2.2根据名称查询转入账户Account target = accountDao.findAccountByName(targetName);//2.3转出账户减钱source.setMoney(source.getMoney()-money);//2.4转入账户加钱target.setMoney(target.getMoney()+money);//2.5更新转出账户accountDao.updateAccount(source);int i=/;//2.6更新转入账户accountDao.updateAccount(target);}}
applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans"xmlns:context="http://www.springframework.org/schema/context"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsd"><!-- 配置账户的业务层--><bean id="accountService" class="com.it.service.impl.AccountServiceImpl"><property name="accountDao" ref="accountDao"></property></bean><!-- 配置账户的持久层--><bean id="accountDao" class="com.it.dao.impl.AccountDaoImpl"><property name="dataSource" ref="dataSource"></property></bean><!-- 配置数据源--><bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"><property name="driverClassName" value="com.mysql.jdbc.Driver"></property><property name="url" value="jdbc:mysql://localhost:3306/itcastspring"></property><property name="username" value="root"></property><property name="password" value="root"></property></bean></beans>
test
@RunWith(SpringJUnit4ClassRunner.class)@ContextConfiguration(locations = "classpath:applicationContext.xml")public class AccountServiceTest {@Autowiredprivate AccountService as;@Testpublic void testTransfer() {as.transfer("aaa", "bbb", 100f);}}
编程式事务控制
Jdbc的操作,需要配置Jdbc的事务管理器
配置applicationContext.xml
<!-- 一:配置事务管理器=============== --><bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"><property name="dataSource" ref="dataSource"/></bean>
配置事务管理模板
配置applicationContext.xml
<!-- 二:配置事务管理的模板 --><bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate"><property name="transactionManager" ref="transactionManager"/></bean>
在业务层注入事务管理模板
配置applicationContext-tx1.xml
<!-- 配置账户的业务层--><bean id="accountService" class="com.it.service.impl.AccountServiceImpl"><property name="accountDao" ref="accountDao"></property><property name="transactionTemplate" ref="transactionTemplate"/></bean>
AccountServiceImpl.java
public class AccountServiceImpl implements AccountService {// 注入事务管理的模板private TransactionTemplate transactionTemplate;public void setTransactionTemplate(TransactionTemplate transactionTemplate) {this.transactionTemplate = transactionTemplate;}private AccountDao accountDao;public void setAccountDao(AccountDao accountDao) {this.accountDao = accountDao;}public Account findAccountById(Integer accountId) {return accountDao.findAccountById(accountId);}public void transfer(final String sourceName, final String targetName, final Float money) {System.out.println("transfer....");transactionTemplate.execute(new TransactionCallbackWithoutResult() {@Overrideprotected void doInTransactionWithoutResult(TransactionStatus transactionStatus) {System.out.println(transactionStatus.isNewTransaction() + " "+transactionStatus.isCompleted());//2.1根据名称查询转出账户Account source = accountDao.findAccountByName(sourceName);//2.2根据名称查询转入账户Account target = accountDao.findAccountByName(targetName);//2.3转出账户减钱source.setMoney(source.getMoney()-money);//2.4转入账户加钱target.setMoney(target.getMoney()+money);//2.5更新转出账户accountDao.updateAccount(source);// int i=1/0;//2.6更新转入账户accountDao.updateAccount(target);}});}}
Spring的声明式事务处理(**)
配置applicationContext.xml
同时去掉:TransactionTemplate的操作

<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans"xmlns:context="http://www.springframework.org/schema/context"xmlns:aop="http://www.springframework.org/schema/aop"xmlns:tx="http://www.springframework.org/schema/tx"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsdhttp://www.springframework.org/schema/aophttp://www.springframework.org/schema/aop/spring-aop.xsdhttp://www.springframework.org/schema/txhttp://www.springframework.org/schema/tx/spring-tx.xsd"><!-- 配置账户的业务层--><bean id="accountService" class="com.it.service.impl.AccountServiceImpl"><property name="accountDao" ref="accountDao"></property></bean><!-- 配置账户的持久层--><bean id="accountDao" class="com.it.dao.impl.AccountDaoImpl"><property name="dataSource" ref="dataSource"></property></bean><!-- 配置数据源--><bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"><property name="driverClassName" value="com.mysql.jdbc.Driver"></property><property name="url" value="jdbc:mysql://localhost:3306/itcastspring"></property><property name="username" value="root"></property><property name="password" value="root"></property></bean><!-- spring中基于XML的声明式事务控制配置步骤1、配置事务管理器2、配置事务的通知此时我们需要导入事务的约束 tx名称空间和约束,同时也需要aop的使用tx:advice标签配置事务通知属性:id:给事务通知起一个唯一标识transaction-manager:给事务通知提供一个事务管理器引用3、配置AOP中的通用切入点表达式4、建立事务通知和切入点表达式的对应关系5、配置事务的属性是在事务的通知tx:advice标签的内部--><!-- 一:配置事务管理器 --><bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"><property name="dataSource" ref="dataSource"></property></bean><!-- 二:配置事务的通知--><tx:advice id="txAdvice" transaction-manager="transactionManager"><!-- 配置事务的属性isolation:用于指定事务的隔离级别。默认值是DEFAULT,表示使用数据库的默认隔离级别。propagation:用于指定事务的传播行为。默认值是REQUIRED,表示一定会有事务,增删改的选择。查询方法可以选择SUPPORTS。read-only:用于指定事务是否只读。只有查询方法才能设置为true。默认值是false,表示读写。timeout:用于指定事务的超时时间,默认值是-1,表示永不超时。如果指定了数值,以秒为单位。rollback-for:用于指定一个异常,当产生该异常时,事务回滚,产生其他异常时,事务不回滚。没有默认值。表示任何异常都回滚。no-rollback-for:用于指定一个异常,当产生该异常时,事务不回滚,产生其他异常时事务回滚。没有默认值。表示任何异常都回滚。测试:no-rollback-for="java.lang.ArithmeticException",遇到算数异常不回滚--><tx:attributes><tx:method name="*" propagation="REQUIRED" read-only="false"/><tx:method name="find*" propagation="SUPPORTS" read-only="true"></tx:method></tx:attributes></tx:advice><!-- 三:配置aop--><aop:config><!-- 配置切入点表达式--><aop:pointcut id="pc" expression="execution(* com.it.service..*.*(..))"></aop:pointcut><!--建立切入点表达式和事务通知的对应关系 --><aop:advisor advice-ref="txAdvice" pointcut-ref="pc"></aop:advisor></aop:config></beans>
注解
配置AccountDaoImpl.java
@Repositorypublic class AccountDaoImpl extends JdbcDaoSupport implements AccountDao {@Autowiredpublic void setDi(DataSource dataSource){super.setDataSource(dataSource);}}
配置AccountServiceImpl.java
@Service@Transactional(readOnly = true)public class AccountServiceImpl implements AccountService {@Autowiredprivate AccountDao accountDao;public Account findAccountById(Integer accountId) {return accountDao.findAccountById(accountId);}@Transactional(readOnly = false,propagation = Propagation.REQUIRED)public void transfer( String sourceName, String targetName, Float money) {System.out.println("transfer....");//2.1根据名称查询转出账户Account source = accountDao.findAccountByName(sourceName);//2.2根据名称查询转入账户Account target = accountDao.findAccountByName(targetName);//2.3转出账户减钱source.setMoney(source.getMoney() - money);//2.4转入账户加钱target.setMoney(target.getMoney() + money);//2.5更新转出账户accountDao.updateAccount(source);int i=/;//2.6更新转入账户accountDao.updateAccount(target);}}
注意:方法级别的事务会覆盖类级别的事务
配置applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans"xmlns:context="http://www.springframework.org/schema/context"xmlns:aop="http://www.springframework.org/schema/aop"xmlns:tx="http://www.springframework.org/schema/tx"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsdhttp://www.springframework.org/schema/aophttp://www.springframework.org/schema/aop/spring-aop.xsdhttp://www.springframework.org/schema/txhttp://www.springframework.org/schema/tx/spring-tx.xsd"><context:component-scan base-package="com.it"></context:component-scan><!--配置JdbcTemplate--><bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"><property name="dataSource" ref="dataSource"></property></bean><!-- 配置数据源--><bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"><property name="driverClassName" value="com.mysql.jdbc.Driver"></property><property name="url" value="jdbc:mysql://localhost:3306/itcastspring"></property><property name="username" value="root"></property><property name="password" value="root"></property></bean><!-- spring中基于XML的声明式事务控制配置步骤1、配置事务管理器2、配置事务的通知,通过注解的方式--><!-- 配置事务管理器 --><bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"><property name="dataSource" ref="dataSource"></property></bean><tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven></beans>
Spring整体的总结:
总结:
1:IOC DI(xml和注解)
2:AOP(5种通知)(xml和注解)
3:声明式事务处理(xml和注解)
- * DBUtils(第三方提供的)
- * JDBCTemplate(是spring提供的)
4:spring3的新特性(纯注解开发)
- @Configuration
- @ConnectionScan
- @Import
- @Bean
自己创建的对象:使用注解
第三方创建的对象(数据源…):使用配置文件
Spring_four的更多相关文章
- 《Spring_Four》第三次作业——基于Jsoup的大学生考试信息展示系统的原型设计与开发
<Spring_Four团队>第三次团队项目——基于Jsoup的大学生考试信息展示系统的原型设计与开发 一.实验目的与要求 (1)掌握软件原型开发技术: (2)学习使用软件原型开发工具:本 ...
- 《Spring_Four》第一次作业:团队亮相
part one: 1.队名:Spring_Four 2.团队成员组成:学号/姓名(标记团队组长) 201571030114 李蕾 201571030143 周甜甜 201571030139 张天旭( ...
- Spring_Four -- 团队项目设计完善&编码测试
团队项目设计完善&编码测试 1.文档<软件设计方案说明书>github地址:https://github.com/gzyt/SRS 2.项目集成开发环境 数据库:Mysql 5.0 ...
- 团队作业6—《Spring_Four》团队项目系统设计改进与详细设计
一.修改完善团队项目系统设计说明书 a.分析<考信项目系统设计说明书>初稿的不足:数据库建模不足 b. 团队项目Github仓库中更新:https://github.com/gzyt/SR ...
- 团队作业5——《Spring_Four》项目需求改进与系统设计
团队项目需求分析改进: 任务1: a.分析<基于Jsoup的大学生考试信息展示系统项目需求规格说明书>初稿的不足,特别是文档需求描述建模不完整的内容. 通过软件工程更深入的学习发现我们的需 ...
- 《Spring_four》团队作业4—基于原型的团队项目需求调研与分析
(一)需求规格说明书github地址:https://github.com/gzyt/SRS (二)原型链接:http://www.cnblogs.com/lztxh/p/9011873.html ( ...
- 《Spring_Four》第二次作业 基于Jsoup的大学生考试信息展示系统开题报告
一.项目概述 该项目拟采用Jsoup对大学生三大考试(考研.考公务员.考教师资格证)进行消息搜集,研发完成一款轻量级的信息展示APP,本项目主要的创新点在于可以搜集大量的考试信息,对其进行一个展示,而 ...
- 实验八 <FBG> 基于原型的团队项目需求调研与分析
<FBG>团队项目原型设计:http://www.cnblogs.com/ymm3/p/9012534.html GitHub的链接地址:https://github.com/FBGfbg ...
- <Dare To Dream 团队>第二次作业:基于B/S的家教管理系统
团队项目GitHub仓库地址:https://github.com/Sophur/Team-Project.git 为其他团队评分结果: 小组名 N A B C D 总分 平均分 Blue Flk ...
随机推荐
- BZOJ 1951: [Sdoi2010]古代猪文 ExCRT+欧拉定理+Lucas
欧拉定理不要忘记!! #include <bits/stdc++.h> #define N 100000 #define ll long long #define ull unsigned ...
- NOI2013 二叉查找树
题目链接:戳我 对于一个排序二叉树来讲,它的中序遍历对应的序列是可以确定的. 我们知道如果求一个访问频率最低的(也就是没有修改),直接就区间DP即可.\(dp[i][j]=min(dp[i][j],d ...
- jq获取页面中checkbox已经选中的checkbox
var len=$("input[name='bike']:checked").length; //len为0未选中
- ACM之路(19)—— 主席树初探
长春赛的 I 题是主席树,现在稍微的学了一点主席树,也就算入了个门吧= = 简单的来说主席树就是每个节点上面都是一棵线段树,但是这么多线段树会MLE吧?其实我们解决的办法就是有重复的节点给他利用起来, ...
- Python最高效爬虫框架
Overview Scrapy is a fast high-level screen scraping and web crawling framework, used to crawl websi ...
- 后盾网lavarel视频项目---lavarel中的tinker是什么
后盾网lavarel视频项目---lavarel中的tinker是什么 一.总结 一句话总结: 是用来调试laravel,可以打印变量或对象信息,显示函数代码,对数据库写入和查询数据 laravel中 ...
- hotspot 线程状态
- [net]tcp和udp&socket
参考 TCP和UDP连接 关于传输层TCP.UDP协议可能我们平时遇见的会比较多,有人说TCP是安全的,UDP是不安全的,UDP传输比TCP快,那为什么呢,我们先从TCP的连接建立的过程开始分析,然后 ...
- 反编译中的 匿名内部类 this.val$的问题【我改】
转: 匿名内部类 this.val$的问题 一天偶尔在网上找到一个jar包,反编译后出现了如下的代码: public void defineAnonymousInnerClass(String nam ...
- windows文件上传到linux服务器上
https://blog.csdn.net/m0_37751917/article/details/80739850 1:检查是否安装sz rz rpm -qa |grep sz rpm -qa | ...