I want to integrate my axis2 project with spring. I managed to load a spring applicationContext by following this guide.
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.
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.