- JDBC事务管理
- 春天提供编程式的事务管理(编程式事务管理)与声明式的事务管理(声明式事务management),为不同的事务实现提供了一致的编程模型,这节以JDBC事务为例,介绍Spring的事务管理。
- 5.3 。1 春天对事务的支持
- 事务是一组原子(Atomic)操作的工作单元,以数据库存取的实例来说,就是一组SQL指令,这一组SQL指令必须全部执行成功,若因为某个原因未全部执行成功(例如其中一行SQL有错误),则先前所有执行过的SQL指令都会被撤消。
- 举个简单的例子,一个客户从A银行转账至B银行,要作的动作为从A银行的账户扣款、在B银行的账户加上转账的金额,两个动作必须成功,如果有一个动作失败,则此次转账失败。
- 事务还必须保持所参与资源的一致性(Consistent),例如在银行账户的例子中,两个账户的转账金额,B账户取款的金额不能大于A账户的存款金额。每个事务彼此之间必须是隔离的(Isolated),例如在A账户中可能有两笔事务,同时进行存款与提款的动作,两个事务基本上不需意识到彼此的存在。事务还必须是可持续的(Durable),在某一笔事务之后,这笔事务必须是被记录下来的。
- 在这里将介绍JDBC如何使用事务管理。首先来看看事务的原子性实现,在JDBC中,可以操作Connection的setAutoCommit()方法,给定false 参数,在下达一连串的SQL语句后,自行执行Connection的commit()来送出变更,如果中间发生错误,则执行rollback()来撤消所有的执行,例如:
- 在Spring中对JDBC的事务管理加以封装,Spring事务管理的抽象关键在于org.springframework.transaction.PlatformTransactionManager接口的实现:
- ...
- 【JAVA]鉴于plaincopy
- 公共 接口 的PlatformTransactionManager {
- 的TransactionStatus getTransaction(TransactionDefinition的
- 定义) 抛出 TransactionException;
- 无效 提交(TransactionStatus对象的状态)
- 抛出 TransactionException;
- 无效 回滚(TransactionStatus对象的状态)
- 抛出 TransactionException;
- }
- PlatformTransactionManager接口有许多具体的事务实现类,例如DataSourceTransactionManager、HibernateTransactionManager、JdoTransaction- Manager、JtaTransactionManager等,通过依赖于PlatformTransactionManager接口及各种的技术实现,Spring在事务管理上可以让开发人员使用一致的编程模型,即使所使用的是不同的事务管理技术。
- TransactionException是未选中Exception。事务的失败通常都是致命的错误,Spring不强迫您一定要处理,而是让您自行选择是否要捕捉异常。
- getTransaction()级),传播行为(传播
- ...
- 【JAVA]鉴于plaincopy
- 公共 接口 的TransactionStatus {
- 布尔 isNewTransaction();
- 无效 的setRollbackOnly();
- 布尔 isRollbackOnly();
- }
- 春天提供编程式的事务管理(编程式事务管理)与声明式的事务管理(声明式事务管理):
- 升编程式的事务管理
- 编程式的事务管理可以清楚地控制事务的边界,也就是让您自行实现事务开始时间、撤消操作的时机、结束时间等,可以实现细粒度的事务控制。
- 升声明式的事务管理
- 然而多数的情况下,事务并不需要细粒度的控制,而是采用声明式的事务管理,好处是Spring事务管理的相关API可以不用介入程序之中,从对象的角度来看,它并不知道自己正被纳入事务管理之中,在不需要事务管理的时候,只要在设置文件上修改一下设置,即可移去事务管理服务。
- 5.3 。2 JDBC编程事务管理
- 春天提供两种方式实现编程式的事务管理,一是直接使用PlatformTransaction-经理实现,二是使用org.springframework.transaction.support.Transaction-模板。
- 先来看看如何使用PlatformTransactionManager,在这里使用它的实现类DataSourceTransactionManager,可以改写一下之前5.2 . 1 节中的JdbcTemplateDemo项目,让它具有事务管理功能,修改一下UserDAO类的insert()方法来作示范:
- ProgrammaticTransactionDemo UserDAO.java
- 【JAVA]鉴于plaincopy
- 包装 onlyfun.caterpillar;
- 进口 java.util.Iterator的;
- 进口 的java.util.List;
- 进口 的java.util.Map;
- 进口 javax.sql.DataSource的;
- 进口 org.springframework.dao.DataAccessException;
- 进口 org.springframework.jdbc.core.JdbcTemplate;
- 进口 org.springframework.jdbc。
- datasource.DataSourceTransactionManager;
- 进口 org.springframework.transaction.TransactionDefinition;
- 进口 org.springframework.transaction.TransactionStatus;
- 进口 org.springframework.transaction。
- support.DefaultTransactionDefinition;
- 公共 类 的UserDAO 实现 IUserDAO {
- 私人 的DataSourceTransactionManager transactionManager的;
- 私人 DefaultTransactionDefinition DEF;
- 私人 的JdbcTemplate JdbcTemplate的;
- 公共 无效 的setDataSource(数据源数据源){
- 的JdbcTemplate = 新 的JdbcTemplate(数据源);
- transactionManager的=
- 新 的DataSourceTransactionManager(数据源);
- //建立事务的定义
- 高清= 新 DefaultTransactionDefinition();
- def.setPropagationBehavior(
- TransactionDefinition.PROPAGATION_REQUIRED);
- }
- 公共 无效 插入(用户用户){
- 字符串名称= user.getName();
- INT 年龄= user.getAge()的intValue()。
- TransactionStatus对象状态=
- transactionManager.getTransaction(DEF);
- 尝试 {
- jdbcTemplate.update(“INSERT INTO的用户(姓名,年龄)”
- + “VALUES('” +名称+ “',” +年龄+ “)” );
- //下面的SQL有错误,用以测试事务
- jdbcTemplate.update(“INSER INTO用户(姓名,年龄)”
- + “VALUES('” +名称+ “',” +年龄+ “)” );
- }
- 抓(DataAccessException的E){
- transactionManager.rollback(状态);
- 扔 é;
- }
- transactionManager.commit(状态);
- }
- 公众 用户发现(整数ID){
- 列表行= jdbcTemplate.queryForList(
- “SELECT * FROM WHERE用户ID =” + id.intValue());
- 迭代它= rows.iterator();
- 如果(it.hasNext()){
- 地图中userMap =(图)it.next();
- 整数I = 新的 整数(
- userMap.get(“ID” )的ToString());
- 字符串名称= userMap.get( “ 名” )的ToString();
- 整数年龄= 新的 整数(
- userMap.get( “ 时代” )的ToString());
- 用户的用户= 新 用户();
- user.setId(ⅰ);
- user.setName(名);
- user.setAge(年龄);
- 返回 用户;
- }
- 返回 空值;
- }
- }
- 在insert()方法中使用了DataSourceTransactionManager来进行事务管理,如果发生了异常,则catch 区块中会进行事务的Rollback,在insert()方法中故意写入错误的SQL(注意INSERT方法少写了一个T),因此实际上数据并不会被储存至数据库中。
- 要使用MySQL数据库进行事务处理,必须建立支持事务的表格类型,例如InnoDB的表格类型,这里用来建立表格的SQL如下所示:
- 【JAVA]鉴于plaincopy
- CREATE TABLE的用户(
- ID INT(11 )NOT NULL AUTO_INCREMENT PRIMARY KEY,
- 命名VARCHAR(100 )NOT NULL 默认 ' ,
- 年龄INT
- )TYPE = InnoDB的;
- 另一个实现编程式事务管理的方法是使用TransactionTemplate,它需要一个TransactionManager实例,如下所示:
- ...
- 【JAVA]鉴于plaincopy
- TransactionTemplate的TransactionTemplate的=
- 新 TransactionTemplate的(transactionManager的);
- ...
- transactionTemplate.execute(新 TransactionCallback(){
- 公共 对象doInTransaction(TransactionStatus对象的状态){
- 返回 jdbcTemplate.update(“INSERT INTO的用户(姓名,年龄)”
- + “VALUES('” +名称+ “',” +年龄+ “)” );
- }
- });
- 如果发生了异常,则会进行Rollback,否则提交事务,如果没有回传值,则也可以使用TransactionCallbackWithoutResult:
- ...
- 【JAVA]鉴于plaincopy
- transactionTemplate.execute(
- 新 TransactionCallbackWithoutResult(){
- 公共 无效 doInTransactionWithoutResult(
- TransactionStatus对象的状态){
- 。...
- }
- });
- 5.3 。3 JDBC声明事务管理
- Spring声明式的事务管理依赖它的AOP框架来完成。使用声明事务管理的好处是,事务管理不能侵入您所开发的组件,具体来说,DAO对象不会意识到正在事务管理之中,事实上也应当如此,因为事务管理是属于系统层面的服务,而不是业务逻辑的一部分,如果想要改变事务管理策略的话,也只需要在定义文件中重新配置。
- 举个例子来说,可以将5.2 . 1 节中的JdbcTemplateDemo项目修改一下,在不修改UserDAO类的情况下,可以为它加入事务管理的服务,一个简单的方法是使用TransactionProxyFactoryBean,指定要介入的事务管理对象及其方法,这需要在定义文件中修改,如下所示:
- DeclarativeTransactionDemo豆-config.xml中
- 【JAVA]鉴于plaincopy
- <?XML版本= “1.0” 编码= “UTF-8” ?>
- <豆的xmlns = “http://www.springframework.org/schema/beans”
- XMLNS:XSI = “http://www.w3.org/2001/XMLSchema-instance”
- XSI:的schemaLocation =“HTTP://www.springframework.org/schema/beans
- HTTP://www.springframework.org/schema/beans/spring-beans-2.0.xsd“>
- <bean的ID = “数据源”
- 类=“org.springframework.jdbc。
- →datasource.DriverManagerDataSource“
- 破坏法= “关闭” >
- <属性名= “driverClassName”
- 值= “com.mysql.jdbc.Driver” />
- <属性名= “URL”
- 值= “的jdbc:mysql的://本地主机:3306 /演示” />
- <属性名= “用户名” 值= “毛毛虫” />
- <属性名= “密码” 值= “123456” />
- </豆>
- <bean的ID = “transactionManager的”
- 类=“org.springframework.jdbc。
- →datasource.DataSourceTransactionManager“>
- <属性名= “数据源” 参考= “数据源” />
- </豆>
- <bean的ID = “userDAO的”
- 类= “onlyfun.caterpillar.UserDAO” >
- <属性名= “数据源” 参考= “数据源” />
- </豆>
- <bean的ID = “userDAOProxy”
- 类=“org.springframework.transaction。
- →interceptor.TransactionProxyFactoryBean“>
- <属性名= “proxyInterfaces” >
- <目录>
- <值> onlyfun.caterpillar.IUserDAO </值>
- </表>
- </物业>
- <属性名= “目标” 参考= “userDAO的” />
- <属性名= “transactionManager的”
- REF = “transactionManager的” />
- <属性名= “transactionAttributes” >
- <道具>
- <支撑键= “插入*” > PROPAGATION_REQUIRED </道具>
- </道具>
- </物业>
- </豆>
- </豆>
- TransactionProxyFactoryBean需要一个TransactionManager,由于这里使用的是JDBC,所以使用DataSourceTransactionManager,TransactionProxyFactoryBean是个代理对象,"target" 属性指定要代理的对象,事务管理会自动介入指定的方法前后,这里使用 "transactionAttributes" 属性指定,"insert*" 表示指定方法名称以insert开头的都要纳入事务管理,您也可以指定方法全名,如果在方法执行过程中发生错误,则所有先前的操作自动撤回,否则正常提交。
- 在"insert*" 等方法上指定了 "PROPAGATION_REQUIRED" ,表示在目前的事务中执行操作,如果事务不存在就建立一个新的,相关的常数意义都可以在API文件的TransactionDefinition接口中找到。您可以加上多个事务定义,中间使用逗号 "," 区隔,例如可以加上只读,或者是指定某个异常发生时撤回操作:
- PROPAGATION_REQUIRED,只读,-MyCheckedException
- MyCheckedException前面加上 "-" 时,表示发生指定异常时撤消操作,如果前面加上 "+" ,表示发生异常时立即提交。
- 由于"userDAO" 被"userDAOProxy" 代理了,所以要做的是取得"userDAOProxy" ,而不是"userDAO" ,例如:
- DeclarativeTransactionDemo SpringDAODemo.java
- 【JAVA]鉴于plaincopy
- 包装 onlyfun.caterpillar;
- 进口 org.springframework.context.ApplicationContext;
- 进口 org.springframework.context。
- support.ClassPathXmlApplicationContext;
- 公共 类 SpringDAODemo {
- 公共 静态 无效 的主要(字串[] args){
- ApplicationContext的背景下=
- 新 的ClassPathXmlApplicationContext(
- “豆-config.xml文件” );
- 用户的用户= 新 用户();
- user.setName( “ 毛毛虫” );
- user.setAge(新的 整数(30 ));
- IUserDAO userDAO的=
- (IUserDAO)context.getBean(“userDAOProxy” );
- userDAO.insert(用户);
- 用户= userDAO.find(新 整型(1 ));
- 的System.out.println( “ 名” + user.getName());
- }
- }
- 您也可以设置不同的TransactionInterceptor来得到更多的管理细节,例如:
- 【JAVA]鉴于plaincopy
- <?XML版本= “1.0” 编码= “UTF-8” ?>
- <豆的xmlns = “http://www.springframework.org/schema/beans”
- XMLNS:XSI = “http://www.w3.org/2001/XMLSchema-instance”
- XSI:的schemaLocation =“HTTP://www.springframework.org/schema/beans
- HTTP://www.springframework.org/schema/beans/spring-beans-2.0.xsd“>
- <bean的ID = “数据源”
- 类=“org.springframework.jdbc。
- →datasource.DriverManagerDataSource“
- 破坏法= “关闭” >
- <属性名= “driverClassName”
- 值= “com.mysql.jdbc.Driver” />
- <属性名= “URL”
- 值= “的jdbc:mysql的://本地主机:3306 /演示” />
- <属性名= “用户名” 值= “毛毛虫” />
- <属性名= “密码” 值= “123456” />
- </豆>
- <bean的ID = “transactionManager的”
- 类=“org.springframework.jdbc。
- →datasource.DataSourceTransactionManager“>
- <属性名= “数据源” 参考= “数据源” />
- </豆>
- <bean的ID = “userDAO的”
- 类= “onlyfun.caterpillar.UserDAO” >
- <属性名= “数据源” 参考= “数据源” />
- </豆>
- <bean的ID = “transactionInterceptor”
- 类=“org.springframework.transaction。
- →interceptor.TransactionInterceptor“>
- <属性名= “transactionManager的” 参考= “transactionManager的” />
- <属性名= “transactionAttributeSource”
- 值=“onlyfun.caterpillar.UserDAO.insert * =
- →PROPAGATION_REQUIRED“/>
- </豆>
- <bean的ID = “userDAOProxy”
- 类=“org.springframework.aop。
- →framework.ProxyFactoryBean“>
- <属性名= “proxyInterfaces” >
- <目录>
- <值> onlyfun.caterpillar.IUserDAO </值>
- </表>
- </物业>
- <属性名= “目标” 参考= “userDAO的” />
- <属性名= “interceptorNames” >
- <目录>
- <值> transactionInterceptor </值>
- </表>
- </物业>
- </豆>
- </豆>
- 即使后来不再需要事务管理,也可以直接在Bean定义文件中修改配置,而不用修改程序重新进行编译等动作。
- 声明事务管理是利用弹簧AOP来达成的,所以执行以上的程序时,请记得您的Classpath设置中必须包括spring-aop.jar。
- 5.3 。4 事务的属性介绍
- Spring使用AOP来完成声明式的事务管理,因而声明式事务是以方法为边界的,Spring的事务属性(Transaction attribute)自然就在于描述事务应用至方法上的策略,在Spring中事务属性分作以下的几个参数:
- 升传播行为(传播行为)
- 传播行为定义了事务应用于方法上之边界(Boundaries),它告知何时该开始一个新的事务,或何时事务该被暂停,或方法是否要在事务中进行。
- Spring定义了几个传播行为,可以在TransactionDefinition的API文件说明上找到相对应的常数与说明,以下列出几个:
- 表5.1 事务传播行为说明
- 传播行为说明
- PROPAGATION_MANDATORY方法必须在一个现存的事务中进行,否则丢出异常
- PROPAGATION_NESTED在一个嵌入的事务中进行,如果不是,则同PROPAGATION_REQUIRED
- PROPAGATION_NEVER指出不应在事务中进行,如果有就丢出异常
- PROPAGATION_NOT_SUPPORTED指出不应在事务中进行,如果有就暂停现存的事务
- PROPAGATION_REQUIRED支持现在的事务,如果没有就建立一个新的事务
- PROPAGATION_REQUIRES_NEW建立一个新的事务,如果现存一个事务就暂停它
- PROPAGATION_SUPPORTS支持现在的事务,如果没有就以非事务的方式执行
- 举个例子来说,如果传播行为被声明为PROPAGATION_REQUIRED,则事务的边界在开始第一个事务的方法呼叫及结束时,如果先前没有事务被开始,则事务边界即为目前方法的执行前后。又如果传播行为被声明为PROPAGATION_REQUIRES_NEW,则事务的边界即为该方法执行的前后。
- 升隔离层级(隔离级别)
- 在一个应用程序中,可能有多个事务同时在进行,这些事务应当彼此之间互相不知道另一个事务的存在,好比现在整个应用程序就只有一个事务存在,由于事务彼此之间独立,若读取的是同一个数据的话,就容易发生问题,例如:
- ñ脏读
- 某个事务已更新一份数据,另一个事务在此时读取了同一份数据,由于某些原因,前一个Roll回来了操作,则后一个事务所读取的数据就会是不正确的。
- n不重复读
- 在一个事务的两次查询之中数据不一致,这可能是因为两次查询过程中间插入了一个事务更新的原有的数据。
- ñ幻影读
- 在一个事务的两次查询中数据笔数不一致,例如有一个事务查询了几列(Row)数据,而另一个事务却在此时插入了新的几列数据,先前的事务在接下来的查询中,就会发现有几列数据是它先前所没有的。
- 为了避免以上问题的方法之一,需要在某个事务进行过程中锁定正在更新或查询的数据字段,直到目前的事务完成,然而完全锁定字段时,若另一个事务来查询同一份数据就必须等待,直到前一个事务完成并解除锁定为止,因而会造成应用程序在查询或更新数据时效率上的问题,而事实上根据需求的不同,并不用在事务进行时完全地锁定数据,隔离层级可以让您根据实际的需求,对数据的锁定进行设置。
- Spring提供了几种隔离层级设置,同类型的设置可以在TransactionDefinition的API文件说明上找到相对应的常数与说明,以下列出几个:
- 表5.2 事务隔离层级说明
- 隔离层级说明
- ISOLATION_DEFAULT使用底层数据库预设的隔离层级
- ISOLATION_READ_COMMITTED允许事务读取其他并行的事务已经送出(提交)的
- 数据字段,可以防止脏读问题
- ISOLATION_READ_UNCOMMITTED允许事务读取其他并行的事务还没送出的数据,会发
- 生肮脏的,不可重复,幻影读取等问题
- 续表
- 隔离层级说明
- ISOLATION_REPEATABLE_READ要求多次读取的数据必须相同,除非事务本身更新
- 数据,可防止脏污,不可重复读问题
- ISOLATION_SERIALIZABLE完整的隔离层级,可防止脏污,Nonrepeatabl
- E,幻影读取等问题,会锁定对应的数据表
- 格,因而有效率问题
- 升只读提示(只读提示)
- 如果事务只进行读取的动作,则可以利用底层数据库在只读操作时发生的一些最佳化动作,由于这个动作利用到数据库在只读的事务操作最佳化,因而必须在事务中才有效,也就是说要搭配传播行为PROPAGATION_REQUIRED、PROPAGATION_REQUIRES_NEW、PROPAGATION_NESTED来设置。
- 升事务超时期间(交易超时时间)
- 有的事务操作可能延续很长一段的时间,事务本身可能关联到数据表格的锁定,因而长时间的事务操作会有效率上的问题,对于过长的事务操作,您要考虑Roll回事务并要求重新操作,而不是无限时的等待事务完成。
- 您可以设置事务超时期间,计时是从事务开始时,所以这个设置必须搭配传播行为PROPAGATION_REQUIRED、PROPAGATION_REQUIRES_NEW、PROPAGATION_NESTED来设置。
- 5.3 。5 TransactionAttributeSource,TransactionAttribute
- 在TransactionProxyFactoryBean的上有setTransactionAttributeSource()与setTransaction属性()方法,它们是用来设置事务属性的策略实例。
- org.springframework.transaction.interceptor.TransactionAttributeSource接口上有一个getTransactionAttribute()方法,您可以根据传递给它的Method实例与Class实例,决定该回传一个什么内容的org.springframework.transaction. interceptor.TransactionAttribute实例,一个最简单的TransactionAttributeSource实现是org.springframework.transaction.interceptor.MatchAlwaysTransaction- AttributeSource,对于每一个方法执行都会应用事务,它回传的TransactionAttribute实例的默认传播行为是PROPAGATION_REQUIRED,隔离层级为ISOLATION_DEFAULE。
- 一个应用的例子如下所示:
- ...
- 【JAVA]鉴于plaincopy
- <bean的ID = “transactionAttributeSource”
- 类=“org.springframework.transaction.interceptor。
- →的MatchAlwaysTransactionAttributeSource“/>
- <bean的ID = “userDAOProxy”
- 类=“org.springframework.transaction。
- →interceptor.TransactionProxyFactoryBean“>
- <属性名= “proxyInterfaces” >
- <目录>
- <值> onlyfun.caterpillar.IUserDAO </值>
- </表>
- </物业>
- <属性名= “目标” 参考= “userDAO的” />
- <属性名= “transactionManager的” 参考= “transactionManager的” />
- <属性名= “transactionAttributeSource”
- REF = “transactionAttributeSource” />
- </豆>
- ...
- 您可以使用org.springframework.transaction.interceptor.DefaultTransaction-属性,并设置自己的事务策略,之后设置给TransactionAttributeSource,例如:
- ...
- 【JAVA]鉴于plaincopy
- <bean的ID = “myTransactionAttribute”
- 类=“org.springframework.transaction。
- →interceptor.DefaultTransactionAttribute“>
- <属性名= “propagationBehaviorName”
- 值= “PROPAGATION_REQUIRES_NEW” />
- <属性名= “isolationLevelName”
- 值= “ISOLATION_REPEATABLE_READ” />
- </豆>
- <bean的ID = “transactionAttributeSource”
- 类=“org.springframework.transaction。
- →interceptor.MatchAlwaysTransactionAttributeSource“>
- <属性名= “transactionAttribute”
- REF = “myTransactionAttribute” />
- </豆>
- <bean的ID = “userDAOProxy”
- 类=“org.springframework.transaction。
- →interceptor.TransactionProxyFactoryBean“>
- <属性名= “proxyInterfaces” >
- <目录>
- <值> onlyfun.caterpillar.IUserDAO </值>
- </表>
- </物业>
- <属性名= “目标” 参考= “userDAO的” />
- <属性名= “transactionManager的” 参考= “transactionManager的” />
- <属性名= “transactionAttributeSource”
- REF = “transactionAttributeSource” />
- </豆>
- ...
- 可以使用org.springframework.transaction.interceptor.NameMatchTransaction- AttributeSource来指定某些方法要应用事务,以及要应用的事务策略,例如:
- ...
- 【JAVA]鉴于plaincopy
- <bean的ID = “transactionAttributeSource”
- 类=“org.springframework.transaction。
- →interceptor.NameMatchTransactionAttributeSource“>
- <属性名= “属性” >
- <道具>
- <支撑键= “插入*” > PROPAGATION_REQUIRES_NEW </道具>
- </道具>
- </物业>
- </豆>
- <bean的ID = “userDAOProxy”
- 类=“org.springframework.transaction。
- →interceptor.TransactionProxyFactoryBean“>
- <属性名= “proxyInterfaces” >
- <目录>
- <值> onlyfun.caterpillar.IUserDAO </值>
- </表>
- </物业>
- <属性名= “目标” 参考= “userDAO的” />
- <属性名= “transactionManager的” 参考= “transactionManager的” />
- <属性名= “transactionAttributeSource”
- REF = “transactionAttributeSource” />
- </豆>
- ...
- 在NameMatchTransactionAttributeSource的 "properties" 属性上,可以指定方法名称与事务策略,方法名称的指定可以指定全名,也可以使用Wildcard来指定,例如上面的指定中,只要方法名称以insert为开头的都会应用相对应的事务策略。
- 在指定事务策略时,指定的格式如下:
- 传播行为,隔离层级,只读,+异常 - 异常
- 除了传播行为一定要设置之外,其他都可选择性的设置,中间以逗号区隔,例如:
- PROPAGATION_REQUIRED,只读,-MyCheckedException
- MyCheckedException前面加上 "-" 时,表示发生指定异常时撤消操作,如果前面加上 "+" ,表示发生异常时立即提交。
- 在比较简单的设置中,可以仅设置TransactionProxyFactoryBean,并在它的 "transactionAttributes" 属性上直接设置要应用事务的方法及事务策略,例如:
- ...
- 【JAVA]鉴于plaincopy
- <bean的ID = “userDAOProxy”
- 类=“org.springframework.transaction。
- →interceptor.TransactionProxyFactoryBean“>
- <属性名= “proxyInterfaces” >
- <目录>
- <值> onlyfun.caterpillar.IUserDAO </值>
- </表>
- </物业>
- <属性名= “目标” 参考= “userDAO的” />
- <属性名= “transactionManager的” 参考= “transactionManager的” />
- <属性名= “transactionAttributes” >
- <道具>
- <支撑键= “插入*” > PROPAGATION_REQUIRED </道具>
- </道具>
- </物业>
- </豆>
- ...
- 甚至也可以直接指定TransactionInterceptor,以获得更多的控制,例如:
- ...
- 【JAVA]鉴于plaincopy
- <bean的ID = “transactionInterceptor”
- 类=“org.springframework.transaction。
- →interceptor.TransactionInterceptor“>
- <属性名= “transactionManager的” >
- REF = “transactionManager的” />
- <属性名= “transactionAttributeSource”
- 值= “onlyfun.caterpillar.UserDAO.insert * =→PROPAGATION_REQUIRED” />
- </豆>
- <bean的ID = “userDAOProxy”
- 类=“org.springframework.aop。
- →framework.ProxyFactoryBean“>
- <属性名= “proxyInterfaces” >
- <目录>
- <值> onlyfun.caterpillar.IUserDAO </值>
- </表>
- </物业>
- <属性名= “目标” 参考= “userDAO的” />
- <属性名= “interceptorNames” 值= “transactionInterceptor” />
- </豆>
- ...
- 选择哪一种设置方式是需求的问题,您可以尝试在DeclarativeTransactionDemo项目的Bean定义文件上设置以上所介绍的方式,基于篇幅的限制,以上仅列出部分的设置内容。
- 5.3 。6 春季 2.0 声明式事务管理:基于XML Schmea
- 在Spring 2.0 中要设置声明式事务管理,可以依赖于Spring 2.0 的<aop>与<tx>标签,因而要记得加入相关的名称空间声明:
- 【JAVA]鉴于plaincopy
- <?XML版本= “1.0” 编码= “UTF-8” ?>
- <豆的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-2.0.xsd
- HTTP://www.springframework.org/schema/aop
- HTTP://www.springframework.org/schema/aop/spring-aop-2.0.xsd
- HTTP://www.springframework.org/schema/tx
- HTTP://www.springframework.org/schema/tx/spring-tx-2.0.xsd“>
- ...
- </豆>
- 事务是系统层面的服务,也就是一个Aspect,其实具体来说就是一个Advice,您可以使用<tx:advice>标签来提供这个Advice,它需要设置一个TransactionManager,并在当中使用<tx:attributes>来设置事务相关属性。
- 可以将先前的DeclarativeTransactionDemo项目改写,修改其beans-config.xml为使用<aop>与<tx>标签的方式:
- DeclarativeTransactionDemo2豆-config.xml中
- 【JAVA]鉴于plaincopy
- <?XML版本= “1.0” 编码= “UTF-8” ?>
- <豆的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-2.0.xsd
- HTTP://www.springframework.org/schema/aop
- HTTP://www.springframework.org/schema/aop/spring-aop-2.0.xsd
- HTTP://www.springframework.org/schema/tx
- HTTP://www.springframework.org/schema/tx/spring-tx-2.0.xsd“>
- <bean的ID = “数据源”
- 类=“org.springframework.jdbc。
- →datasource.DriverManagerDataSource“
- 破坏法= “关闭” >
- <属性名= “driverClassName”
- 值= “com.mysql.jdbc.Driver” />
- <属性名= “URL”
- 值= “的jdbc:mysql的://本地主机:3306 /演示” />
- <属性名= “用户名” 值= “毛毛虫” />
- <属性名= “密码” 值= “123456” />
- </豆>
- <bean的ID = “transactionManager的”
- 类=“org.springframework.jdbc。
- →datasource.DataSourceTransactionManager“>
- <属性名= “数据源” 参考= “数据源” />
- </豆>
- <bean的ID = “userDAO的”
- 类= “onlyfun.caterpillar.UserDAO” >
- <属性名= “数据源” 参考= “数据源” />
- </豆>
- <TX:建议ID = “txAdvice”
- 交易经理= “transactionManager的” >
- <TX:属性>
- <TX:方法名= “插入*” 的传播= “要求” />
- <TX:方法名= “发现*” 只读= “真” />
- </ TX:属性>
- </ TX:建议>
- <AOP:配置>
- <AOP:切入点ID = “userDAOPointcut”
- 表达式= “执行(* onlyfun.caterpillar.IUserDAO。*(..))” />
- <AOP:顾问咨询-REF = “txAdvice”
- 切入点-REF = “userDAOPointcut” />
- </ AOP:配置>
- </豆>
- 注意到<tx:method>中的属性设置,对于传播行为、隔离层级、只读、超时、异常时撤回或提交,都有对应的"propagation" 、"isolation" 、"timeout" 、"read-only" 、"rollback-for" 、"no-rollback-for" 属性可以设置,若不设置,"propagation" 属性默认是"REQUIRE" ,"isolation" 属性默认是"DEFAULT" 、"timeout" 属性默认是"-1" (单位是秒)、"read-only" 属性默认是"false" 。
- 与先前介绍春季 2.0 基于XML Schema的AOP设置相同,由于不再于设置文件中设置代理对象,所以直接取得"userDAO" 实例进行操作即可。
- 5.3 。7 春天 2.0 声明式事务管理:基于注解
- 声明式事务管理在Spring 2.0 中,也支持使用Annotation的标示方式,方法是使用@Transactional 来标示,例如可以将DeclarativeTransactionDemo项目的UserDAO改写,在上头直接标示@Transactional ,并设置相关属性:
- DeclarativeTransactionDemo3 UserDAO.java
- 【JAVA]鉴于plaincopy
- 包装 onlyfun.caterpillar;
- 进口 java.util.Iterator的;
- 进口 的java.util.List;
- 进口 的java.util.Map;
- 进口 javax.sql.DataSource的;
- 进口 org.springframework.jdbc.core.JdbcTemplate;
- 进口 org.springframework.transaction.annotation.Propagation;
- 进口 org.springframework.transaction.annotation.Transactional;
- 公共 类 的UserDAO 实现 IUserDAO {
- 私人 的JdbcTemplate JdbcTemplate的;
- 公共 无效 的setDataSource(数据源数据源){
- 的JdbcTemplate = 新 的JdbcTemplate(数据源);
- }
- @Transactional (传播= Propagation.REQUIRED)
- 公共 无效 插入(用户用户){
- 字符串名称= user.getName();
- INT 年龄= user.getAge()的intValue()。
- jdbcTemplate.update(“INSERT INTO的用户(姓名,年龄)”
- + “VALUES('” +名称+ “',” +年龄+ “)” );
- }
- @Transactional (只读= 真)
- 公众 用户发现(整数ID){
- 列表行= jdbcTemplate.queryForList(
- “SELECT * FROM WHERE用户ID =” + id.intValue());
- 迭代它= rows.iterator();
- 如果(it.hasNext()){
- 地图中userMap =(图)it.next();
- 整数I = 新的 整数(userMap.get(“ID” )的ToString());
- 字符串名称= userMap.get( “ 名” )的ToString();
- 整数年龄=
- 新的 整数(userMap.get( “ 时代” )的ToString());
- 用户的用户= 新 用户();
- user.setId(ⅰ);
- user.setName(名);
- user.setAge(年龄);
- 返回 用户;
- }
- 返回 空值;
- }
- }
- 在使用@Transactional 时,相关的属性设置为"propagation" 、"isolation" 、"readOnly" 、"timeout" 、"rollbackFor" 、"noRollbackFor" 等,而在beans-config.xml中,则要使用<tx:annotation-driven>标签,并指定TransactionManager,例如:
- DeclarativeTransactionDemo3豆-config.xml中
- 【JAVA]鉴于plaincopy
- <?XML版本= “1.0” 编码= “UTF-8” ?>
- <豆的xmlns = “http://www.springframework.org/schema/beans”
- XMLNS:XSI = “http://www.w3.org/2001/XMLSchema-instance”
- 的xmlns:TX = “http://www.springframework.org/schema/tx”
- XSI:的schemaLocation =“HTTP://www.springframework.org/schema/beans
- HTTP://www.springframework.org/schema/beans/spring-beans-2.0.xsd
- HTTP://www.springframework.org/schema/tx
- HTTP://www.springframework.org/schema/tx/spring-tx-2.0.xsd“>
- <bean的ID = “数据源”
- 类=“org.springframework.jdbc。
- →datasource.DriverManagerDataSource“
- 破坏法= “关闭” >
- <属性名= “driverClassName”
- 值= “com.mysql.jdbc.Driver” />
- <属性名= “URL”
- 值= “的jdbc:mysql的://本地主机:3306 /演示” />
- <属性名= “用户名” 值= “毛毛虫” />
- <属性名= “密码” 值= “123456” />
- </豆>
- <bean的ID = “transactionManager的”
- 类=“org.springframework.jdbc。
- →datasource.DataSourceTransactionManager“>
- <属性名= “数据源” 参考= “数据源” />
- </豆>
- <bean的ID = “userDAO的”
- 类= “onlyfun.caterpillar.UserDAO” >
- <属性名= “数据源” 参考= “数据源” />
- </豆>
- <TX:注解驱动的事务管理器= “transactionManager的” />
- </豆>
- 同样的,由于不再于设置文件中设置代理对象,所以直接取得"userDAO" 实例进行操作即可。