【问题标题】:Why is my transaction not rolledback by spring annotations?为什么我的事务没有被spring注解回滚?
【发布时间】:2016-05-28 07:26:46
【问题描述】:

我想尽办法调试这个问题。基本上,我有一个 mvc 应用程序,其中有一个 BusinessManagerImpl 类,它有 2 个 DAO(UserDaoProductDao),并且正在使用带有连接池而不是 ORM 的 JDBC。数据库是带有 InnoDb 引擎的 mySQL。 RestUserControllerBusinessManagerImpl 的调用类。

BusinessManagerImpl.addUser() 已使用@Transactional 注释进行注释。我也尝试在课堂级别注释@Transactional,但似乎没有什么不同。两个 DAO 也都这样注释。

BusinessManagerImpl.addUser() 使用UserDao 插入用户,但随后对ProductDao.getAllProducts() 的调用会故意抛出RuntimeException 以使事务回滚。我的期望是用户不应该被插入,因为 RuntimeException 已经发生并且事务将被回滚,但我已经检查了我的数据库并插入了新用户。

我尝试过抛出一个检查异常并使用@Transactional 注释的“rollback for”参数,但它不起作用。我还尝试了不同的传播值,例如Propagation.Required,但似乎对回滚事务没有影响。我曾尝试在 stackoverflow 和 google 上进行搜索,但没有找到任何帮助。有人可以阐明我做错了什么或遗漏了什么吗?谢谢你。以下是我的设置:

应用程序上下文.xml

<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:mvc="http://www.springframework.org/schema/mvc"
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.2.xsd
    http://www.springframework.org/schema/mvc 
    http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd
    http://www.springframework.org/schema/tx
    http://www.springframework.org/schema/tx/spring-tx-3.2.xsd        
    http://www.springframework.org/schema/context 
    http://www.springframework.org/schema/context/spring-context-3.2.xsd">

<tx:annotation-driven transaction-manager="transactionManager" proxy-target-class="true"/>
<context:component-scan base-package="someproject" />
<!-- <context:annotation-config /> -->


<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
    <property name="driverClassName" value="com.mysql.jdbc.Driver" />
    <property name="url" value="jdbc:mysql://localhost:3306/somedb" />
    <property name="username" value="xxx" />
    <property name="password" value="yyy" />
</bean>

<bean id="transactionManager"
    class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource"></property>
</bean> 

BusinessManagerImpl 类

@Service 
public class BusinessManagerImpl implements BusinessManager{

@Autowired
private UserDao userDao;

@Autowired
private ProductDao productDao;
....
@Override   
@Transactional(propagation=Propagation.REQUIRED)
public User addUser(User user) throws Exception {
    // TODO Auto-generated method stub
    User tempUser = userDao.addUser(user);
    productDao.getAllProducts();

    return tempUser;
}

UserDaoImpl 类

@Service
public class UserDaoImpl implements UserDao {

private DataSource dataSource;

@Autowired
public UserDaoImpl(DataSource dataSource) {
    super();
    setDataSource(dataSource);
}   

public void setDataSource(DataSource dataSource) {
    this.dataSource = dataSource;
}       

private JdbcTemplate getJdbcTemplate(){
    JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);

    return jdbcTemplate;
}       
...
    @Override   
@Transactional(propagation=Propagation.MANDATORY)
public User addUser(final User user) {
    KeyHolder holder = new GeneratedKeyHolder();

    final String sql = "insert into user (username, password) "
            + " VALUES (?, ?)";

    getJdbcTemplate().update(new PreparedStatementCreator() {

        @Override
        public PreparedStatement createPreparedStatement(Connection connection) throws SQLException {
            PreparedStatement ps = connection.prepareStatement(sql.toString(), Statement.RETURN_GENERATED_KEYS);

            int index = 1;              

            ps.setString(index++, user.getUsername());
            ps.setString(index++, user.getPassword());


            return ps;
        }
    }, holder);

    int seq = holder.getKey().intValue();
    user.setSeq(seq);
    return user;
}

ProductDaoImpl 类

@Service
public class ProductDaoImpl implements ProductDao {

private DataSource dataSource;

@Autowired
public ProductDaoImpl(DataSource dataSource) {
    super();
    setDataSource(dataSource);
}   

public void setDataSource(DataSource dataSource) {
    this.dataSource = dataSource;
}   

@Override
@Transactional(propagation=Propagation.MANDATORY)
public List<Product> getAllProducts() throws Exception {

    if(true)
        throw new RuntimeException("on purpose");

    JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
    List<Product> products = jdbcTemplate.query(
            "select * from product",
            new ProductRowMapper());

    return products;
}

RestUserController 类

@RestController
public class RestUserController {
private static Logger logger = LoggerFactory.getLogger(RestUserController.class);

@Autowired
private BusinessManager businessManager;

@RequestMapping(value = "/adduser", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<String> createEmployee(@RequestBody User user) 
{
    logger.debug("adding user:"+user);
    User addedUser=null;
    try {
        addedUser = businessManager.addUser(user);
        return new ResponseEntity(addedUser, HttpStatus.CREATED);
    } catch (Exception e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }

    return new ResponseEntity(HttpStatus.INTERNAL_SERVER_ERROR);

}

web.xml

   <?xml version="1.0" encoding="UTF-8"?>
    <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" version="2.5">
    <display-name>Spring3 MVC Application</display-name>
    <context-param>
        <param-name>contextConfigLocation</param-name>
            <param-value>/WEB-INF/application-context.xml  
        </param-value>
        </context-param>
          <listener>
            <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
          </listener>
          <servlet>
            <servlet-name>spring-web</servlet-name>
            <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
            <load-on-startup>1</load-on-startup>
          </servlet>
          <servlet-mapping>
            <servlet-name>spring-web</servlet-name>
            <url-pattern>/</url-pattern>
          </servlet-mapping>
        </web-app>

spring-web-servlet.xml

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:mvc="http://www.springframework.org/schema/mvc"
    xsi:schemaLocation="
        http://www.springframework.org/schema/beans     
        http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
        http://www.springframework.org/schema/mvc 
        http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd
        http://www.springframework.org/schema/context 
        http://www.springframework.org/schema/context/spring-context-3.2.xsd">

    <context:component-scan base-package="someproject" />

    <bean
        class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix">
            <value>/WEB-INF/views/jsp/</value>
        </property>
        <property name="suffix">
            <value>.jsp</value>
        </property>
    </bean>

    <mvc:resources mapping="/resources/**" location="/resources/" />

    <mvc:annotation-driven />

</beans>

【问题讨论】:

  • 你使用哪个应用服务器?
  • 只是 tomcat。 tomcat 中没有配置,应用程序被部署为外部 tomcat 中的战争
  • 我猜你有一个由DispatcherServlet加载的配置xml,其中还包含&lt;context:component-scan base-package="someproject" /&gt;...
  • 嗨,M. Deinum,你是对的,我的 spring-web-context.xml 中确实有这个。我已经更新了我的问题以包括 web.xml 和 spring-web-context.xml。但是,您的评论让我开始思考,所以我决定将我的控制器所在的 spring-web-context.xml 中的基本包更改为 ,但是我仍然保持 web.xml 原样,并且事务管理正常工作!
  • 我也尝试过从 web.xml 中排除上面的 sn-p,但是从 spring 中得到了一堆自动装配堆栈跟踪,所以显然排除是不正确的。我不是 100% 确定,但我似乎没有在书籍中遇到过如何为这种情况进行设置的书籍。我只能假设您以前一定遇到过这种情况。你知道为什么 spring 无法协调 spring-web-context.xml 和 application-context.xml 中的两个组件扫描吗?

标签: java mysql spring transactions


【解决方案1】:

我发现在 Dao 实现类上使用 @Service 注释很奇怪。尝试用@Repository 替换它们并将rollbackFor = {Exception.class} 添加到所有事务注释中。

【讨论】:

  • 谢谢,但我确实记得也使用过@Repository,但结果相同。将使用 rollbackFor 参数再次尝试您的建议并在此处发布结果。
  • 好的,我已经尝试了你的建议,但我的用户仍然插入到数据库中:(
  • 感谢 ritesh.garg 的意见。
【解决方案2】:

对于您的 2 个应用程序上下文配置,您必须以不同的方式指定基本包。不要让 web 应用上下文扫描 DAO 包。

【讨论】:

  • 这如何改变观察到的行为?
  • 感谢 user29637。为了回答 Nikem 的问题,当 productDao 中抛出异常并且 base-package 被指定为“someproject.controller”时,没有插入要插入的用户
猜你喜欢
  • 1970-01-01
  • 2020-04-28
  • 1970-01-01
  • 2022-08-18
  • 2020-05-01
  • 2018-06-17
  • 2011-07-18
  • 1970-01-01
  • 2021-07-06
相关资源
最近更新 更多