Hibernate4,Spring4,Axis2和@Transactional注释未打开事务:如果没有活动事务,则get无效

时间:2014-09-04 14:27:39

标签: spring hibernate dependency-injection axis2 transactional

我想将我的axis2项目与spring集成。我设法通过遵循本指南加载spring applicationContext。

  

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

简称
这是我的axis2 VersionService:

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);
    }
}

问题:当轴调用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>

但是在这种情况下,由于org.hibernate.HibernateException:找不到当前线程的会话,因此Spring无法加载userMyAppDAO

详细信息
我的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仍然返回相同的异常。

2 个答案:

答案 0 :(得分:1)

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

最简单的解决方案是使用getResponse而非@Transactional标记getTransaction方法。 注意:仅当在spring生成的代理上调用getResponse时才会起作用,这在您提供的堆栈跟踪中并不明确。

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

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

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

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

答案 1 :(得分:0)

感谢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)在除spring2服务之外的spring bean中使用@Transactional注释。事实上,他们的加载似乎是以春天无法创建代理的方式发生的。在我的例子中,解决方案是创建一个带弹簧注释的服务并将其方法设置为事务性的。

    @服务 公共类VersionHandler {

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

    }