执行了session.update(someObject)后,数据始终没有更新,为什么呢?
首先想到的,就是看看到底执行了哪些SQL,于是在mysql jdbc url后加上参数profileSQL=true,然后看日志中输出的SQL。居然没有找到那条期望的update语句!为什么呢?!难道是hibernate以为我没有更改对象,不可能吧?我特意把数据库里面的数据更改掉,确保有更新,但是还是没有看到update语句? 看来是没有flush了。但是为什么不会flush呢?我已经在执行这段代码的方法上加上了@Transactional,那么spring应当会执行commit的,而hibernate应该在commit的时候flush的。回想之前没有看到begin transaction和commit语句,看来spring的事务管理没有生效。打开spring的事务管理的日志,果然看不到开启事务的日志。 为什么spring的日志不起效果呢?难道是因为我没有使用接口的原因?我查了一下配置文件, applicationContext.xml里确实配置了
<tx:annotation-driven transaction-manager="transactionManager" proxy-target-class="true"/>
和
<aop:aspectj-autoproxy proxy-target-class="true"/>
那这样的话aop就应该起效果。仔细查了一下起动时spring输出的日志,发现了spring已经识别了被标的方法了,那为什么没有生效呢? 找了好久没有找到原因,又一想,这种情况下,除非controller里面注入的是另一个对象,而那个对象上的声明式事务没有起效,于是再细读spring输出的日志,果然发现了多处实例这个bean的日志,按理说默认是单例的,只会初始化一次才对。为了验证一下,特意在这个类的构造方法里输出日志,果然发现构造方法被调用了两次。这使我想起了spring文档里面说过,DispatchServlet和ContextLoaderListener会分别初始化自己的WebApplicationContext,ContextLoaderListener初始化的是Root Context,其它DispatchServlet初始化的Context会继承root context,如果一个bean在自己的context里面找不到,就会自己到root context里找。那看来是不小心让同一个类在两个context里面都配置了,而DispatchServlet的context里没有配置声明式事务,我的controller注入的是本地的没有声明式事务的bean,而不是root context里面的那个有事务的bean。于是我查了一下配置,果然在main-servlet.xml和applicationContext.xml里面都发现了
<context:component-scan base-package="com.mycompany.*"/>
怪不得会出现这种情况。那怎么解决呢?应该让main-servlet.xml不要去实例化非@Controller的bean,而applicationContext.xml不要初始化@Controller的bean,就行了。于是给main-servlet.xml和applicationContext.xml分别加入如下配置:
<context:component-scan base-package="com.mycompany.*" use-default-filters="false">
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
和
<context:component-scan base-package="com.mycompany.*" >
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller" />
</context:component-scan>