是这样的,我的项目的框架是 之前 公司统一使用的框架,配置都配置好了的。 大神们说是没有问题的, 我简单过了一下,也没有问题。 在项目上线并完成了好久后。。。就在前几天,我的同事小刘居然 发现了一个BUG: 事务没有回滚, 之前的有一个同事也说到,事务配置这样没有问题?我的项目的事务怎么没有回滚? 我还以为 是他的项目 出了什么其他问题,而不是事务配置的问题。。。我在对我的项目进行 测试事务的时候 确实是 事务没有回滚!! 坑爹啊, 项目都完成使用上线了之后,居然 告诉我 事务配置没有起作用了? 坑爹啊,,,,而且是巨坑!!!还好发现的早,而且 可能是之前项目代码搞得 太好了,,出的问题并没有引起事务 那块的注意。 也不知道 是 谁 配置的公司项目框架这么坑爹的!!! 废话不多说。 先上 没有优化时候的配置
<?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:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:jpa="http://www.springframework.org/schema/data/jpa"
xmlns:tx="http://www.springframework.org/schema/tx"
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-3.0.xsd
http://www.springframework.org/schema/data/jpa http://www.springframework.org/schema/data/jpa/spring-jpa-1.0.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd"
default-init-method="init" >
<context:annotation-config />
<import resource="base-db-mybatis.xml"/>
<!-- 每个领域对象的定义 -->
<context:component-scan base-package="com.skg.gl.*" />
<context:component-scan base-package="com.skg.crm.*" />
<context:component-scan base-package="com.skg.crm.service.*" />
<bean id="operationLogListener" class="com.skg.crm.service.listener.OperationLogListener"></bean>
<!--事务配置示例-->
<aop:config proxy-target-class="true">
<aop:advisor advice-ref="txAdvice" pointcut="execution(* com.skg.crm.*.domain.*.*(..)) and !execution(* com.skg.crm.toolkit.domain.*.*(..))" />
</aop:config>
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="get*" read-only="true"/>
<tx:method name="is*" read-only="true"/>
<tx:method name="find*" read-only="true"/>
<tx:method name="query*" read-only="true"/>
<tx:method name="*" isolation="DEFAULT" rollback-for="java.lang.Exception"/>
</tx:attributes>
</tx:advice>
</beans>
以上就是事务配置了。 开始 我 觉得会不会 是 配置的 切点有问题? 认真看了一下:
com.skg.crm.*.domain.*.*(..)
这里是 domian 服务层下面,一层里面的所有类的,所有方法。
坑爹啊,谁配置的啊,肯定有问题啊, 因为 我的项目里面 服务层就 只有 domain 一层,,domain 下面就是 service 类了啊,这样肯定是错的
改为: execution(* com.skg.crm.*.domain..*(..))
即 domain 层 下面 的类,或者是 所有子包下面的类,的所有 service 类都加上 事务控制
刚开始的时候以为,可以了,结果发现测试,,还是事务没有回滚!!! 坑爹了,,,看起来配置没有错啊,怎么就有问题? 郁闷了。 随便百度了一下,随便看看,,,还是没有找到问题啊。 我猜会不会是
proxy-target-class="true" 这个去掉看看
继续测试:
出现了问题 ;
expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations :
忘记加注解,或者注解使用了 类.
晕了,,, 这个就是新手 造成的问题了。。。 比如 使用 注解注入的时候,,没有 引用 service 的接口,,而直接 使用了 serviceImpl 服务层的实现类, 关键是 他还可以用,,,而没有出问题,项目跑起来也没有问题。
比如 : 这样
@Resource
private xxxserviceImpl xxx;
不知道是 那个员工粗心造成的,,, 找到 代码 的相关同事,,, 提醒了一下。。。将错误的注解写法,改过来。
再次启动 服务器的时候又出问题了:
aop要使用 类代理,而不是接口代理
Can not set com.skg.crm.system.domain.UserLoginHI field com.skg.crm.system.web.controller.SystemController.userLoginHi to com.sun.proxy.$Proxy70
这样啊, 原因 proxy-target-class 默认是 false 就是 使用 接口代理的方式来 执行AOP的。
可是由于 项目的原来的一些 架构和其他的代码问题,,,他们写service 按照的是 其他的方式来的, 继承的是 父类的 service ,所以他们是看起来是没有 实现service 接口的。。没办法了 那就必须 配置:
proxy-target-class="true" 了。 可是这样配置之后,还是 原来的那个问题啊,事务没有起作用。
对我这个 对 AOP 事务不熟悉的人来说,百度不到 ,,,我怀疑会不会是写法有问题 ? 改为:
<aop:config poxy-target-class="true" >
<aop:pointcut id="appService"
expression="execution(* com.skg.crm.*.domain..*(..))" />
<aop:advisor advice-ref="txAdvice" pointcut-ref="appService" />
</aop:config>
可是报错了啊,,, 错误是: aop:config 不支持 poxy-target-class="true" 这样的配置,不能加上 poxy-target-class="true" ,,,这个是什么情况 ?
后台错误: cvc-complex-type.3.2.2: 元素 'aop:config' 中不允许出现属性 'poxy-target-class'。
大家看清楚了: 是不是拼错了, 应该是这样的: proxy-target-class="true"
细心点,,肯定是 写错了,,或者复制错了,,,低级错误,,下次要注意小心了。
其实
<aop:config proxy-target-class="true" >
<aop:pointcut id="aopAppService"
expression="execution(* com.skg.crm.*.domain..*(..))" />
<aop:advisor advice-ref="txAdvice" pointcut-ref="aopAppService" />
</aop:config>
这种写法也是可以的。
那问题还是之前的问题,怎么办? 我继续百度,,,耐心的百度,换着方式百度。。。 终于找到了:
参考地址: http://blog.csdn.net/z_dendy/article/details/9446669
由于采用的是SpringMVC、 MyBatis,故统一采用了标注来声明Service、Controller
<span style="color:#FF0000;"><strong>由于服务器启动时的加载配置文件的顺序为web.xml---root-context.xml(Spring的配置文件)---servlet-context.xml(SpringMVC的配置文件),由于
root-context.xml配置文件中Controller会先进行扫描装配,但是此时service还没有进行事务增强处理,得到的将是原样的Service(没有经过事务加强处理,
故而没有事务处理能力)</strong></span>,所以我们必须在root-context.xml中不扫描Controller,配置如下:
<!-- 自动扫描组件,这里要把controler下面的 controller去除,他们是在spring3-servlet.xml中配置的,如果不去除会影响事务管理的。 -->
<context:component-scan base-package="com.sence">
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller" />
</context:component-scan>
在servlet-context.xml中扫描Controller同时不扫描Service,配置如下:
<!-- 扫描所有的controller 但是不扫描service-->
<context:component-scan base-package="com.sence">
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller" />
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Service" />
</context:component-scan>
感谢大神,人家说得很清楚明白了。 于是改为这样:对应的controller 也会 进行修改配置的。
<?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:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:jpa="http://www.springframework.org/schema/data/jpa"
xmlns:tx="http://www.springframework.org/schema/tx"
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-3.1.xsd
http://www.springframework.org/schema/data/jpa http://www.springframework.org/schema/data/jpa/spring-jpa-1.0.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.1.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.2.xsd"
default-init-method="init" >
<context:annotation-config />
<import resource="base-db-mybatis.xml"/>
<!-- 每个领域对象的定义 -->
<context:component-scan base-package="com.skg.crm" >
<!--为了事务管理必须过滤掉controller的注解扫描 -->
<context:exclude-filter type="annotation"
expression="org.springframework.stereotype.Controller" />
</context:component-scan>
<!-- <context:component-scan base-package="com.skg.crm.*" />
<context:component-scan base-package="com.skg.crm.service.*" /> -->
<bean id="operationLogListener" class="com.skg.crm.service.listener.OperationLogListener"></bean>
<!-- <aop:config poxy-target-class="true" >
<aop:pointcut id="appService"
expression="execution(* com.skg.crm.*.domain..*(..))" />
<aop:advisor advice-ref="txAdvice" pointcut-ref="appService" />
</aop:config> -->
<aop:aspectj-autoproxy />
<!--事务配置示例-->
<aop:config proxy-target-class="true">
<aop:advisor advice-ref="txAdvice" pointcut="execution(* com.skg.crm.*.domain..*(..)) " />
</aop:config>
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="get*" read-only="true" rollback-for="java.lang.Exception" />
<tx:method name="is*" read-only="true" rollback-for="java.lang.Exception" />
<tx:method name="find*" read-only="true" rollback-for="java.lang.Exception" />
<tx:method name="query*" read-only="true" rollback-for="java.lang.Exception" />
<tx:method name="*" propagation="REQUIRED" read-only="false" isolation="DEFAULT" rollback-for="java.lang.Exception"/>
</tx:attributes>
</tx:advice>
<!--引进redis -->
<!-- <import resource="spring-redis.xml"/> -->
</beans>
以上,在我测试 异常的时候,确实会回滚了,,,终于事务配置好了,,,搞得我好蛋疼啊。。。 还好百度到了,不然坑爹啊。 可是在我 测试 get* 方法只读的 时候 。 发现, 我 在方法里面 进行了 更新,插入,删除,,,这样的事情,居然可以?!!! 说好的只读呢? 是不是我哪里又出问题了? 真难搞! 我明明记得 之前我的其他项目,在开发的时候,只读是 可以其效果的,如果 只读方法里面 更新,增加,删除, 是会出异常,提示这个是只读方法的,可是在 这个项目怎么就不可以??? 百度、。。。
参考地址: http://boy00fly.iteye.com/blog/1142754
http://www.iteye.com/problems/10624
找到了, 总之就是 说, 对于 oracle 来说,在 jdbc 项目里面只读 配置是 不效果的,或者说是在不同的jdbc oracle 驱动里面 是不一致的,不需要纠结这个问题。 也不需要担心,性能的问题。
因为我们只要约定好,在 只读的方法里面操作只读就好了,不要 操作写 数据!!! 约定执行好。 而且 事务配置, propagation 也不要 随便配置,就 按照默认值来吧, 默认就算 是 只读方法里面 写了数据库了,出了异常了,也是可以 数据回滚的,因此不需要担心, 也不需要随便配置的,OK!!
到目前为止 。 事务配置填坑 完成,,,我的项目并没有 使用 声明式事务 配置!
总结:1. 首先jdbc的规范中已经说明了readonly只是将此连接设置为只读模式,作为驱动程序启用数据库优化的提示,并不一定以只读事务执行! 2. 对于oracle的jdbc驱动而言,不同版本的驱动会得出不同的结论! “只读事务”并不是一个强制选项,它只是一个“暗示”,提示数据库驱动程序和数据库系统,这个事务并不包含更改数据的操作,
那么JDBC驱动程序和数据库就有可能根据这种情况对该事务进行一些特定的优化,比方说不安排相应的数据库锁,以减轻事务对数据库的压力,毕竟事务也是要消耗数据库的资源的。
这里我的项目用的是 oracle的,是有事务的支持的表,,,如果 用其他数据,比如 mysql 要注意,使用 该表使用的是 InnoDB 引擎, 如果不是,是没有事务的表,,这样如果表没有事务,在 spirng 配置了事务也是没用的。