Spring的声明式事务示例 2.docx
《Spring的声明式事务示例 2.docx》由会员分享,可在线阅读,更多相关《Spring的声明式事务示例 2.docx(16页珍藏版)》请在冰豆网上搜索。
![Spring的声明式事务示例 2.docx](https://file1.bdocx.com/fileroot1/2023-2/5/c7150b2e-88de-4f70-ba93-d39d7cc32fc4/c7150b2e-88de-4f70-ba93-d39d7cc32fc41.gif)
Spring的声明式事务示例2
Spring的声明式事务示例2
Spring的声明式事务示例
(2)2011年01月10日星期一09:
43beanid="myDataSource"class="mons.dbcp.BasicDataSource"
destroy-method="close"
!
--resultsinasetDriverClassName(String)call--
propertyname="driverClassName"value="com.mysql.jdbc.Driver"/
propertyname="url"value="jdbc:
mysql:
//localhost:
3306/test"/
propertyname="username"value="root"/
propertyname="password"value="manager"/
propertyname="initialSize"value="4"/
propertyname="maxActive"value="20"/
propertyname="testWhileIdle"value="true"/
propertyname="validationQuery"value="select1"/
propertyname="testOnBorrow"value="true"/
/beanbeanid="myDataSource_sec"class="mons.dbcp.BasicDataSource"
destroy-method="close"
propertyname="driverClassName"value="com.mysql.jdbc.Driver"/
propertyname="url"value="jdbc:
mysql:
//localhost:
3306/test_sec"/
propertyname="username"value="root"/
propertyname="password"value="manager"/
propertyname="initialSize"value="4"/
propertyname="maxActive"value="20"/
propertyname="testWhileIdle"value="true"/
propertyname="validationQuery"value="select1"/
propertyname="testOnBorrow"value="true"/
/beanbeanid="jdbcTransactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager"
propertyname="dataSource"ref="myDataSource"/
/bean
/beans2.3创建数据库test
表:
class(班级)
字段名字段类型说明
Idint(11)班级IdclassNamevarchar(10)班级名称
表:
student(学生)
字段名字段类型说明
Idint(11)学生IdstudentNamevarchar(10)学生姓名
classIdint(11)班级Id3、运行示例
3.1编译程序
首先使用maven编译程序,下载所需的项目包。
所需的spring包需要手动考到SSI-ibatis2\src\main\webapp\WEB-INF\lib下。
3.2运行
启动tomcat,输入示例地址:
正确数据录入
输入正确的数据后,点击提交,页面如下:
class表的信息如下:
Student表信息如下:
3.2.2程序抛出异常测试
Student字段输入"2",在BO代码中(StudentBOImpl.java),会抛出Exception。
如红色部分代码:
…
@OverridepublicvoidinsertStudent(MapString,ObjectparaMap)throwsException{
intclassId=this.getStudentDAO().insertClass(paraMap);
MapString,Objectmap=paraMap;
map.put("classId",classId);
if(((String)map.get("studentName")).length()2){
thrownewException("Thestudentnameissimple!
");
}
this.getStudentDAO().insertStudent(map);
}
…
抛出异常信息:
java.lang.Exception:
Thestudentnameissimple!
com.nhn.ssi.bo.StudentBOImpl.insertStudent(StudentBOImpl.java:
16)
…
class表的信息如下:
Student表信息如下:
3.2.3取消回滚异常测试
修改applicationContext-db.xml文件的事务策略配置,删除insert*方法中的rollback配置:
…
tx:
adviceid="txAdvice"
transaction-manager="jdbcTransactionManager"
tx:
attributestx:
methodname="insert*"/
tx:
methodname="get*"read-only="true"/
tx:
methodname="*"/
/tx:
attributes
/tx:
advice
…
运行程序后,如下:
异常还会抛出,抛出的异常信息也相同。
但是数据库的结果却不一样:
class表的信息:
Student表信息:
通过对比发现,取消rollback设置后,抛出该异常后,class表信息添加了,事务没有执行。
3.2.4运行时异常出现后的测试
由于数据库设置studentName的长度为10,所以当输入超过10个字符后,会抛出运行时异常。
页面如下:
点击提交后,出现如下异常:
org.springframework.dao.DataIntegrityViolationException:
SqlMapClientoperation;SQL;
---Theerroroccurredindatasource/sql/student.xml.
---Theerroroccurredwhileapplyingaparametermap.
---ChecktheinsertStudent-InlineParameterMap.
---Checkthestatement(updatefailed).
---Cause:
com.mysql.jdbc.MysqlDataTruncation:
Datatruncation:
Datatoolongforcolumn'studentName'atrow1;nestedexceptionismon.jdbc.exception.NestedSQLException:
---Theerroroccurredindatasource/sql/student.xml.
---Theerroroccurredwhileapplyingaparametermap.
---ChecktheinsertStudent-InlineParameterMap.
---Checkthestatement(updatefailed).
---Cause:
com.mysql.jdbc.MysqlDataTruncation:
Datatruncation:
Datatoolongforcolumn'studentName'atrow1
org.springframework.jdbc.support.SQLStateSQLExceptionTranslator.doTranslate(SQLStateSQLExceptionTranslator.java:
101)
org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:
72)
org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:
80)
org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:
80)
org.springframework.orm.ibatis.SqlMapClientTemplate.execute(SqlMapClientTemplate.java:
203)
org.springframework.orm.ibatis.SqlMapClientTemplate.insert(SqlMapClientTemplate.java:
364)
com.nhn.ssi.dao.StudentDAOImpl.insertStudent(StudentDAOImpl.java:
19)
com.nhn.ssi.bo.StudentBOImpl.insertStudent(StudentBOImpl.java:
18)
sun.reflect.NativeMethodAccessorImpl.invoke0(NativeMethod)
sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:
39)
sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:
25)
java.lang.reflect.Method.invoke(Method.java:
597)
org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:
307)
org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:
183)
org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:
150)
org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:
107)
org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:
172)
org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:
89)
org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:
172)
org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:
202)
$Proxy5.insertStudent(UnknownSource)
com.nhn.ssi.action.StudentAction.save(StudentAction.java:
25)
sun.reflect.NativeMethodAccessorImpl.invoke0(NativeMethod)
sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:
39)
sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:
25)
java.lang.reflect.Method.invoke(Method.java:
597)
…
数据库结果如下:
class表的信息:
Student表信息:
发现,抛出异常后,exaple3并没有插到class表,事务执行了。
Spring管理事务,运行时的异常会自动回滚。
4、声明式事务管理的相关配置说明
Spring没有直接管理事务,事实上,它有很多供选择的事务管理器,将事务管理的责任委托给使用JTA或持久化机制的某个特定平台的事务实现。
这些管理器都是org.springframework.transaction.PlatformTransactionManager接口的实例。
4.1事务管理器的定义
表:
Spring针对不同事务可选的事务实现
事务管理器实现目标
org.springframework.jdbc.datasource.DataSourceTransactionManager在单一的JDBCDataSource中管理事务
mons.dbcp.BasicDataSource使用dbcp(JDBC核心类)管理事务
org.springframework.orm.hibernate3.LocalSessionFactoryBean当持久化机制是Hibernate3时,用它来管理事务
org.springframework.orm.jdo.LocalPersistenceManagerFactoryBean当JDO用作持久化时,用它来管理事务
org.springframework.orm.jpa.LocalEntityManagerFactoryBean当JPA用作持久化时,用它来管理事务
每种事务管理器都充当了对特定平台的事务实现的代理。
这样你就只要和Spring中的事务打交道,而不用关心实际上事务实现是什么样的。
要使用一个事务管理器,你得在应用上下文中声明它。
例如声明JDBC事务管理器:
beanid="transactionManager"class="org.springframework.jdbc.datasource.DataSourceTransactionManager"
propertyname="dataSource"
refbean="dataSource"/
/property
/bean4.2Spring事务管理策略如何定义
Spring声明式事务应用封装在tx:
advice/标签中。
tx:
advice/标签中的transaction-manager属性设置PlatformTransactionManagerbean(管理事务的bean)的名字,用它来驱动事务。
如果你把PlatformTransactionManager的名字命名为transactionManager,你可以省略事务通知(即tx:
advice/标签)标签的transaction-manager属性。
aop:
config/定义确保事务的通知(advice)定义由txAdvicebean在程序中合适的点执行。
首先,定义了一个切入点,配合定义的Service接口(选项定义执行,然后我们使用advisor(顾问)关联切入点和事务通知器。
关于AOP更多的知识,参考AOP学习部分。
4.2.1tx:
advice/设置
默认的td:
advice/设置是:
传播(Propagation)设置:
需要(REQUIRED)
隔离级别:
默认(DEFAULT)
事务:
读/写(read/write)
事务超时:
默认为基本事务系统的默认超时,或无(如果不支持超时)
任何运行时异常(RuntimeException)触发回滚,任何检查的异常不触发。
你可以修改默认的设置;嵌套在tx:
advice/和tx:
attributes/标签内的tx:
method/标签的各种属性摘录如下:
属性是否必须默认描述
name是方法名和事务属性是有关联的。
通配符(*)字符可用于很多关联到相同事务属性的方法设置。
如get*、handle*、on*Event等等。
propagation否REQUIRED事务传播行为
isolation否DEFAULT事务隔离级别
timeout否-1事务超时的值(时间,单位秒)
read-only否false设置事务是否为只读
rollback-for否触发回滚的异常;多个异常用逗号分割。
如:
com.foo.MyBusinessException,ServletExceptionno-rollback-for否不触发回滚的异常,多个异常用逗号分割。
如:
com.foo.MyBusinessException,ServletException4.2.2传播行为
传播行为定义了关于客户端和被调用方法的事务边界。
Spring定义了7种截然不同的传播行为。
传播行为意义
PROPAGATION_MANDATORY表示该方法必须运行在一个事务中。
如果当前事务不存在,将抛出一个异常
PROPAGATION_NESTED表示如果当前已存在一个事务,则该方法应当运行在一个嵌套的事务中。
被嵌套的事务可以从当前事务中单独的提交或回滚。
如果当前事务不存在,那么它看起来就和PROPAGATION_REQUIRED没两样。
请注意各厂商对于这种传播行为的支持是参差不齐的。
请参考资源管理器的文档,确定它们是否支持嵌套事务
PROPAGATION_NEVER表示当前方法不应该运行在一个事务上下文中。
如果当前存在一个事务,则会抛出一个异常
PROPAGATION_NOT_SUPPORTED表示该方法不应在事务中运行。
如果一个现有的事务正在进行中,它将在该方法的运行期间被挂起。
PROPAGATION_REQUIRED表示当前方法必须运行在一个事务中。
如果一个现有的事务正在进行中,该方法将运行在这个事务中。
否则的话,就要开始一个新的事务
PROPAGATION_REQUIRES_NEW表示当前方法必须运行在它自己的事务里。
它将启动一个新的事务。
如果一个现有事务在运行的话,将在这个方法运行期间被挂起。
PROPAGATION_SUPPORTS表示当前方法不需要事务处理环境,但如果有一个事务已经在运行的话,这个方法也可以在这个事务里运行
表Spring的事务传播规则
上表中大部分传播行为看上去很相似,那是因为它们分别对应了EJB容器管理的事务(CMT)中所有的传播规则。
传播规则回答了一个问题,就是新的事务是否要被启动或是被挂起,或者方法是否要在事务环境中运行。
4.2.3隔离级别
在一个典型的应用中,多个事务并发运行,经常会操作同一个数据来完成他们的任务。
并发虽然是必需的,但是会导致下列问题:
脏读(Dirtyread)--脏读发生在一个事务读取了被另一个改写但还未提交的数据时。
如果这些改变在稍后被回滚,那么第一个事务读取的数据就是无效的。
不可重复读(Nonrepeatableread)--不可重复读发生在一个事务执行的查询两次或两次以上,但每一次查询结果都不同时。
这通常是由于另外一个并发事务在两次查询之间更新了数据。
幻读(Phantomread)--幻读和不可重复读相似。
当一个事务读取几行记录后,另一个并发事务插入一些记录,幻读就发生了。
在后来的查询中,第一个事务就会发现一些原来没有的额外记录。
在理想状态下,事务要被完全相互隔离,以防止这些问题发生。
然而,完全隔离会影响性能,因为隔离经常牵涉到锁定在数据库中的记录(有时是锁定完整的数据表)。
侵占性的锁定会阻碍并发,要求事务互相等待来完成工作。
应该认识到完全隔离会影响性能,并且不是所有应用都要完全隔离,有时应用需要在事务隔离上有些弹性。
因此就有好几个隔离级别,如下表:
隔离级别含义
ISOLATION_DEFAULT使用后端数据库默认的隔离级别
ISOLATION_READ_UNCOMMITTED允许你读取还未提交的改变了的数据。
可能导致脏读、幻读或不可重复读
ISOLATION_READ_COMMITTED允许在并发事务已经提交后读取。
可防止脏读、幻读或不可重复读
ISOLATION_REPEATABLE_READ对相同字段的多次读取的结果是一致的,除非数据被事务本身改变。
可防止脏读和不可重复读,但幻读仍可能发生
ISOLATION_SERIALIZABLE完全服从ACID的隔离级别,确保不发生脏读、不可重复读和幻读。
这在所有隔离级别中也是最慢的,因为它是典型的通过完全锁定在事务中涉及的数据表来完成的
表Spring的事务隔离级别
ISOLATION_READ_UNCOMMITTED是最高效的隔离级别,但是事务的隔离程度也是最低的,事务对脏读、不可重复读和幻读是开放的。
在另一个极端,ISOLATION_SERIALIZABLE防止任何形式的隔离问题,但效率是最低的。
要知道并不是所有的事务管理器都支持表三所列的隔离级别,请参考你的事务管理器文档,来确定有哪些可使用的隔离级别。
4.2.4只读
如果一个事务只对后端数据库执行读操作,数据库就可以利用事务的只读特性,使用某些优化措施。
通过声明一个事务为只读,你就给了后端数据库一个机会,来应用那些它认为合适的优化措施。
因为只读的优化措施是在事务启动时由后端数据库实施的,所以,只有将那些具有可能启动新事物的传播行为的方法的事务标记成只读才有意义(PROPAGATON_REQUIRED,PROPAGATION_REQUIRED_NEW和PROPAGATION_NEXTED)。
此外,如果你使用Hibernate作为持久化机制,声明一个只读事务将使Hibernate的flush模式设置为FLUSH_NEVER。
这就告诉Hibernate避免和数据库进行不必要的对象同步,将所有更新延迟到事务的结束。
4.2.5事务超时
最后,在事务中你可以选择设置的另外一