我正在尝试在 Spring MVC 应用程序中使用 @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: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" >
<!-- Root Context: defines shared resources visible to all other web components -->
<tx:annotation-driven transaction-manager="txManager"/>
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close" >
<property name="driverClassName" value="org.postgresql.Driver" />
<property name="url" value="jdbc:postgresql://localhost:5432/qas" />
<property name="username" value="postgres" />
<property name="password" value="P0stgr3s" />
</bean>
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate" >
<constructor-arg name="dataSource" ref="dataSource" />
</bean>
</beans>
控制器文件:
package com.silverthorn.txtest;
import java.text.DateFormat;
import java.util.Date;
import java.util.Locale;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
/**
* Handles requests for the application home page.
*/
@Controller
public class HomeController {
private static final Logger logger = LoggerFactory.getLogger(HomeController.class);
@Autowired
private TagsDAO tagsDAO;
/**
* Simply selects the home view to render by returning its name.
*/
@RequestMapping(value = "/", method = RequestMethod.GET)
public String home(Locale locale, Model model) {
logger.info("Welcome home! the client locale is "+ locale.toString());
Date date = new Date();
DateFormat dateFormat = DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.LONG, locale);
String formattedDate = dateFormat.format(date);
model.addAttribute("serverTime", formattedDate );
return "home";
}
@RequestMapping(value="/tags", method=RequestMethod.GET)
public @ResponseBody ResponseDocument addTags(@RequestParam("tag") String[] tags) {
ResponseDocument doc = new ResponseDocument();
try {
tagsDAO.addTags(tags);
} catch ( RuntimeException e ) {
doc.setError("Could not add tags");
}
return doc;
}
}
和 DAO 文件:
package com.silverthorn.txtest;
import java.util.HashMap;
import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
@Repository("tagsDAO")
public class TagsDAO {
@Autowired
private NamedParameterJdbcTemplate jdbcTemplate;
@Transactional(propagation = Propagation.REQUIRED)
public void addTags(String[] tags) {
Map<String, Object> params = new HashMap<String, Object>();
for(String tag : tags) {
params.put("tag", tag);
jdbcTemplate.update("INSERT INTO qas.tags (tag) VALUES (:tag)", params);
}
}
}
以下是事务应该失败时的输出日志示例:
DEBUG: org.springframework.jdbc.core.JdbcTemplate - Executing prepared SQL update
DEBUG: org.springframework.jdbc.core.JdbcTemplate - Executing prepared SQL statement [INSERT INTO qas.tags (tag) VALUES (?)]
DEBUG: org.springframework.jdbc.datasource.DataSourceUtils - Fetching JDBC Connection from DataSource
DEBUG: org.springframework.jdbc.core.JdbcTemplate - SQL update affected 1 rows
DEBUG: org.springframework.jdbc.datasource.DataSourceUtils - Returning JDBC Connection to DataSource
DEBUG: org.springframework.jdbc.core.JdbcTemplate - Executing prepared SQL update
DEBUG: org.springframework.jdbc.core.JdbcTemplate - Executing prepared SQL statement [INSERT INTO qas.tags (tag) VALUES (?)]
DEBUG: org.springframework.jdbc.datasource.DataSourceUtils - Fetching JDBC Connection from DataSource
DEBUG: org.springframework.jdbc.datasource.DataSourceUtils - Returning JDBC Connection to DataSource
INFO : org.springframework.beans.factory.xml.XmlBeanDefinitionReader - Loading XML bean definitions from class path resource [org/springframework/jdbc/support/sql-error-codes.xml]
INFO : org.springframework.jdbc.support.SQLErrorCodesFactory - SQLErrorCodes loaded: [DB2, Derby, H2, HSQL, Informix, MS-SQL, MySQL, Oracle, PostgreSQL, Sybase]
DEBUG: org.springframework.jdbc.support.SQLErrorCodesFactory - Looking up default SQLErrorCodes for DataSource [org.apache.commons.dbcp.BasicDataSource@704fb]
DEBUG: org.springframework.jdbc.datasource.DataSourceUtils - Fetching JDBC Connection from DataSource
DEBUG: org.springframework.jdbc.datasource.DataSourceUtils - Returning JDBC Connection to DataSource
DEBUG: org.springframework.jdbc.support.SQLErrorCodesFactory - Database product name cached for DataSource [org.apache.commons.dbcp.BasicDataSource@704fb]: name is 'PostgreSQL'
DEBUG: org.springframework.jdbc.support.SQLErrorCodesFactory - SQL error codes for 'PostgreSQL' found
DEBUG: org.springframework.jdbc.support.SQLErrorCodeSQLExceptionTranslator - Translating SQLException with SQL state '23505', error code '0', message [ERROR: duplicate key value violates unique constraint "tags_pkey"
Detail: Key (tag)=(hello) already exists.]; SQL was [INSERT INTO qas.tags (tag) VALUES (?)] for task [PreparedStatementCallback]
从日志文件中可以看出,似乎没有创建事务。在没有创建事务的情况下,显然无法回滚对数据库的更新。我已经把头撞到墙上了 2 天了,并且无法更接近获得所需的行为。我能得到的任何帮助将不胜感激。