SpringMVC+Spring+Hibernate集成问题

    |     2017年2月17日   |   JavaWeb   |     0 条评论   |    1962

在项目框架搭建时使用SpringMVC+Spring+Hibernate集成遇到如下问题:

org.springframework.dao.InvalidDataAccessApiUsageException: Write operations are not allowed in read-only mode (FlushMode.MANUAL): Turn your Session into FlushMode.COMMIT/AUTO or remove 'readOnly' marker from transaction definition.
	at org.springframework.orm.hibernate4.HibernateTemplate.checkWriteOperationAllowed(HibernateTemplate.java:1128)
	at org.springframework.orm.hibernate4.HibernateTemplate$12.doInHibernate(HibernateTemplate.java:621)
	at org.springframework.orm.hibernate4.HibernateTemplate$12.doInHibernate(HibernateTemplate.java:618)

分析错误:

Write operations are not allowed in read-only mode (FlushMode.MANUAL): Turn your Session into FlushMode.COMMIT/AUTO or remove 'readOnly' marker from transaction definition.
写操作在只读模式下不被允许((FlushMode.MANUAL): 把你的Session改成FlushMode.COMMIT/AUTO或者清除事务定义中的readOnly标记。

错误原因:

OpenSessionInViewFilter在getSession的时候,会把获取回来的session的flush mode 设为FlushMode.NEVER。然后把该sessionFactory绑定到TransactionSynchronizationManager,使request的整个过程都使用同一个session,在请求过后再去除该sessionFactory的绑定,最后closeSessionIfNecessary根据该session是否已和transaction绑定来决定是否关闭session。在这个过程中,若HibernateTemplate 发现自当前session有不是readOnly的transaction,就会获取到FlushMode.AUTO Session,使方法拥有写权限。也即是,如果有不是readOnly的transaction就可以由Flush.NEVER转为Flush.AUTO,拥有insert,update,delete操作权限,如果没有transaction,并且没有另外人为地设flush model的话,则doFilter的整个过程都是Flush.NEVER。
所以受transaction(声明式的事务)保护的方法有写权限,没受保护的则没有。

解决办法:

方法一:

spring-servlet.xml:

 <context:component-scan base-package="com.ittx.spring002.controller" >
		<!-- 只扫描base-package指定包下@Controller注解 -->
		<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller" />
 </context:component-scan>

 applicatioContext.xml:

<context:component-scan base-package="com.ittx.spring002">
		<!-- 除了base-package包下@Controller注解全都扫描 -->
		<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller" />
</context:component-scan>

方法二 :

 Dao层HibernateTemplate操作数据时,使用execute方法回调方式实现,如下:

	原方式:getHibernateTemplate().save(user); 
        
        更改后方式:getHibernateTemplate().execute(new HibernateCallback<User>() {
			@Override
			public User doInHibernate(Session session) throws HibernateException {
				session.save(user);
				return null;
			}
	});

方法三:

合并spring-servlet.xml和applicationContext.xml

错误问题引申知识点一:

事务实现方式:

1.用代码实现事务管理

 session.beginTrasaction();
   session.save();
   session.getTrasaction().commit(); 

2.用注解来实现事务管理

 <tx:annotation-driven transaction-manager="txManager" />

	<!-- 声明式容器事务管理 -->
	<tx:advice id="txAdvice" transaction-manager="txManager">
		<tx:attributes>
			<tx:method name="save*" propagation="REQUIRED" />
			<tx:method name="add*" propagation="REQUIRED" />
			hibernate4必须配置为开启事务 否则 getCurrentSession()获取不到
			<tx:method name="get*" propagation="REQUIRED" read-only="true" />
		</tx:attributes>
	</tx:advice>
	<aop:config expose-proxy="true">
		只对业务逻辑层实施事务
		<aop:pointcut id="txPointcut"
			expression="execution(* com.ittx.spring002.server..*.*(..))" />
		<aop:advisor advice-ref="txAdvice" pointcut-ref="txPointcut" />
	</aop:config>

3.用注解来实现事务管理

<bean id="txManager"
		class="org.springframework.orm.hibernate4.HibernateTransactionManager">
		<property name="sessionFactory" ref="sessionFactory" />
	</bean>

	<!-- 用注解来实现事务管理 -->
	<tx:annotation-driven transaction-manager="txManager" />

错误问题引申知识点二:

使用说明

<context:component-scan base-package="com.ittx.spring002" />

在xml配置了这个标签后,spring可以自动去扫描base-pack下面或者子包下面的Java文件,如果扫描到有@Component @Controller@Service等这些注解的类,则把这些类注册为bean

注意:如果配置了那么标签就可以不用再xml中配置了,因为前者包含了后者。另外还提供了两个子标签

1. <context:include-filter>
2. <context:exclude-filter>

在说明这两个子标签前,先说一下有一个use-default-filters属性,改属性默认为true,这就意味着会扫描指定包及子包下的全部的标有@Component的类,并注册成bean.也就是@Component的子注解@Service,@Reposity等。如果只想扫描当前包下类use-default-filters属性改为false

转载请注明来源:SpringMVC+Spring+Hibernate集成问题
回复 取消