【问题标题】:Hibernate4, Spring4, Axis2 and @Transactional annotation not opening transaction: get is not valid without an active transactionHibernate4、Spring4、Axis2 和 @Transactional 注释未打开事务:没有活动事务,get 无效
【发布时间】:2014-10-29 08:33:41
【问题描述】:

我想将我的axis2项目与spring集成。我设法按照本指南加载了一个 spring applicationContext。

https://axis.apache.org/axis2/java/core/docs/spring.html

简短
这是我的axis2版本服务:

public class VersionService extends MyappService{

    private static final Logger log = Logger.getLogger(VersionService.class);

    @Autowired
    NewUserMyappDAO newUserMyappDAO;

    public Response getResponse(){
        Response response = new Response();
        UserMyapp ub = getTransaction();
        return response;
    }

    @Transactional
    public UserMyapp getTransaction(){
        return newUserMyappDAO.findById(13);
    }
}

问题:当axis调用getResponse()方法时,dao设法获得注入的sessionFactory(和hibernate会话),但是当@Transactional在该方法之上使用时,之前没有打开任何事务。这就是为什么我得到:

 Caused by: org.hibernate.HibernateException: get is not valid without active transaction
at org.hibernate.context.internal.ThreadLocalSessionContext$TransactionProtectionWrapper.invoke(ThreadLocalSessionContext.java:348)
at $Proxy45.get(Unknown Source)
at com.myapp.framework.model.dao.NewMyappDAO.findById(NewMyappDAO.java:35)
at com.myapp.ws.version.VersionService.getTransaction(VersionService.java:127)
at com.myapp.ws.version.VersionService.getResponse(VersionService.java:119)

我想要的是有一个 getTransaction() 方法,它可以自动启动事务 (Hibernate session.beginTransaction()) 并在其中出现问题时回滚。

我也试过删除

<prop key="hibernate.current_session_context_class">org.hibernate.context.internal.ThreadLocalSessionContext</prop>

但在这种情况下,spring 无法加载 userMyAppDAO,因为 org.hibernate.HibernateException: No Session found for current thread

详情
我的 applicationContext.xml 看起来像这样:

<?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:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache.xsd">


    <context:annotation-config />

    <context:component-scan base-package="com.myapp.framework.model.dao"></context:component-scan>

    <!-- Axis2 Web Service, but to Spring, its just another bean that has dependencies -->
    <bean id="versionService" class="com.myapp.ws.version.VersionService"/>

    <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
        <property name="driverClassName" value="com.mysql.jdbc.Driver" />
        <property name="url" value="jdbc:mysql://db.myapp.com:3307/MyappAPI" />
        <property name="username" value="myapp" />
        <property name="password" value="myappculomyapp" />
    </bean>


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

        <property name="packagesToScan">
            <list>
                <value>com.myapp.framework.model.dao</value>
            </list>
        </property>

        <property name="hibernateProperties">
            <props>
                <prop key="hibernate.dialect">org.hibernate.dialect.MySQL5InnoDBDialect</prop>

                <prop key="hibernate.connection.CharSet">utf8</prop>
                <prop key="hibernate.connection.characterEncoding">utf8</prop>
                <prop key="hibernate.connection.useUnicode">true</prop>
                <prop key="hibernate.show_sql">false</prop>
                <prop key="hibernate.format_sql">true</prop>
                <prop key="hibernate.use_sql_comments">true</prop>
                <prop key="hibernate.globally_quoted_identifiers">true</prop>
                <prop key="hibernate.connection.autocommit">false</prop>

                <prop key="hibernate.current_session_context_class">org.hibernate.context.internal.ThreadLocalSessionContext</prop>
                <prop key="hibernate.transaction.factory_class">org.hibernate.engine.transaction.internal.jdbc.JdbcTransactionFactory</prop>

                <prop key="hibernate.cache.use_second_level_cache">true</prop>
                <prop key="hibernate.cache.region.factory_class">org.hibernate.cache.ehcache.EhCacheRegionFactory</prop>

            </props>
        </property>
    </bean>

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

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

</beans>

这是 DAO 及其超类:

@Repository
public class NewUserMyappDAO extends NewMyappDAO<UserMyapp, Integer>{

    @Autowired
    public NewUserMyappDAO(SessionFactory sessionFactory){
        super(UserMyapp.class, sessionFactory);
    }
}

@Repository
public abstract class NewMyAppDAO<E, ID extends Serializable>
        implements IMyAppDAO<E, ID> {

    private final Class<E> entityClass;
    protected Session session;

    public NewMyAppDAO(Class<E> entityClass, SessionFactory sessionFactory) {

        this.entityClass = entityClass;
        this.session = sessionFactory.getCurrentSession();
    }

    public Class<E> getEntityClass() {
        return entityClass;
    }

    @SuppressWarnings({ "unchecked" })
    public E findById(ID id) {
        Object obj = null;
        try {
            obj = session.get(getEntityClass(), id);
        } catch(ObjectNotFoundException e){
            return null;
        }
        return (E) obj;
    }

编辑

vp8106 留下的答案似乎是正确的,但我试图后退一步,试图以编程方式管理事务。我所做的是在 getResponse() 方法中显式使用 beginTransaction()、commitTransaction()、rollbackTransaction() 和 close()。即使 sessionFactory 对象是一个单例并且它是用

初始化的
<prop key="hibernate.current_session_context_class">thread</prop>

没有事务启动,我的 dao 仍然返回相同的异常。

【问题讨论】:

  • 这个org.hibernate.HibernateException: No Session found for current thread 异常告诉您您没有正确配置事务并且不能通过简单地使用另一个当前会话上下文来解决。您的 VersionService 不是 Spring 托管的 bean,因此此类事务将无法处理。确保它是一个完全 sprnig 托管的 bean(包括代理创建)。
  • 感谢您的回复。我不明白如何检查它。 Axis 让人难以理解。我的意思是,userMyAppDAO 被正确注入,所以我认为我正在使用 Spring 管理的 bean。
  • 我找到了这个链接stackoverflow.com/questions/8846586/…。它建议将注释驱动的设置移动到 servlet 上下文中,而不是将它们留在应用程序中。看来我应该创建一个axis2-servlet.xml并以某种方式将它传递给axis2 servlet。

标签: spring hibernate dependency-injection axis2 transactional


【解决方案1】:

很可能是Axis调用了没有@Transactional标记的spring bean的getResponse方法。事实是,spring 为带有@Transactional 注释的方法的bean 创建了一个动态代理。此代理包装对事务方法的调用,该方法在目标方法执行之前启动事务并在目标方法执行之后提交。但在您的情况下,方法 getResponse 调用 this bean 的方法 getTransaction,而不是代理。因此事务感知代码不会执行,也没有事务启动。

最简单的解决方案是将getResponse 方法标记为@Transactional 而不是getTransaction注意:只有在 spring 生成的代理上调用 getResponse 时它才会起作用,这在您提供的堆栈跟踪中并不清楚。

编辑
在 Spring 环境中你应该使用

<prop key="hibernate.current_session_context_class">org.springframework.orm.hibernate4.SpringSessionContext</prop>

它将休眠会话生命周期绑定到 Spring 的 HibernateTransactionManager。

为了启动、提交或回滚事务 HibernateTransactionManager 的.getTransaction(...)commit(...)rollback(...) 应该使用而不是hibernate Session 的方法。要以编程方式管理事务,最好使用TransactionTemplate,它可以帮助您避免编写样板代码来开始、提交或回滚事务。

【讨论】:

  • 感谢您的回复。我刚刚编辑了答案,以了解它是春季还是休眠。
  • 它仍然抛出 org.hibernate.HibernateException: No Session found for current thread at org.springframework.orm.hibernate4.SpringSessionContext.currentSession(SpringSessionContext.java:106) 将应用程序加载到 tomcat 中。看一下代码,我看到在 SpringSessionContext 类中的行 TransactionSynchronizationManager.getResource(this.sessionFactory);返回一个空值。这可能与 bean 作为根上下文而不是 servlet 上下文加载这一事实有关。
  • 结果表明我对所有休眠查询都使用了相同的 Session 对象(没有每次都从 sessionFactory 获取)。 thread 似乎可以使用 thread 和编程事务管理。
【解决方案2】:

感谢 vp816 和 M. Deinum,我设法了解了正在发生的事情。

  1. 这里的大错误是我每次调用 NewMyAppDAO 的 findById 方法时都使用相同的私有 Session 字段对象。使用受保护的 getSession() 方法可以正确使用会话。

    @Repository 公共抽象类 NewMyAppDAO 实现 IMyAppDAO {

    private final Class<E> entityClass;
    protected SessionFactory sessionFactory;
    
    public NewMyAppDAO(Class<E> entityClass, SessionFactory sessionFactory) {
    
        this.entityClass = entityClass;
        this.sessionFactory = sessionFactory;
    }
    
    protected Session getSession(){
        return  this.sessionFactory.getCurrentSession();
    }
    

    }

  2. 使用程序化事务管理我们必须设置属性

    <prop key="hibernate.current_session_context_class">thread</prop>
    

    弹簧设置:

    <tx:annotation-driven>
    

    在这种情况下是没用的。

  3. 如果我们想使用 Spring Annotation Driven transactions,我们必须:
    a) 从 sessionFactory bean 配置中删除 hibernate.current_session_context_class 属性
    b) 添加弹簧设置 <bean id="transactionManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager"> <property name="sessionFactory" ref="sessionFactory" /> </bean> <tx:annotation-driven transaction-manager="transactionManager" /> c) 在 spring bean 中使用 @Transactional 注释,除了axis2服务。事实上,它们的加载似乎是以 spring 无法创建代理的方式发生的。就我而言,解决方案是创建一个带有弹簧注释的服务并将其方法设置为事务性的。

    @服务 公共类 VersionHandler {

    @Autowired
    NewUserMyappDAO newUserMyappDAO;
    
    @Transactional
    public UserMyapp getUserMyapp(int transactionId){
        return newUserMyappDAO.findById(transactionId);
    }
    

    }

【讨论】:

    猜你喜欢
    • 2014-04-02
    • 2014-04-05
    • 2014-02-14
    • 2021-09-25
    • 2015-03-05
    • 1970-01-01
    • 2014-08-13
    • 2012-08-13
    • 1970-01-01
    相关资源
    最近更新 更多