0

I want to integrate my axis2 project with spring. I managed to load a spring applicationContext by following this guide.

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

IN SHORT
Here's is my 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);
    }
}

The problem: when axis calls getResponse() method the dao manages to obtain the injected sessionFactory (and the hibernate session), but when the @Transactional is used on top of the method no transaction is opened before. That's why I get get:

 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)

What I want is having a getTransaction() method that automatically starts the transaction (Hibernate session.beginTransaction()) and rollbacks if something fails in it.

I also tried to remove

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

but in this case spring fails to load the userMyAppDAO because of org.hibernate.HibernateException: No Session found for current thread

IN DETAILS
My applicationContext.xml looks like this:

<?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>

And here's the DAO and its superclass:

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

EDIT

The answer left by vp8106 seems to be going in the right way, but i tried to move a step backwords trying to manage transaction programmatically. What i did is to use the beginTransaction(), commitTransaction(), rollbackTransaction() and close() explicitly in getResponse() method. Even if the sessionFactory object is a singleton and it is initialized with

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

no transaction is started and my dao still returns the same exception.

ecostanzi
  • 240
  • 1
  • 8
  • 19
  • 1
    This `org.hibernate.HibernateException: No Session found for current thread` exception tells you that you haven't configured transactions correctly and that isn't fixed by simply using another current session context. Your `VersionService` isn't a spring managed bean and as such transactions won't work on that. Make sure that it is a fully sprnig managed bean (including proxy creation). – M. Deinum Sep 04 '14 at 15:04
  • Thanks for your reply. I don't understand how to check it. Axis is making it hard to understand. I mean, the userMyAppDAO is injected correctly so I thought I was working with a spring managed beans. – ecostanzi Sep 04 '14 at 15:23
  • I found this link http://stackoverflow.com/questions/8846586/no-session-found-for-current-thread-spring-3-1-x-and-hibernate-4. It suggests to move the annotation driven settings to the servlet context instead of leaving them on the application one. It seems I should create an axis2-servlet.xml and pass it to the axis2 servlet in some way. – ecostanzi Sep 05 '14 at 09:05

2 Answers2

1

It is likely that Axis calls getResponse method of spring bean that is not marked with @Transactional. The fact is that spring creates a dynamic proxies for beans which have method annotated with @Transactional. This proxy wraps calls to transacted method starting transaction before and commiting after target method execution. But in your case method getResponse calls method getTransaction of this bean, not proxy. Thus transaction-aware code does not execute, and no transaction started.

The simpliest possible solution is to mark getResponse method with @Transactional instead of getTransaction. Note: it will work only if getResponse is called on spring-generated proxy, which is not clear from stacktrace you provided.

EDIT
In Spring environment you should use

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

It binds hibernate session lifecycle to the Spring's HibernateTransactionManager.

In order to start, commit or rollback transactions HibernateTransactionManager's .getTransaction(...), commit(...) or rollback(...) should be used instead of hibernate Session's methods. To manage transactions programmatically it would be better to use TransactionTemplate which helps you to avoid writing boilerplate code to begin, commit, or rollback transactions.

vp8106
  • 201
  • 2
  • 5
  • Thanks for your reply. I just edited the answer in order to understand if it's spring or hibernate. – ecostanzi Sep 12 '14 at 16:05
  • It is still throwing org.hibernate.HibernateException: No Session found for current thread at org.springframework.orm.hibernate4.SpringSessionContext.currentSession(SpringSessionContext.java:106) while loading the app into tomcat. Giving a look to the code I see that in SpringSessionContext class the row TransactionSynchronizationManager.getResource(this.sessionFactory); returns a null value. It probably has to do to the fact that the beans are loading as root context, not servlet context. – ecostanzi Sep 15 '14 at 08:34
  • It came out i was using the same Session object for all my hibernate queries (without getting it every time from sessionFactory). thread seems to work using thread and programmatic transaction management. – ecostanzi Sep 15 '14 at 09:30
0

Thanks to vp816 and M. Deinum I managed to understand what's happening.

  1. The big mistake here was that I was using the same private Session field object every time I was calling findById method of NewMyAppDAO. Using the protected getSession() method allows to use session correctly.

    @Repository public abstract class NewMyAppDAO implements 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. Using programmatic transaction management we have to set the property

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

    Spring setting:

    <tx:annotation-driven>
    

    in this case is useless.

  3. If we want to use Spring Annotation Driven transactions we have to:
    a) delete hibernate.current_session_context_class property from sessionFactory bean configuration
    b) adding spring settings <bean id="transactionManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager"> <property name="sessionFactory" ref="sessionFactory" /> </bean> <tx:annotation-driven transaction-manager="transactionManager" /> c) Using @Transactional annotation in spring beans except from axis2 services. In fact their loading seems to happen in such a way that spring doesn't manage to create the proxy. In my case the solution has been to create a spring-annotated service and set its method as transactional.

    @Service public class VersionHandler {

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

    }

ecostanzi
  • 240
  • 1
  • 8
  • 19