[转帖]spring + hibernate时,current_session_context_class配置问题_Tomcat, WebLogic及J2EE讨论区_Weblogic技术|Tuxedo技术|中间件技术|Oracle论坛|JAVA论坛|Linux/Unix技术|hadoop论坛_联动北方技术论坛  
网站首页 | 关于我们 | 服务中心 | 经验交流 | 公司荣誉 | 成功案例 | 合作伙伴 | 联系我们 |
联动北方-国内领先的云技术服务提供商
»  游客             当前位置:  论坛首页 »  自由讨论区 »  Tomcat, WebLogic及J2EE讨论区 »
总帖数
1
每页帖数
101/1页1
返回列表
0
发起投票  发起投票 发新帖子
查看: 12427 | 回复: 0   主题: [转帖]spring + hibernate时,current_session_context_class配置问题        下一篇 
    本主题由 zhou 于 2012-12-3 10:56:07 置为精华
鲲鹏展翅
注册用户
等级:少校
经验:1148
发帖:79
精华:9
注册:2012-11-19
状态:离线
发送短消息息给鲲鹏展翅 加好友    发送短消息息给鲲鹏展翅 发消息
发表于: IP:您无权察看 2012-11-22 9:30:45 | [全部帖] [楼主帖] 楼主

当初学使用spring来配置hibernate的事务管理时,当要把current session绑定到线程上,本人很习惯式的把hibernate的current_session_context_class的值设置为thread(hibenate native api 提供三个值:thread,jta, manage).
可是当调用session.save()方法是遇到以下的错:save is not valid without transaction.

 applicationContext.xml
<!-- ORM的sessionFactory -->
<bean id="sessionFactory"
class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<!-- Set the locations of multiple Hibernate XML config files, for example
as classpath resources "classpath:hibernate.cfg.xml,classpath:extension.cfg.xml". -->
<property name="configLocation" value="classpath:hibernate.cfg.xml" />
<property name="configurationClass" value="org.hibernate.cfg.Configuration" />
</bean>
<tx:annotation-driven/>
<!-- 配置事务处理意见 -->
<tx:advice id="baseAdvice" transaction-manager="txManager">
<!-- 事务的属性 -->
<tx:attributes>
<tx:method name="list*" propagation="REQUIRED" read-only="true"/>
<tx:method name="*"/>
</tx:attributes>
</tx:advice>
<!-- 配置事务的aop -->
<aop:config>
<aop:pointcut id="daoPointcut" expression="execution(* com.mcao.dao.*.*(..))" />
<aop:advisor advice-ref="baseAdvice" pointcut-ref="daoPointcut" />
</aop:config>
<!-- 定义事务管理器 -->
<bean id="txManager"
class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<!-- 设置关联的sessionFactory -->
<property name="sessionFactory" ref="sessionFactory" />
</bean>
<bean id="userDao" class="com.mcao.dao.UserDaoImpl">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
<bean id="userService" class="com.mcao.service.UserService">
<property name="userDao" ref="userDao" />
</bean>
hibernate.cfg.xml
<session-factory>
<!-- mysql jdbc 连接配置 -->
<property name="connection.driver_class">com.mysql.jdbc.Driver</property>
<property name="connection.url">jdbc:mysql://localhost:3306/test</property>
<property name="connection.username">root</property>
<property name="connection.password">123456</property>
<!-- JDBC connection pool (use the built-in) -->
<property name="connection.pool_size">1</property>
<!-- SQL dialect -->
<property name="dialect">org.hibernate.dialect.MySQLDialect</property>
<!-- Enable Hibernate's automatic session context management -->
<property name="current_session_context_class">thread</property>
<!-- Disable the second-level cache -->
<property name="cache.provider_class">org.hibernate.cache.NoCacheProvider</property>
<!-- Echo all executed SQL to stdout -->
<property name="show_sql">true</property>
<!-- batch size -->
<property name="hibernate.jdbc.batch_size">40</property>
<!-- Drop and re-create the database schema on startup -->
<property name="hbm2ddl.auto">update</property>
<mapping class="com.mcao.domain.User"/>
</session-factory>
userDaoImpl:
public void save(Serializable domainObject) {
      Session session = sessionFactory.getCurrentSession();
      session.save(domainObject); // 错误发生在此行
}


在网上查了半天,也没查到真正的原因。最后看了源代码才明白。
当current_session_context_class = thread时,具体的类就是org.hibernate.context.ThreadLocalSessionContext.
Session session = sessionFactory.getCurrentSession();只是获取当前的session,并未开启transaction(hibernate native的事务开始,是需要调用session.beginTransaction())当执行session.save(domainObject); 当前的session会被TransactionProtectionWrapper包装(代理模式),因此session.save(domainObject);被执行时,实际是通过TransactionProtectionWrapper.invoke代理执行。
这个代理添加了一些对于session调用的方法的约束,除了以下列举的方法,其他的都需要在事务中执行。由于事务未开启,所有realSession.getTransaction().isActive()为false,当然就会抛出异常了。

 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
      try {
            // If close() is called, guarantee unbind()
            if ( "close".equals( method.getName()) ) {
                  unbind( realSession.getSessionFactory() );
            }
            else if ( "toString".equals( method.getName() )
            || "equals".equals( method.getName() )
            || "hashCode".equals( method.getName() )
            || "getStatistics".equals( method.getName() )
            || "isOpen".equals( method.getName() )
            || "getListeners".equals( method.getName() ) //useful for HSearch in particular
            ) {
                  // allow these to go through the the real session no matter what
            }
            else if ( !realSession.isOpen() ) {
                  // essentially, if the real session is closed allow any
                  // method call to pass through since the real session
                  // will complain by throwing an appropriate exception;
                  // NOTE that allowing close() above has the same basic effect,
                  // but we capture that there simply to doAfterTransactionCompletion the unbind...
            }
            else if ( !realSession.getTransaction().isActive() ) {
                  // limit the methods available if no transaction is active
                  if ( "beginTransaction".equals( method.getName() )
                  || "getTransaction".equals( method.getName() )
                  || "isTransactionInProgress".equals( method.getName() )
                  || "setFlushMode".equals( method.getName() )
                  || "getSessionFactory".equals( method.getName() ) ) {
                        log.trace( "allowing method [" + method.getName() + "] in non-transacted context" );
                  }
                  else if ( "reconnect".equals( method.getName() )
                  || "disconnect".equals( method.getName() ) ) {
                        // allow these (deprecated) methods to pass through
                  }
                  else {
                        throw new HibernateException( method.getName() + " is not valid without active transaction" );
                  }
            }
            log.trace( "allowing proxied method [" + method.getName() + "] to proceed to real session" );
            return method.invoke( realSession, args );
      }
      catch ( InvocationTargetException e ) {
            if ( e.getTargetException() instanceof RuntimeException ) {
                  throw ( RuntimeException ) e.getTargetException();
            }
            else {
                  throw e;
            }
      }
}


解决方法有二:
1, 添加session.beginTransaction(), 开启事务。
2, current_session_context_class对应的class设置为org.springframework.orm.hibernate3.SpringSessionContext
或者不指定(同样是org.springframework.orm.hibernate3.SpringSessionContext),在此类中,获取current session时,就已经把事务开启了。




赞(0)    操作        顶端 
总帖数
1
每页帖数
101/1页1
返回列表
发新帖子
请输入验证码: 点击刷新验证码
您需要登录后才可以回帖 登录 | 注册
技术讨论