问题
I have tried to configure declarative transaction management within my Spring-based web application and it refuses to cooperate with me.
I have two main problems:
- Setting defaultAutoCommit to false on our dataSource (which we need for our application) causes all queries to rollback, with or without transactions being involved.
- Transactions are configured and proxy classes as well as transactional methods are created, however no transactions appear to be used.
The first problem is rather perplexing, as every individual query is being rolled back in the database. This includes SELECT statements as well. What could cause every query to be rolled back in the database?
As for the second problem, my configuration of the transaction management is outlined below:
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:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:context="http://springframework.org/schema/context"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-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/context http://www.springframework.org/schema/spring-context-3.0.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.0.xsd"
default-autowire="byName">
<!-- 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="*" 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(* foo.bar.service.*.*(..))"/>
<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="dataSource" destroy-method="close" class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName" value="${jdbc.driverClassName}" />
<property name="url" value="${jdbc.url}" />
<property name="username" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
<property name="defaultAutoCommit" value="false" />
</bean>
<bean id="fooService" class="foo.bar.service.FooService" />
<bean id="barService" class="foo.bar.service.BarService" />
<bean id="zapService" class="foo.bar.service.ZapService" />
</beans>
From all of the tutorials and forums that I have visited in trying to solve this problem, I believe that my configuration should be correct. However I do not fully understand aop and spring transactions so I may be missing something crucial.
As alluded to above, I can trace through my logs and see proxies, as well as transactional methods, being created for my service classes. However when I actually run the application and trace through the logs I don't see any statements dealing with the DataSourceTransactionManager or transactions being created, committed, rolled back, etc.
It would appear to me that nothing is actually being run, and I am terribly confused as I have followed many different tutorials and tried this many different ways, but it always ends up with this situation.
I am also fairly certain that I have my log4j properties set up correctly to receive messages from the DataSourceTransactionManager, but I am supplying them below to make sure that it is not just a logging error on my part.
My log4j is set up with the following loggers to try and trace through the transactions:
log4j.logger.org.springframework=INFO, file
log4j.logger.org.springframework.jdbc.datasource=DEBUG, file
log4j.logger.org.springframework.transaction=DEBUG, file
Note: I ran the top logger on DEBUG at one point, and that is where I verified that the service proxies were being created.
Does anyone have any insights into what might be happening? I am rather stuck at the moment, as I do see some parts involved with transactions being created but I don't see any signs of any transactions being used whatsoever.
Edit:
Additional information as requested by JB Nizet.
My entire application is annotation-driven and so my service beans are annotated with @Service and injected into my controllers via name-based autowiring.
The below is an example of one of my service classes (names have been changed but will reflect my applicationContext.xml).
@Service("zapService")
public class ZapService
{
/**
* Data access object which performs the database look up
*/
private ZapDAO zapDAO;
/**
* Add the given zap to the database
*
* @param zap a populated zap
*/
public void processNewZap(Zap zap)
{
zapDAO.processNewZap(zap);
}
}
As you can see, my service classes are simply proxies between the controller classes and the dao classes. The DAOs are where I actually handle database connections.
I believe I read somewhere that making the services transactional, rather then the dao classes, was a preferred practice when dealing with transactions. Please correct me if I am wrong.
The ZapDAO class is outlined below.
@Repository("zapDAO")
public class ZapDAO
{
/**
* Log4j logger for this class
*/
Logger logger = Logger.getLogger(ZapDAO.class);
/**
* Spring jdbc object to handle interacting with the database
*/
private JdbcTemplate jdbcTemplate;
public void processNewZap(Zap zap) {
... query constructing logic ...
this.jdbcTemplate.update(INSERT_ZAP_QUERY_SQL);
}
public void setDataSource(DataSource dataSource)
{
Assert.notNull(dataSource, "You must supply a valid data source");
this.jdbcTemplate = new JdbcTemplate(dataSource);
}
}
I use a jdbcTemplate to handle my connections and queries.
回答1:
So after hours upon hours of searching, debugging, and ripping my hair out, I finally stumbled upon this little gem which provided all the answers.
I never would have suspected something like that to be the issue but following the steps outlined in the above link worked perfectly.
Inside my dispatch-servlet.xml I had originally declared my component-scan as follows:
<context:component-scan base-package="foo.bar"/>
Which is a parent package to all of my application beans. So, as described in the link above, Spring was overwriting my transactional service beans from the applicationContext.xml with the service beans from the dispatcher-servlet.xml which did not know about the transactions.
All I did was break up the above component-scan to scan only the folders which contained non-transactional beans.
<context:component-scan base-package="foo.bar.controller"/>
<context:component-scan base-package="foo.bar.model"/>
<context:component-scan base-package="foo.bar.service.display"/>
<context:component-scan base-package="foo.bar.service.security"/>
<!-- foo.bar.service gets scanned in applicationContext.xml and includes
transactions so we must make sure to not include it here. The transactional beans
will be overridden in that case -->
After this my transactions worked exactly as expected and I was finally seeing footprints of the transactions and the DataSourceTransactionManager in my log files. This also fixed my first initial problem of the automatic rollbacks in the database. I guess it must have been closely tied with the lack of transactions.
来源:https://stackoverflow.com/questions/5491748/spring-jdbc-declarative-transactions-created-but-not-doing-anything