I'm trying to get my spring DAOs to work but I only get this exception
PropertyAccessException 1: org.springframework.beans.MethodInvocationException: Property 'sessionFactory' threw exception; nested exception is org.hibernate.HibernateException: No Hibernate Session bound to thread, and configuration does not allow creation of non-transactional one here
my applicationContext.xml looks like this
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd">
<bean class="org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor"/>
<bean id="ds1Datasource" class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiName" value="java:comp/env/jdbc/ds1"/>
</bean>
<bean id="mySessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="dataSource" ref="db2Datasource"/>
<property name="mappingResources">
<list>
<value>hibernate/P1.hbm.xml</value>
</list>
</property>
<property name="hibernateProperties">
<value>
hibernate.dialect=org.hibernate.dialect.DB2400Dialect
hibernate.connection.autocommit=false
hibernate.connection.charset=UTF-8
hibernate.show_sql=true
</value>
</property>
</bean>
<tx:annotation-driven transaction-manager="myTxManager"/>
<bean id="myTxManager" class="org.springframework.transaction.jta.JtaTransactionManager" />
<bean id="p1" class="dao.DomainP1Impl">
<property name="sessionFactory" ref="mySessionFactory" />
</bean>
And my DAO like this
#Transactional
public class DomainP1Impl implements DomainP1 {...}
What I'm doing wrong? Forgotten to mention: I'm using a JBoss AS 4.2.3
Add this to the session factory bean (mySessionFactory):
<property name="exposeTransactionAwareSessionFactory"><value>false</value></property>
And then see explanation here.
Stupid question maybe, but how are you getting the session in your dao?
You should be using "getCurrentSession()" to get the session, or better still extend spring's HibernateDAOSupport for your dto's.
Your config is the same as one we used on a recent project and had none of the issues you're having. Did not have to do any more configuration than you've shown in your original question. We did however always extend HibernateDAOSupport in our DAO's. And we were also on Jboss.
I'm not sure you should be setting that property to false (exposeTransactionAwareSessionFactory) - it should work without it.
I've found the javadocs for LocalSessionFactoryBean to be particularly helpful.
No query gets executed because your datasources are all screwed up ds1Datasource and db2Datasource
<bean id="ds1Datasource" class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiName" value="java:comp/env/jdbc/ds1"/>
</bean>
<bean id="mySessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="dataSource" ref="db2Datasource"/>
Related
I am using simple "helloWorld"ish application to learn Spring,Hibernate and transaction management using AOP. But is not working as expected. I am getting exception in transaction management. Details as follows :-
Spring version 4.3.8
Hibernate version 5.2.10
HSQL DB version 2.3.4
Spring.xml look as follows
<?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.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd">
<!-- Enable Annotation based Declarative Transaction Management -->
<tx:annotation-driven proxy-target-class="true" mode="aspectj"
transaction-manager="transactionManager" />
<!-- THIS IS COMMENTED. Without commenting same result. I TRIED USING HibernateTransactionManager. still got same result. -->
<!--
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean> -->
<bean id="dataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="org.hsqldb.jdbcDriver"/>
<property name="url" value="jdbc:hsqldb:file:C:/ProjectRelated/softwares/hsqldb-2.3.4/hsqldb/data/FirstFile"/>
<property name="username" value="sa"/>
<property name="password" value="sys"/>
</bean>
<bean id="mySessionFactory" class="org.springframework.orm.hibernate5.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="packagesToScan" >
<array>
<value>com.kaushik.winnersoft.data</value>
</array>
</property>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.HSQLDialect</prop>
</props>
</property>
</bean>
<bean id="hibernateTemplate" class="org.springframework.orm.hibernate5.HibernateTemplate">
<property name="sessionFactory" ref="mySessionFactory"/>
</bean>
<bean id="customerDAO" class="com.kaushik.winnersoft.dao.CustomerDAOImpl">
<property name="hibernateTemplate" ref="hibernateTemplate"></property>
</bean>
<bean id="customerManager" class="com.kaushik.winnersoft.CustomerManagerImpl">
<property name="customerDAO" ref="customerDAO"></property>
</bean>
DAOImpl class is
public class CustomerDAOImpl implements CustomerDAO {
private HibernateTemplate hibernateTemplate;
public HibernateTemplate getHibernateTemplate() {
return hibernateTemplate;
}
public void setHibernateTemplate(HibernateTemplate hibernateTemplate) {
this.hibernateTemplate = hibernateTemplate;
}
#Override
#Transactional
public void create(Customer customer) {
System.out.println("in dao creating");
hibernateTemplate.save(customer);
System.out.println("in dao creating done");
}
I get output as follows
Doing
in dao creating
Exception in thread "main" org.springframework.dao.InvalidDataAccessApiUsageException: Write operations are not allowed in read-only mode (FlushMode.MANUAL): Turn your Session into FlushMode.COMMIT/AUTO or remove 'readOnly' marker from transaction definition.
at org.springframework.orm.hibernate5.HibernateTemplate.checkWriteOperationAllowed(HibernateTemplate.java:1165)
at org.springframework.orm.hibernate5.HibernateTemplate$12.doInHibernate(HibernateTemplate.java:643)
at org.springframework.orm.hibernate5.HibernateTemplate$12.doInHibernate(HibernateTemplate.java:640)
at org.springframework.orm.hibernate5.HibernateTemplate.doExecute(HibernateTemplate.java:359)
at org.springframework.orm.hibernate5.HibernateTemplate.executeWithNativeSession(HibernateTemplate.java:326)
at org.springframework.orm.hibernate5.HibernateTemplate.save(HibernateTemplate.java:640)
at com.kaushik.winnersoft.dao.CustomerDAOImpl.create(CustomerDAOImpl.java:27)
at com.kaushik.winnersoft.CustomerManagerImpl.createCustomer(CustomerManagerImpl.java:20)
at com.kaushik.winnersoft.SpringTest.main(SpringTest.java:14)
Answer
Based on the coments given below by M. Denium; I did following changes and it worked.
1) Used HibernateTransactionManager instead of DataSourceTransactionManager
<bean id="transactionManager"
class="org.springframework.orm.hibernate5.HibernateTransactionManager">
<property name="sessionFactory" ref="mySessionFactory" />
</bean>
2) In removed mode="aspectj"
so it looks like
<tx:annotation-driven proxy-target-class="true" transaction-manager="transactionManager" />
I see to problems with your configuration
You are not using the proper PlatformTransactionManager
Unless you are using load- or compile time weaving I doubt the mode="aspectj" is really correct.
First you need to use the PlatformTransactionManager that supports your persistence technology, as you are using Hibernate 5, you need to use the org.springframework.orm.hibernate5.HibernateTransactionManager instead of the DataSourceTransactionManager. (the latter is for application that only do plain JDBC transactions).
From the <tx:annotation-driven /> remove the mode="aspectj" as I suspect you are actually not using full blown AspectJ but are relying on plain Spring to do this for you.
Pro Tip: Instead of using HibernateTemplate, which isn't recommended anymore since Hibernate 3.0.1, just use plain SessionFactory with getCurrentSession instead. This will allow you to write plain Hibernate daos. As suggested in the reference guide.
I've got an app running on Tomcat 7, using Spring, Mybatis, and .. Mybatis-spring.
Here's the setup for the DB and transactions in servlet-context.xml:
<jee:jndi-lookup id="dataSource" jndi-name="jdbc/MyDS" />
<!-- enable the configuration of transactional behavior based on annotations -->
<tx:annotation-driven transaction-manager="transactionManager"/>
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="mapperLocations" value="classpath*:maps/*.xml" />
<property name="transactionFactory">
<bean class="org.apache.ibatis.transaction.managed.ManagedTransactionFactory" />
</property>
</bean>
<mybatis:scan base-package="com.domain.dao.mappers" />
<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
<constructor-arg index="0" ref="sqlSessionFactory" />
</bean>
The error I'm getting when I run a method that uses the SqlSession is the following:
org.springframework.dao.TransientDataAccessResourceException: SqlSessionFactory must be using a SpringManagedTransactionFactory in order to use Spring transaction synchronization
at org.mybatis.spring.SqlSessionUtils.getSqlSession(SqlSessionUtils.java:136)
What am I doing wrong?
My goal, really, is just to use transactions with this setup. I don't think I necessarilly need JTA. But if that's easy enough to setup on Tomcat, I'm willing to take a swing at it.
And I solved the problem myself. Really simple solution. In case anyone runs into the same issue, all I needed to do is remove the following from the sqlSessionFactory bean:
<property name="transactionFactory">
<bean class="org.apache.ibatis.transaction.managed.ManagedTransactionFactory" />
</property>
I must have somehow entered it thinking I needed it, but apparently it's only needed if you're not using CMT (Container Managed Transactions).
You can solve this by changing the transaction factory to
<property name="transactionFactory">
<bean class="org.mybatis.spring.transaction.SpringManagedTransactionFactory" />
</property>
I use Hibernate (through JPA) configured by Spring and when I launch my application (war deployed on Tomcat 6), I get this error:
org.hibernate.HibernateException: Connection cannot be null when 'hibernate.dialect' not set
It seems strange because I've set the hibernate dialect as follows:
p:databasePlatform="org.hibernate.dialect.MySQL5Dialect
For more information, here my full 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:p="http://www.springframework.org/schema/p"
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.1.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.1.xsd">
<bean id="dataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource"
p:driverClassName="com.mysql.jdbc.Driver"
p:url="jdbc:mysql://localhost/room_management" p:username="root" p:password=""/>
<bean id="entityManagerFactory"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"
p:dataSource-ref="dataSource" p:persistenceUnitName="RoomManagement">
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"
p:database="MYSQL"
p:databasePlatform="org.hibernate.dialect.MySQL5Dialect"
p:showSql="true"/>
</property>
</bean>
<bean id="transactionManager"
class="org.springframework.orm.jpa.JpaTransactionManager"
p:entityManagerFactory-ref="entityManagerFactory"/>
<context:annotation-config/>
<context:component-scan base-package="com.parisdescartes.roommanagement.*"/>
<tx:annotation-driven/>
</beans>
So, I decided to precise Hibernate Dialect within META-INF/persistence.xml file, and this time that works. Here how I precised it:
<properties>
<property name="hibernate.dialect"
value="org.hibernate.dialect.MySQL5Dialect"/>
</properties>
Do you have an idea why Hibernate Dialect is not set using Spring configuration ?
Not sure why it won't work with your configuration. Maybe something goes wrong with using the p: annotation. I'll post my code (which works for my config) to try if it will fix your code! :)
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<property name="showSql" value="false"/>
<property name="generateDdl" value="true"/>
<property name="databasePlatform" value="org.hibernate.dialect.MySQL5Dialect"/>
</bean>
</property>
</bean>
Good luck!
A bit late, but I think this could add value. Not necessarily you need to add databasePlatform property if you specify database property, the adapter itself will figure out the dialect.
<property name="database" value="MYSQL" />
Relevant code from org.springframework.orm.jpa.vendor.HibernateJPAVendorAdapter
protected Class determineDatabaseDialectClass(Database database) {
switch (database) {
case DB2: return DB2Dialect.class;
case DERBY: return DerbyDialect.class;
case H2: return H2Dialect.class;
case HSQL: return HSQLDialect.class;
case INFORMIX: return InformixDialect.class;
case MYSQL: return MySQLDialect.class;
case ORACLE: return Oracle9iDialect.class;
case POSTGRESQL: return PostgreSQLDialect.class;
case SQL_SERVER: return SQLServerDialect.class;
case SYBASE: return SybaseDialect.class;
default: return null;
}
}
Dialect could be auto-detected from DataSource driver. So nether hibernate.dialect no database were needed.
If exception 'hibernate.dialect' not set happened, it usually means, than something wrong with DB connection:
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"
p:dataSource-ref="dataSource"
p:packagesToScan="ru.javawebinar.**.model">
<property name="jpaPropertyMap">
<map>
<entry key="#{T(org.hibernate.cfg.AvailableSettings).FORMAT_SQL}" value="${hibernate.format_sql}"/>
<entry key="#{T(org.hibernate.cfg.AvailableSettings).USE_SQL_COMMENTS}" value="${hibernate.use_sql_comments}"/>
</map>
</property>
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"
p:showSql="${jpa.showSql}">
</bean>
</property>
</bean>
I have referred to the question asked at Hibernate using multiple databases. My problem is similar but I am facing a different problem.I created two xml file each has a separate datasource and session factory.
In my web.xml I have
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>*The xml files* </param-value>
Once I run the project,the various loadings and the bindings are done from both the xml files.The various annotations and databases/tables are properly identified.But just after this is done before the control even goes outside.I get the following error.
main ERROR [org.springframework.web.context.ContextLoader] - Context initialization failed
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'daoEager' defined in URL [jar:file:/C:/Users/.../.metadata/.plugins/org.eclipse.wst.server.core/tmp1/wtpwebapps/infobutton-service/WEB-INF/lib/core-data-1.0.0-SNAPSHOT.jar!/.../DaoHibernateEagerImpl.class]: Unsatisfied dependency expressed through constructor argument with index 0 of type [org.hibernate.SessionFactory]: : No unique bean of type [org.hibernate.SessionFactory] is defined: expected single matching bean but found 2: [sessionFactory, profilesessionFactory]; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No unique bean of type [org.hibernate.SessionFactory] is defined: expected single matching bean but found 2: [sessionFactory, profilesessionFactory]
The class DaoHibernateEagerImpl is
#Implementation
#Repository("daoEager")
public class DaoHibernateEagerImpl extends DaoHibernateImpl
{
// ========================= CONSTANTS =================================
/**
* A logger that helps identify this class' printouts.
*/
private static final Logger log = getLogger(DaoHibernateEagerImpl.class);
// ========================= CONSTRUCTORS ==============================
/**
* Required for a Spring DAO bean.
*
* #param sessionFactory
* Hibernate session factory
*/
#Autowired
public DaoHibernateEagerImpl(final SessionFactory sessionFactory)
{
super(sessionFactory);
}
// ========================= DEPENDENCIES ==============================
// ========================= IMPLEMENTATION: Dao =======================
// ========================= IMPLEMENTATION: SearchEngine ==============
}
One of the xml files is
<?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:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:jee="http://www.springframework.org/schema/jee"
xmlns:jaxws="http://cxf.apache.org/jaxws"
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/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
http://www.springframework.org/schema/jee
http://www.springframework.org/schema/jee/spring-jee-3.0.xsd
http://cxf.apache.org/jaxws http://cxf.apache.org/schemas/jaxws.xsd">
<context:annotation-config />
<!--
Data source: reads a properties file and injects them into a DBCP DS
Second datasource for Resource Profiles
-->
<bean id="profiledataSource"
class=".....ConfigurableBasicDataSource">
<constructor-arg index="0">
<bean class="org.apache.commons.dbcp.BasicDataSource" />
</constructor-arg>
<property name="properties">
<bean
class="org.springframework.beans.factory.config.PropertiesFactoryBean">
<property name="locations">
<list>
<value>WEB-INF/datasource-local.properties
</value>
</list>
</property>
</bean>
</property>
<!-- FUR-946: idle connections break. Adding connection testing. -->
<property name="testOnBorrow" value="true" />
<property name="testWhileIdle" value="true" />
</bean>
<!-- Session factory -->
<!-- Session Factory for the second datasource-->
<bean id="profilesessionFactory"
class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
<property name="dataSource" ref="profiledataSource" />
<!--
Hibernate configuration properties (read from a properties file)
-->
<property name="hibernateProperties">
<bean
class="org.springframework.beans.factory.config.PropertiesFactoryBean">
<property name="locations">
<list>
<value>WEB-INF/hibernate.properties
</value>
<value>WEB-INF/datasource-local.properties
</value>
</list>
</property>
</bean>
</property>
<!-- Using improved naming strategy -->
<property name="namingStrategy">
<bean class="org.hibernate.cfg.DefaultNamingStrategy" />
</property>
<!-- Mapping annotated classes using search patterns -->
<property name="annotatedClasses">
<list>
<value><![CDATA[....profiledb.domain.Profiles]]></value>
</list>
</property>
</bean>
<!-- Hibernate data access template -->
<bean id="profilehibernateTemplate"
class="org.springframework.orm.hibernate3.HibernateTemplate">
<property name="sessionFactory" ref="profilesessionFactory" />
</bean>
<tx:annotation-driven />
<!-- a PlatformTransactionManager is still required -->
<bean id="profiletransactionManager"
class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="profilesessionFactory" />
</bean>
<bean id="profilesdbDao" class="....profiledb.service.ProfilesDaoImpl" >
<property name="sessionFactory" ref="profilesessionFactory"></property>
<context:component-scan base-package="....core.data" />
The other xml file is similar but has a different datasource and the session factory is
<bean id="sessionFactory"
class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
.....
The error message is pretty clear:
expected single matching bean but found 2: [sessionFactory, profilesessionFactory]
Which is understandable:
<bean id="profilesessionFactory"
<!-- ... -->
<bean id="sessionFactory"
The fix should be simple:
#Autowired
public DaoHibernateEagerImpl(
#Qualifier("profilesessionFactory") final SessionFactory sessionFactory)
If you cannot modify DaoHibernateEagerImpl class, you can always fall-back to XML-based configuration. First disable CLASSPATH scanning of DaoHibernateEagerImpl so that #Autowired is not picked up. Then simply write:
<bean class="DaoHibernateEagerImpl">
<constructor-arg ref="profilesessionFactory"/>
<!-- ... -->
</bean>
Finally you can take advantage of #Primary annotation / primary="true" directive:
<bean id="sessionFactory" primary="true">
<!-- ... -->
This will prefer sessionFactory when autowiring and there are two possibilities instead of throwing an exception.
Its because of...if you dont mention #Primary(annotation) / primary="true"(in xml config), Hiibernate does not know which sessionfactory to pick up.In that case it will give you the error mentioned. Just try with #Primary annotation it will work...
I tried to shorten this to what I think is relevant, I hope it's sufficient and not overwhelming. Please help!
I'm converting a small wicket+databinder+hibernate web application to use wicket+spring+hibernate. I have a DAO service class, with a hibernate SessionFactory injected by Spring. I am able to do read-only operations using the session factory (autocommit is on by default). What I want to do is use the HibernateTransactionManager and the #Transactional annotation to do a transactional operation.
I define a DAO service implementation, which uses an injected SessionFactory in a method marked #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);
}
}
When I try to call isObjectPersistent(), I get a hibernate exception because no one has called 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)
I also notice from the full stack trace that the OpenSessionInViewFilter is being invoked, I'm not sure if that's relevant. Let me know if you need the rest of the stack trace.
If I create a custom WebRequestCycle subclass, which begins a transaction, I can get past this. This seems to me to undermine the purpose of #Transactional, and my implementation of it also turned out to be problematic.
Here is my applicationContext.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>
How can I get my DAO to begin a transaction, commit at the end of that method, or rollback on error? I want to use the most minimal/standard configuration possible; I prefer annotations over XML if given the choice.
Edit:
I revised the applicationContext above to remove the AOP configuration stuff, which wasn't working, anyway.
Using the debugger, I determined that the SessionImpl stored in the TransactionInterceptor's session holder map is not the same session as the SessionImpl that is retrieved in the DAO method when I call sessionFactory.getCurrentSession(). Can anyone explain why this is? What am I doing wrong? The magic is not working. =(
Edit
I also notice the following message in my console during startup:
WARN - stractEhcacheRegionFactory - No TransactionManagerLookup found in Hibernate config, XA Caches will be participating in the two-phase commit!
It turns out that the problem was not actually in the configuration information that I posted. Sorry!
My above configuration links to an externalized hibernate.cfg.xml, which declared the following property:
<!-- Enable Hibernate's automatic session context management -->
<property name="current_session_context_class">thread</property>
I must've copied this from some sample hibernate config file somewhere. This property caused my SessionFactory to ignore the session context provided by spring, and use a thread-local context in its place. Removing this property fixed the problem (hibernate uses the JTA context by default if none is specified).
This can be made much simpler. Read this section 13.3.3 (Hibernate) Declarative transaction demarcation, especially the last part. This is usually enough configuration if you use #Transactional:
<?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:aop="http://www.springframework.org/schema/aop"
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/tx
http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">
<!-- SessionFactory, DataSource, etc. omitted -->
<bean id="transactionManager"
class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory"/>
</bean>
<tx:annotation-driven/>
<bean id="myProductService" class="product.SimpleProductService">
<property name="productDao" ref="myProductDao"/>
</bean>
</beans>
To answer your comments:
No, if you inject the components via Spring, Spring does the wiring, hibernate doesn't need to know anything about spring, that's the whole point of using spring (to decouple the individual layers). Service and dao is a separation that not everybody uses. The important part is that the public methods backed by the interface are marked as transactional, because all methods marked as transactional will be intercepted by a proxy that does the transaction handling, while others won't.
You may want to read the general section about Declarative Transaction Managmenet in Spring to understand the process (this applies to all transactional technologies, not just to hibernate).