Spring的事务管理简化了传统的事务管理流程,提升了开发效率。可是首先先要了解Spring的数据库编程。java
数据库编程是互联网编程的基础,Spring框架为开发者提供了JDBC模板模式,即jdbcTemplate,它能够简化许多代码,但在实际应用中jdbcTemplate使用并不常见,在大多数时候都采用Spring结合MyBatis进行开发。在这里,只讲述Spring的jdbcTemplate开发。mysql
本节Spring数据库编程主要使用的是SpringJDBC模块的core和DataSource包,core是JDBC的核心包,包括经常使用的JdbcTemplate类,DataSource是访问数据源的工具类包。若是要使用SpringJDBC操做数据库,须要进行配置,配置以下:spring
<!--配置数据源--> <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <!--Mysql驱动--> <property name="driverClassName" value="com.mysql.jdbc.Driver"/> <!--链接的url--> <property name="url" value="jdbc:mysql://localhost:3306/spring?characterEncoding=utf-8"/> <!--用户名密码的配置--> <property name="username" value="root"/> <property name="password" value="root"/> </bean> <!--配置JDBC模板--> <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> <property name="dataSource" ref="dataSource"/> </bean>
在上述示例中,配置JDBC模板须要将dataSource注入到jdbcTemplate,而在数据访问层(Dao)中须要使用jdbcTemplate时也须要将jdbcTemplate注入到对应的bean中。sql
@Repository("testDao") public class TestDaoImpl implements TestDao { @Autowired //按照类型注入 private JdbcTemplate jdbcTemplate;
public int update(String sql,Object args[])
:该方法能够对数据表进行增长、修改、删除。使用args[]设置参数,函数返回的是更新的行数。示例以下:数据库
String insertSQL="insert into user values(NULL,?,?)"; Onject param[] = {"chencheng","m"}; jdbcTemplate.update(insertSQL,param);
public List<T> query(String sql,RowMapper<T> rowmapper,Object args[])
:该方法能够对数据表进行查询操做,rowMapper将结果集映射到用户自定义的类中(前提是类的属性名与字段名相同)。示例以下:express
jdbcTemplate.query(sql,new BeanPropertyRowMapper<User>(User.class),param);
<?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: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/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"> <!--指定须要扫描的包--> <context:component-scan base-package="com.ch5"/> <!--配置数据源--> <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <!--Mysql驱动--> <property name="driverClassName" value="com.mysql.jdbc.Driver"/> <!--链接的url--> <property name="url" value="jdbc:mysql://localhost:3306/spring?characterEncoding=utf-8"/> <!--用户名密码的配置--> <property name="username" value="root"/> <property name="password" value="root"/> </bean> <!--配置JDBC模板--> <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> <property name="dataSource" ref="dataSource"/> </bean> </beans>
package com.ch5; public class User { 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 "User{" + "id=" + id + ", name='" + name + '\'' + ", money=" + money + '}'; } }
package com.ch5.dao; import com.ch5.User; import java.util.List; public interface TestDao { public int update(String sql,Object[] param); public List<User> query(String sql,Object[] param); }
package com.ch5.dao.Impl; import com.ch5.User; import com.ch5.dao.TestDao; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jdbc.core.BeanPropertyRowMapper; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.stereotype.Repository; import java.util.List; @Repository("testDao") public class TestDaoImpl implements TestDao { @Autowired //按照类型注入 private JdbcTemplate jdbcTemplate; @Override public int update(String sql, Object[] param) { return jdbcTemplate.update(sql,param); } @Override public List<User> query(String sql, Object[] param) { return jdbcTemplate.query(sql,new BeanPropertyRowMapper<User>(User.class),param); } }
package com.ch5.Test; import com.ch5.User; import com.ch5.dao.TestDao; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import java.util.List; public class JdbcTemplateTest { public static void main(String[] args) { ApplicationContext appCo = new ClassPathXmlApplicationContext("appliationContext.xml"); TestDao testDao=(TestDao)appCo.getBean("testDao"); String insertSql="insert into account values(null,?,?)"; Object param[] = {"chencheng",1050.0}; testDao.update(insertSql,param); String selectSql="select * from account"; List<User> list=testDao.query(selectSql,null); for (User user : list) { System.out.println(user); } } }
在代码中显式的调用beginTransaction
、commit
、rollback
等与事务处理相关的方法,这就是编程式事务管理,当只有少数事务操做时,编程式事务管理才比较适合。编程
package com.itheima.utils; /** * 和事务管理相关的工具类,它包含了,开启事务,提交事务,回滚事务和释放链接 */ public class TransactionManager { private ConnectionUtils connectionUtils; public void setConnectionUtils(ConnectionUtils connectionUtils) { this.connectionUtils = connectionUtils; } /** * 开启事务 */ public void beginTransaction(){ try { connectionUtils.getThreadConnection().setAutoCommit(false); }catch (Exception e){ e.printStackTrace(); } } /** * 提交事务 */ public void commit(){ try { connectionUtils.getThreadConnection().commit(); }catch (Exception e){ e.printStackTrace(); } } /** * 回滚事务 */ public void rollback(){ try { connectionUtils.getThreadConnection().rollback(); }catch (Exception e){ e.printStackTrace(); } } /** * 释放链接 */ public void release(){ try { connectionUtils.getThreadConnection().close();//还回链接池中 connectionUtils.removeConnection(); }catch (Exception e){ e.printStackTrace(); } } }
<!-- 配置事务管理器--> <bean id="txManager" class="com.itheima.utils.TransactionManager"> <!-- 注入ConnectionUtils --> <property name="connectionUtils" ref="connectionUtils"></property> </bean> <aop:config> <aop:pointcut id="pt1" expression="execution(* com.itheima.service.impl.*.*(..)"/> <aop:aspect id="txAdvice" ref="txManager"> <!--配置前置事务,开启事务--> <aop:before method="beginTransaction" pointcut-ref="pt1"/> <!--配置后置事务,提交事务--> <aop:after-returning method="commit" pointcut-ref="pt1"/> <!--配置异常事务,回滚事务--> <aop:after-throwing method="rollback" pointcut-ref="pt1"/> <!--配置最终事务,释放链接--> <aop:after method="release" pointcut-ref="pt1"/> </aop:aspect> </aop:config>
基于底层API的编程式事务管理就是根据PlatformTransactionManager
,TransactionDefinition
和TeansactionStatus
等几个核心接口,经过编程的方式进行事务管理,下面经过一个实例描述底层API的事务管理实现:api
<!--配置事务管理器--> <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"/> </bean>
package com.ch5.dao.Impl; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.datasource.DataSourceTransactionManager; import org.springframework.stereotype.Repository; import org.springframework.transaction.TransactionDefinition; import org.springframework.transaction.TransactionStatus; import org.springframework.transaction.support.DefaultTransactionDefinition; @Repository("codeTransaction") public class CodeTransaction { @Autowired private JdbcTemplate jdbcTemplate; @Autowired private DataSourceTransactionManager transactionManager; public String testTransaction(){ //默认事务定义 TransactionDefinition definition=new DefaultTransactionDefinition(); //开启事务 TransactionStatus transactionStatus = transactionManager.getTransaction(definition); String message="执行成功,没有回滚"; try{ String sql = "delete * from account"; String insertSql = "insert into account values(?,?,?)"; Object param[] = {"1","chenheng",2000}; jdbcTemplate.update(sql); //id重复,所以发生错误。 jdbcTemplate.update(insertSql,param); jdbcTemplate.update(insertSql,param); //提交事务 transactionManager.commit(transactionStatus); }catch (Exception e){ //出现异常,回滚 transactionManager.rollback(transactionStatus); message="事务回滚"; e.printStackTrace(); } return message; } }
package com.ch5.Test; import com.ch5.dao.Impl.CodeTransaction; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class TransactionMangagerTest { public static void main(String[] args) { ApplicationContext appCo=new ClassPathXmlApplicationContext("appliationContext.xml"); CodeTransaction codeTransaction = (CodeTransaction)appCo.getBean("codeTransaction"); String result = codeTransaction.testTransaction(); System.out.println(result); } }
TransactionTemplate
的编程式事务管理事务处理的代码散落在业务逻辑代码中,破坏了原有代码的条理性,而且每个事务都会有相似的启动事务,提交以及回滚事务的代码。app
TransactionTemplate
的excute
方法有一个TransactionCallback
接口类型的参数,该接口定义了一个DoInTransaction
的方法,一般以匿名内部类的方式实现TransactionCallback
接口,并在其doInTransaction
方法中写业务逻辑代码。在这里可使用默认的事务提交和回滚规则,在业务代码中不须要显式调用任何事务处理的API,doInTransaction
方法有一个TransactionStatus
类型的参数,能够在方法的任何位置调用该参数的setRollbackOnly
方法将事务标识为回滚,以执行事务回滚。框架
根据默认规则,若是在执行回调方法的过程当中抛出未检查异常,或者显式调用了setRollbackOnly
方法,则回滚事务;若是事务执行完成或者抛出了checked类型的异常,则提交事务。
基于TransactionTemplate
的编程式事务管理的步骤以下:
springframwork
提供的org,springframework,transaction.support.TransactionTemplate
类为事务管理器添加事务模板。完整的配置文件以下:<?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: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/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"> <!--指定须要扫描的包--> <context:component-scan base-package="com.ch5"/> <!--配置数据源--> <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <!--Mysql驱动--> <property name="driverClassName" value="com.mysql.jdbc.Driver"/> <!--链接的url--> <property name="url" value="jdbc:mysql://localhost:3306/spring?characterEncoding=utf-8"/> <!--用户名密码的配置--> <property name="username" value="root"/> <property name="password" value="root"/> </bean> <!--配置事务管理器--> <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"/> </bean> <!--为事务管理器txManager建立transactionTemplate--> <bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate"> <property name="transactionManager" ref="txManager"/> </bean> <!--配置JDBC模板--> <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> <property name="dataSource" ref="dataSource"/> </bean> </beans>
TransactionTemplateDao
package com.ch5; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.stereotype.Repository; import org.springframework.transaction.TransactionStatus; import org.springframework.transaction.support.TransactionCallback; import org.springframework.transaction.support.TransactionTemplate; @Repository("transactionTemplateDao") public class TransactionTemplateDao { @Autowired private JdbcTemplate jdbcTemplate; @Autowired private TransactionTemplate transactionTemplate; String message = ""; public String TransactionTemplateTest(){ //以你命好内部类的方式实现TransactionCallback接口。使用默认的事务规则。 transactionTemplate.execute(new TransactionCallback<Object>() { @Override public Object doInTransaction(TransactionStatus status) { String insertSql = "insert into account values(?,?,?)"; Object param[] = {9,"chen",5000.0}; try{ jdbcTemplate.update(insertSql,param); jdbcTemplate.update(insertSql,param); message="执行成功,未回滚"; }catch (Exception e){ message="事务回滚"; } return message; } }); return message; } }
TransactionTemplateDaoTest
package com.ch5.Test; import com.ch5.TransactionTemplateDao; import com.ch5.dao.Impl.CodeTransaction; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class TransactionTemplateDaoTest { public static void main(String[] args) { ApplicationContext appCo=new ClassPathXmlApplicationContext("appliationContext.xml"); TransactionTemplateDao transactionTemplateDao = appCo.getBean("transactionTemplateDao", TransactionTemplateDao.class); String result = transactionTemplateDao.TransactionTemplateTest(); System.out.println(result); } }
Spring的声明式事务管理是经过AOP技术实现的事务管理,其本质是对方法先后拦截,而后再目标方法开始以前建立一个事务,在执行完成后提交或回滚事务。
与编程式事务管理相比较,声明式事务惟一不足的地方是最细粒度只能做用到方法级别,没法作到像编程式事务管理那样能够做用到代码块级别,但即使有这样的须要,能够经过变通方法进行解决。例如能够将要进行事务处理的代码块单独封装为方法。
Spring声明式事务管理能够经过两种方式实现,一是基于XML方式,二是基于@Transactional
注解的方式
基于XML方式的声明式事务管理是经过在配置文件中配置事务规则的相关声明来实现的。Spring提供了tx命名空间来配置事务管理,提供了<tx:advice>
元素来配置事务的通知,在配置<tx:advice>
时通常要指定id和transaction-manager属性,其中id是配置文件的惟一标识。transaction-manager指定了事务管理器。另外还须要配置<tx:attributes>
子元素,该子元素可配置多个<tx:method>
子元素决定执行事务的细节。
在<tx:advice>
元素配置了事务的加强处理后就能够经过编写AOP配置让Spring自动对目标对象生成代理,下面经过实例演示XML方式让Spring实现声明式事务管理。为了体现事务管理的流程,建立Dao、Service、Controller3层实现。
package statment.dao; public interface TestDao { public int save(String sql,Object param[]); public int delete(String sql,Object param[]); }
package statment.dao.Impl; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.stereotype.Repository; import statment.dao.TestDao; @Repository("testDao") public class TestDaoImpl implements TestDao { @Autowired private JdbcTemplate jdbcTemplate; public int save(String sql, Object[] param) { return jdbcTemplate.update(sql,param); } public int delete(String sql, Object[] param) { return jdbcTemplate.update(sql,param); } }
package statment.Service; public interface TestService { public void test(); }
package statment.Service.Impl; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import statment.Service.TestService; import statment.dao.TestDao; @Service("testService") public class TestServiceImpl implements TestService { @Autowired private TestDao testDao; public void test() { String deleteSql="delete from account"; String saveSql="insert into account values(?,?,?)"; Object param[] = {1,"shitji",5000}; testDao.delete(deleteSql,null); testDao.save(saveSql,param); } }
package statment.controller; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import statment.Service.TestService; @Controller public class StatementController { @Autowired private TestService testService; public void test(){ testService.test(); } }
<?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/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd"> <context:component-scan base-package="statment"/> <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/spring"/> <property name="username" value="root"/> <property name="password" value="root"/> </bean> <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> <property name="dataSource" ref="dataSource"/> </bean> <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"/> </bean> <tx:advice id="myAdvice" transaction-manager="txManager"> <tx:attributes> <tx:method name="*"/> </tx:attributes> </tx:advice> <aop:config> <aop:pointcut id="txPonintCut" expression="execution(* statment.Service.*.*(..))"/> <aop:advisor advice-ref="myAdvice" pointcut-ref="txPonintCut"/> </aop:config> </beans>
package statment.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import statment.controller.StatementController; public class XMLTest { public static void main(String[] args) { ApplicationContext appCo=new ClassPathXmlApplicationContext("bean.xml"); StatementController controller = appCo.getBean("statementController", StatementController.class); controller.test(); } }
package statment.Service.Impl; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import statment.Service.TestService; import statment.dao.TestDao; @Service("testService") @Transactional public class TestServiceImpl implements TestService { @Autowired private TestDao testDao; public void test() { String deleteSql="delete from account"; String saveSql="insert into account values(?,?,?)"; Object param[] = {1,"shitji",5000}; testDao.delete(deleteSql,null); testDao.save(saveSql,param); } }
加入
@Transactional
,就能够指定这个类须要受到Spring的事务管理,注意该注解只针对public修饰的方法添加。
声明式事务处理的流程是:
若是开发者在代码逻辑中加入try...catch
语句,Spring不能在声明式事务处理中正常执行事务的回滚。缘由是Spring只在发生未被捕获的RuntimeException
时才会回滚事务。所以须要处理这种问题。
在基于XML方式的声明式事务管理捕获异常,须要补充两个步骤:
<tx:method name="*" rollback-for="java.lang.Exception"/>
throw new RuntimeException();
@Transactional(rollbackFor = {Exception.class})
throw new RuntimeException();