spring + hibernate时,current_session_context_class配置问题

当初学使用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时,就已经把事务开启了。

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值