spring学习笔记(22)声明式事务配置,readOnly无效写无异常
在上一节内容中。我们使用了编程式方法来配置事务,这种优点是我们对每一个方法的控制性非常强。比方我须要用到什么事务,在什么位置假设出现异常须要回滚等。能够进行非常细粒度的配置。但在实际开发中。我们可能并不须要这样细粒度的配置。
还有一方面,假设我们的项目非常大,service层方法非常多。单独为每一个方法配置事务也是一件非常繁琐的事情。并且也可能会造成大量反复代码的冗杂堆积。面对这些缺点,我们首要想到的就是我们spring中的AOP了。
spring声明式事务的实现恰建立在AOP之上。
在这一篇文章中。我们介绍spring的声明式事务配置。
实例分析
声明式事务配置原理相当于使用了围绕增强,拦截目标方法,在其调用前织入我们的事务。然后在调用结束依据执行情况提交或回滚事务。通过横切的逻辑,能够让我们的service层更专注于自身业务逻辑的处理而免去繁琐的事务配置。
配置声明式事务的核心在于配置我们的TransactionProxyFactoryBean和BeanNameAutoProxyCreator。先看以下一个实例配置
事务核心类配置
<bean id="transactionInterceptor"
class="org.springframework.transaction.interceptor.TransactionInterceptor">
<property name="transactionManager" ref="transactionManager" /><!-- 指定一个事务管理器-->
<property name="transactionAttributes"><!-- 配置事务属性 `-->
<props>
<prop key="add*" >PROPAGATION_REQUIRED,-Exception</prop>
<prop key="update*">PROPAGATION_REQUIRED,+Exception</prop>
<prop key="delete*">PROPAGATION_REQUIRED</prop>
<prop key="*">PROPAGATION_REQUIRED</prop>
</props>
</property>
</bean>
<bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
<property name="beanNames"><!-- 配置须要代理的Bean-->
<list>
<value>myBaseServiceImpl</value>
</list>
</property>
<property name="interceptorNames"><!-- 声明拦截器-->
<list>
<value>transactionInterceptor</value>
</list>
</property>
</bean>
<!-- 測试用到的相关依赖-->
<bean id="myBaseDao" class="com.yc.dao.MyBaseDaoImpl">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
<bean id="myBaseServiceImpl" class="com.yc.service.MyBaseServiceImpl">
<property name="myBaseDao" ref="myBaseDao" />
</bean>
属性具体分析
在实例中我们通过配置拦截器和代理生成器。
在配置TransactionInterceptor事务属性时,key相应于方法名,我们以add*来匹配目标类中所有以add开头的方法,在针对目标对象类的方法进行拦截配置事务时。我们依据属性的定义顺序拦截,假设它被key="add*"所在事务属性拦截,即使后面有key="*"能够匹配随意方法,也不会再次被拦截。关于标签内的事务属性格式例如以下:
传播行为 [。隔离级别] [,仅仅读属性] [。超时属性] [,-Exception] [,+Exception]
其中除了传播行为外,其它都是可选的。
每一个属性说明可见下表
| 属性 | 说明 |
|---|---|
| 传播行为 | 取值必须以“PROPAGATION_”开头,具体包含:PROPAGATION_MANDATORY、PROPAGATION_NESTED、PROPAGATION_NEVER、PROPAGATION_NOT_SUPPORTED、PROPAGATION_REQUIRED、PROPAGATION_REQUIRES_NEW、PROPAGATION_SUPPORTS,共七种取值。 |
| 隔离级别 | 取值必须以“ISOLATION_”开头,具体包含:ISOLATION_DEFAULT、ISOLATION_READ_COMMITTED、ISOLATION_READ_UNCOMMITTED、ISOLATION_REPEATABLE_READ、ISOLATION_SERIALIZABLE。共五种取值。 |
| 仅仅读属性 | 假设事务是仅仅读的,那么我们能够指定仅仅读属性,使用“readOnly”指定。
否则我们不须要设置该属性。 |
| 超时属性 | 取值必须以“TIMEOUT_”开头,后面跟一个int类型的值。表示超时时间,单位是秒。 |
| +Exception | 即使事务中抛出了这些类型的异常,事务仍然正常提交。必须在每一个异常的名字前面加上“+”。异常的名字能够是类名的一部分。比方“+RuntimeException”、“+tion”等等。可同一时候指定多个。如+Exception1,+Exception2 |
| -Exception | 当事务中抛出这些类型的异常时。事务将回滚。必须在每一个异常的名字前面加上“-”。异常的名字能够是类名的所有或者部分。比方“-RuntimeException”、“-tion”等等。可同一时候指定多个,如-Exception1,-Exception2 |
从配置文件里能够看出,我们能够配置多个拦截器和多个Bean来适配不同的事务。这种声明式事务使用起来还是非常方便的。
service层配置
使用声明式事务后,相对于上篇文章样例。我们的service层需改写成:
public class MyBaseServiceImpl implements MyBaseService{
private MyBaseDao myBaseDao;
@Override
public void queryUpdateUser(final Integer id,final String newName) {
User user = myBaseDao.queryUnique(User.class, id);
System.out.println(user);
user.setName(newName);
myBaseDao.update(user);
System.out.println(user);
}
public void setMyBaseDao(MyBaseDao myBaseDao) {
this.myBaseDao = myBaseDao;
}
}
可见,我们去除了事务模板的侵入式注入,同一时候还去除了事务(在每一个方法中的)侵入式配置。当然,编程式事务的优点是能将事务配置细粒度到每一个方法其中。。当我们大部分方法的事务还是一致的。我们能够使用声明式事务。针对那些须要独立配置的,我们能够将其排除出声明式事务,然后使用编程式事务或后面我们会提到的注解式事务单独配置。
測试结果和分析
以下,执行我们同样的測试方法:
public class Test1 {
@Test
public void test(){
ApplicationContext ac = new ClassPathXmlApplicationContext("classpath:spring/spring-datasource.xml");
MyBaseServiceImpl myBaseService= (MyBaseServiceImpl) ac.getBean("myBaseServiceImpl");
myBaseService.queryUpdateUser(1, "newName2");
}
}
执行測试方法,会发现报错:
java.lang.ClassCastException: com.sun.proxy.$Proxy8 cannot be cast to com.yc.service.MyBaseServiceImpl
意思是我们的代理类无法转换成我们自己定义的Service实现类。究其原因,是由于我们的BeanNameAutoProxyCreator没有默认使用CGLib代理,这样我们的代理类是利用JDK动态代理基于接口创建的。而非基于类创建,我们有以下两种解决方法:
1. 将代理类转换成MyBaseServiceImpl所实现的接口MyBaseService而非MyBaseServiceImpl:
MyBaseService myBaseService= (MyBaseService) ac.getBean("myBaseServiceImpl");
2. 在BeanNameAutoProxyCreator配置下加入:
<property name="proxyTargetClass" value="true"/>,即
<bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
<property name="proxyTargetClass" value="true"/>
<property name="beanNames">
<list>
<value>myBaseServiceImpl</value>
</list>
</property>
<property name="interceptorNames">
<list>
<value>transactionInterceptor</value>
</list>
</property>
</bean>
然后,再执行測试程序,我们会得到正确的结果,部分信息打印例如以下所看到的:
DEBUG: org.hibernate.engine.jdbc.internal.LogicalConnectionImpl - Obtaining JDBC connection
DEBUG: org.hibernate.engine.jdbc.internal.LogicalConnectionImpl - Obtained JDBC connection
DEBUG: org.hibernate.engine.transaction.spi.AbstractTransactionImpl - begin
DEBUG: org.hibernate.loader.Loader - Done entity load
User [id=1, name=newName]
User [id=1, name=newName2]
DEBUG: org.springframework.orm.hibernate4.HibernateTransactionManager - Initiating transaction commit
DEBUG: org.hibernate.engine.transaction.spi.AbstractTransactionImpl - committing
这和我们使用编程式事务的结果基本是一致的。
拓展測试
如今。在我们的拦截器中略微改动一行:
<prop key="*">PROPAGATION_REQUIRED,readOnly</prop>
我们将其设置为仅仅读模式,这时候。调用我们的測试方法。queryUpdateUser(1,”newName3”)(由于前面測试已将name改动成newName2,为了显示不同的结果,这里射程newName3做參数)。显然。前面的add*,update*,delete*都不能匹配。这时候必然启动key="*"所属事务。执行方法,我们会发现结果:
User [id=1, name=newName2]
User [id=1, name=newName3]
这似乎和我们没设置readOnly应有的结果一致,但我们再次执行,程序没有抛出异常,并且会发现结果仍是:
User [id=1, name=newName2]
User [id=1, name=newName3]
说明我们的改动实际上并没有生效!这时在看DEBUG信息,发如今:
DEBUG: org.hibernate.engine.transaction.spi.AbstractTransactionImpl - begin信息上面多了一行:
DEBUG: org.springframework.jdbc.datasource.DataSourceUtils - Setting JDBC Connection [jdbc:mysql://localhost:3306/yc, UserName=yc@localhost, MySQL Connector Java] read-only
说明当前事务确实为仅仅读模式
归纳
这里单独拿出readOnly来分析。主要是针对实际开发中可能遇到的麻烦。设想我们哪天仅仅读属性配置错了。但我们没发现,而当我们试图进行相应的写数据操作时,发现程序并没有出现异常,但数据不管怎么都写不进去。
这个时候就要好好看看我们的仅仅读属性有没有跑到它不该到的地方去了!
spring学习笔记(22)声明式事务配置,readOnly无效写无异常的更多相关文章
- Spring学习笔记:声明式事务管理增删改查业务
一.关于是事务 以方法为单位,进行事务控制:抛出异常,事务回滚. 最小的执行单位为方法.决定执行成败是通过是否抛出异常来判断的,抛出异常即执行失败 二.声明式事务: 声明式事务(declarative ...
- Spring学习笔记3—声明式事务
1 理解事务 事务:在软件开发领域,全有或全无的操作被称为事务.事务允许我们将几个操作组合成一个要么全部发生要么全部不发生的工作单元. 事务的特性: 原子性:事务是由一个或多个活动所组成的一个工作单元 ...
- spring学习08(声明式事务)
11.声明式事务 11.1 回顾事务 事务在项目开发过程非常重要,涉及到数据的一致性的问题,不容马虎! 事务管理是企业级应用程序开发中必备技术,用来确保数据的完整性和一致性. 事务就是把一系列的动作当 ...
- Spring声明式事务配置详解
Spring支持编程式事务管理和声明式的事务管理. 编程式事务管理 将事务管理代码嵌到业务方法中来控制事务的提交和回滚 缺点:必须在每个事务操作业务逻辑中包含额外的事务管理代码 声明式事务管理 一般情 ...
- 【Spring】——声明式事务配置详解
项目中用到了spring的事务: @Transactional(rollbackFor = Exception.class, transactionManager = "zebraTrans ...
- spring基于xml的声明式事务控制配置步骤
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.sp ...
- spring整合mybatis,ioc容器及声明式事务配置
步骤: 1.创建jdbc.properties文件,用来管理存放连接数据库的相关信息 jdbc.properties:jdbc.user=root jdbc.password=123456 jdbc. ...
- spring boot中的声明式事务管理及编程式事务管理
这几天在做一个功能,具体的情况是这样的: 项目中原有的几个功能模块中有数据上报的功能,现在需要在这几个功能模块的上报之后生成一条消息记录,然后入库,在写个接口供前台来拉取消息记录. 看到这个需求,首先 ...
- 八、Spring之深入理解声明式事务
Spring之深入理解声明式事务 何为事务? 事务就是把一系列的动作当成一个独立的工作单元,这些动作要么全部完成,要么全部不起作用. 事务的四个属性: 1.原子性(atomicity) 事务是原子性操 ...
随机推荐
- 洛谷P1725琪露诺(单调队列优化dp)
P1725 琪露诺 题目描述 在幻想乡,琪露诺是以笨蛋闻名的冰之妖精.某一天,琪露诺又在玩速冻青蛙,就是用冰把青蛙瞬间冻起来.但是这只青蛙比以往的要聪明许多,在琪露诺来之前就已经跑到了河的对岸.于是琪 ...
- Python 38 sql基础
数据库服务器中存放的是 库(文件加) .表(文件) .表里面是记录(一行数据) 增 删 改 查 1.库相关 创建------------------create databa ...
- android service--delphixe 10.3
开发中的陷阱: 1. 别放什么 *.wav文件,这个 服务窗口不能随便放东西,不然铁定出现意想不到的结果,比如 无法运行,因为没 ui界面,随意都不知是啥问题. 2. 不能加载 datamodule ...
- Java NIO Buffer说明
Buffer 有3个重要的参数:位置(position).容量(capactiy).上限(limit) 位置(position): 写:当前缓冲区的位置,将从position的下一个位置写数据. 读: ...
- CentOS7 搭建Kafka(三)工具篇
CentOS7 搭建Kafka(三)工具篇 做为一名懒人,自然不喜欢敲那些命令,一个是容易出错,另外一个是懒得记,能有个工具就最好了,一查还挺多,我们用个最主流的Kafka Manager Kafka ...
- 使用GetInvocationList对委托链进行更多的控制
委托链中所有项都会被调用,因为委托类型的 Invoke 方法包含了对数组中的所有项进行遍历的代码.这是一个很简单的算法.尽管这个简单的算法足以应付很多情形,但也有它的局限性.例如,除了最后一个返回值, ...
- 用 Django2.0 做 简单的BBS(前端用 Bootstrap)
实现目标: 开发首页显示BBS的标题和摘要,点击BBS的标题可跳转到BBS详细页面进行展示. 开发环境及开发工具: Python 3.6.3 Django 2.0 Pycharm 2017.3 实现过 ...
- Android开发笔记(11)——DialogFragment & 点击监听
转载请注明:http://www.cnblogs.com/igoslly/p/6931519.html DialogFragment使用 & 点击监听 /* DialogFragment是用于 ...
- 控件中出现的e.xxxx之类的
在遇到窗体应用程序开发的时候,会在控件事件的后台写一些代码,特别是带e.xxx什么的 C#中的Graphics g = e.Graphics是什么意思? 解释是: Graphics 这个类,比较特殊, ...
- C# 获取所有网卡信息
private void Form1_Load(object sender, EventArgs e) { //获取说有网卡信息 NetworkInterface[] nics = NetworkIn ...