Annotation based transactions not working - spring 3
I am trying to integrate hibernate into my first Spring web-project using annotation-based transactions. However, this does not work, as I notice on a save query not committing.
This is the aop configuration:
<context:annotation-config />
<context:component-scan base-package="package.names.project.controller" />
<context:component-scan base-package="package.names.project.model" />
<context:component-scan base-package="package.names.common.util" />
<aop:aspectj-autoproxy>
<aop:include name="Logger" />
</aop:aspectj-autoproxy>
<!-- the transactional advice (what 'happens'; see the <aop:advisor/> bean
below) -->
<tx:advice id="txAdvice" transaction-manager="txManager">
<!-- the transactional semantics... -->
<tx:attributes>
<!-- other methods use the default transaction settings (see below) -->
<tx:method name="save" read-only="false"/>
<tx:method name="*" rollback-for="Exception" />
</tx:attributes>
</tx:advice>
<!-- ensure that the above transactional advice runs for any execution of
an operation defined by a service in the service package -->
<aop:config>
<aop:pointcut id="serviceOperations" expression="execution(* package.names.project.dao.*.*(..))" />
<aop:advisor advice-ref="txAdvice" pointcut-ref="serviceOperations" />
</aop:config>
<!-- similarly, don't forget the PlatformTransactionManager -->
<bean id="txManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<bean id="L开发者_如何学JAVAogger" class="package.names.common.aop.Logger" />
<tx:annotation-driven transaction-manager="txManager" />
I have the following hibernate configuration:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<property name="hibernate.connection.driver_class">org.postgresql.Driver</property>
<property name="hibernate.connection.url">jdbc:postgresql://localhost:5432/db</property>
<property name="hibernate.connection.username">user</property>
<property name="connection.password">pass</property>
<property name="connection.pool_size">1</property>
<property name="hibernate.dialect">org.hibernate.dialect.PostgreSQLDialect</property>
<property name="show_sql">true</property>
<property name="hbm2ddl.auto">update</property>
<property name="connection.autocommit">true</property>
<mapping class="package.names.model.Person" />
</session-factory>
</hibernate-configuration>
Bean declarations:
<bean id="personDao" class="package.names.project.dao.PersonDaoImpl"></bean>
<bean id="dbService" class="package.names.project.service.DbService"></bean>
My service layer looks like this
@Service("Service")
public class DbService {
@Autowired
private PersonDao personDao;
public void setPersonDao(PersonDao personDao){
this.personDao=personDao;
}
@Transactional(readOnly = false)
public void save(Person person){
personDao.save(person.getName(), person.getAge());
}
@Transactional
public List<String> listPerson(){
return personDao.listPerson();
}
}
Changed the sessionFactory in my dao to an autowired one:
public class PersonDaoImpl implements PersonDao {
@Autowired
SessionFactory factory;
public void setFactory(SessionFactory factory){
this.factory = factory;
}
public Long save(String personName, Integer personAge) {
Long personId = null;
Session session = factory.openSession();
System.out.println( TransactionSynchronizationManager.isActualTransactionActive());
Person person = new Person();
person.setName(personName);
person.setAge(personAge);
session.saveOrUpdate(person);
session.close();
return personId;
}..
There are just no signs the transactions are committed. (No insert statements are shown while show-sql is true) There are no error messages to clearify it.
(if I add 'session.beginTransaction().commit()' to my save() function, it will work. But that seems against the principles of declarative transaction management.)
UPDATE:
The database connection works fine, as does the listPerson() method.
I can confirm transaction is active in the save method and that the save method is used by using
logger.info(TransactionSynchronizationManager.isActualTransactionActive())
And I can see autocommitting is set to true in the logging:
INFO: autocommit mode: true
I even created a service layer between the controller and the dao, to be sure it's not the dao layer causing problems. I used
@Transactional(readOnly = false)
to be sure the problem wasn't caused by a read-only mode. What's left to try?
UPDATE 2: Fixed the problem. It was caused by opening a new session each time the method save was called. Now I just use factory.openSession() one time, after that I use factory.getCurrentSession() and I don't close the session.
The default behavior for
ApplicationContext
implementations is to eagerly pre-instantiate all singleton beans at startup.
Since your exception is complaining about eager-loading try adding lazy-init="true"
attribute to your Spring beans.
Example:
<bean id="transactionManager"
class="org.springframework.orm.hibernate3.HibernateTransactionManager" lazy-init="true">
<property name="sessionFactory" ref="mySessionFactory" />
</bean>
For more information on Lazy initialization see.
EDIT
Also to resolve your commit issue, try adding below to your datasource
settings:
<property name="defaultAutoCommit" value="true" />
To use @Transactional
your bean must be accessed via Spring container. In provided sample I don't see anything that would show how this bean is created. You must have either
<context:component-scan base-package="package.names"/>
or
<bean class="package.names.PersonDaoImpl"/>
And then you would have to get bean from container either via autowiring or getBean()
.
So how do you get your DAO bean instance in the application? Also it would be helpful to see bean class declaration as well as interface. I did have cases when transactional proxies are not properly created because of some specifc class relationships.
精彩评论