Опитах се да съкратя това до това, което смятам за уместно, надявам се да е достатъчно и да не е непосилно. Моля помогнете!
Преобразувам малко уеб приложение wicket+databinder+hibernate да използва wicket+spring+hibernate. Имам DAO сервизен клас с хибернация SessionFactory, инжектирана от Spring. Мога да извършвам операции само за четене, използвайки фабриката за сесии (автоматичното подаване е включено по подразбиране). Това, което искам да направя, е да използвам HibernateTransactionManager и анотацията @Transactional, за да извърша транзакционна операция.
Дефинирам реализация на DAO услуга, която използва инжектирана SessionFactory в метод, означен с @Transactional:
public class DAO implements IDAO {
@SpringBean
private SessionFactory sessionFactory;
public DAO() {
super();
}
@Transactional
public Object execute(SessionUnit sessionUnit) {
Session sess = sessionFactory.getCurrentSession();
Object result;
result = sessionUnit.run(sess);
sess.flush();
return result;
}
public void setSessionFactory(SessionFactory sessionFactory) {
this.sessionFactory = sessionFactory;
}
@Transactional
public boolean isObjectPersistent(Object object) {
return sessionFactory.getCurrentSession().contains(object);
}
}
Когато се опитам да извикам isObjectPersistent()
, получавам изключение за хибернация, защото никой не е извикал session.beginTransaction():
Caused by: org.hibernate.HibernateException: contains is not valid without active transaction
at org.hibernate.context.ThreadLocalSessionContext$TransactionProtectionWrapper.invoke(ThreadLocalSessionContext.java:338)
at $Proxy38.contains(Unknown Source)
at com.gorkwobbler.shadowrun.karma.db.hibernate.DAO.isObjectPersistent(DAO.java:35)
(reflection stuff omitted...)
at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:307)
at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:182)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:149)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:106)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:171)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:106)
(reflection stuff omitted...)
at org.apache.wicket.proxy.LazyInitProxyFactory$JdkHandler.invoke(LazyInitProxyFactory.java:416)
at org.apache.wicket.proxy.$Proxy36.isObjectPersistent(Unknown Source)
Също така забелязвам от пълното проследяване на стека, че OpenSessionInViewFilter се извиква, не съм сигурен дали това е уместно. Уведомете ме, ако имате нужда от останалата част от проследяването на стека.
Ако създам персонализиран подклас WebRequestCycle, който започва транзакция, мога да преодолея това. Струва ми се, че това подкопава целта на @Transactional и моето внедряване също се оказа проблематично.
Ето моето приложениеContext.xml:
<?xml version="1.0" encoding="UTF-8"?>
<!-- Reference: http://wicketinaction.com/2009/06/wicketspringhibernate-configuration/ -->
<beans default-autowire="autodetect"
xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd">
<!-- bean definitions -->
<bean id="wicketApplication" class="com.gorkwobbler.shadowrun.karma.view.wicket.core.WicketApplication" />
<bean id="placeholderConfigurer"
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="ignoreUnresolvablePlaceholders" value="false" />
<property name="systemPropertiesModeName" value="SYSTEM_PROPERTIES_MODE_OVERRIDE" />
<property name="ignoreResourceNotFound" value="false" />
<property name="locations">
<list>
<value>classpath*:/application.properties</value>
</list>
</property>
</bean>
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName">
<value>${jdbc.driver}</value>
</property>
<property name="url">
<value>${jdbc.url}</value>
</property>
<property name="username">
<value>${jdbc.username}</value>
</property>
<property name="password">
<value>${jdbc.password}</value>
</property>
</bean>
<tx:annotation-driven transaction-manager="txManager" />
<!-- setup transaction manager -->
<bean id="txManager"
class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory">
<ref bean="sessionFactory" />
</property>
</bean>
<!-- hibernate session factory -->
<bean id="sessionFactory"
class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
<property name="configLocation">
<value>classpath:/hibernate.cfg.xml</value>
</property>
<property name="dataSource" ref="dataSource" />
<property name="hibernateProperties">
<props>
</props>
</property>
<property name="packagesToScan">
<list>
<value>com.gorkwobbler.shadowrun.karma.domain</value>
<value>com.gorkwobbler.shadowrun.karma.domain.*</value>
</list>
</property>
</bean>
<bean id="dao"
class="com.gorkwobbler.shadowrun.karma.db.hibernate.DAO">
<property name="sessionFactory">
<ref bean="sessionFactory" />
</property>
</bean>
<!-- Don't know what this is for, but it was in the sample config I started from -->
<!-- <context:component-scan base-package="com.gorkwobbler.shadowrun.karma" /> -->
</beans>
Как мога да накарам моя DAO да започне транзакция, да се ангажира в края на този метод или да върне обратно при грешка? Искам да използвам възможно най-минималната/стандартна конфигурация; Предпочитам анотации пред XML, ако имам избор.
Редактиране:
Ревизирах приложения Context по-горе, за да премахна конфигурацията на AOP, която така или иначе не работеше.
С помощта на дебъгера установих, че SessionImpl, съхранен в картата на притежателя на сесията на TransactionInterceptor, не е същата сесия като SessionImpl, който се извлича в DAO метода, когато извикам sessionFactory.getCurrentSession(). Може ли някой да обясни защо е така? какво правя грешно Магията не работи. =(
Редактиране
Също така забелязвам следното съобщение в моята конзола по време на стартиране:
WARN - stractEhcacheRegionFactory - No TransactionManagerLookup found in Hibernate config, XA Caches will be participating in the two-phase commit!