我想将我的axis2项目与spring集成。我设法通过遵循本指南加载spring applicationContext。
简称
这是我的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仍然返回相同的异常。
答案 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,我设法理解发生了什么。
这里最大的错误是我每次调用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();
}
}
使用程序化事务管理,我们必须设置属性
<prop key="hibernate.current_session_context_class">thread</prop>
春天设定:
<tx:annotation-driven>
在这种情况下没用。
如果我们想使用 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);
}
}