Spring @Transactional 不起作用

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/10019426/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-08 05:02:59  来源:igfitidea点击:

Spring @Transactional not working

springspring-mvcspring-transactionstransactional

提问by Chris Doyle

I previously had a post on this issue that was resolved. However since rebuilding the project with auto wired beans and less XML configuration I find I am revisiting this issue. I have followed the way my previous project implemented this but it doesn't work. Can someone help me with why or what I should change to make it work?

我之前有一篇关于这个问题的帖子已经解决了。但是,自从使用自动连接的 bean 和较少的 XML 配置重建项目后,我发现我正在重新审视这个问题。我已经按照我以前的项目实施的方式进行了此操作,但它不起作用。有人可以帮助我解释为什么或我应该改变什么以使其工作吗?

I am on purpose using a non existent table name in the insert user details method to deliberately throw an exception. However the statements for insert user and insert user roles are not rolled back. Please help.

我故意在插入用户详细信息方法中使用不存在的表名来故意抛出异常。但是,插入用户和插入用户角色的语句不会回滚。请帮忙。



My current design for the registration is like this.

我目前的注册设计是这样的。

Part of servlet.xml:

部分servlet.xml中

<context:component-scan base-package="com.doyleisgod.golfer.controllers"/>
<context:component-scan base-package="com.doyleisgod.golfer.dao"/>
<context:component-scan base-package="com.doyleisgod.golfer.services"/>
<context:component-scan base-package="com.doyleisgod.golfer.validators"/>


Part of application context:


部分应用程序上下文

<context:annotation-config />
<tx:annotation-driven />    

<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="${jdbc.driverClassName}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>

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


Registration controller:


注册控制器

package com.doyleisgod.golfer.controllers;

import javax.validation.Valid;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import com.doyleisgod.golfer.formdata.RegistrationForm;
import com.doyleisgod.golfer.services.IRegistrationService;
import com.doyleisgod.golfer.validators.RegistrationFormValidator;

/**
 * Description: Registration controller provides and processes the registration form.
 * @author Chris Doyle
*/
@Controller
@RequestMapping("/registration.htm")
public class RegistrationController {
    protected final Log logger = LogFactory.getLog(getClass());
    @Autowired private IRegistrationService iRegistrationService;
    @Autowired private RegistrationFormValidator registrationFormValidator;

    // sets a customer validator for the registration form
    @InitBinder
    protected void initBinder(WebDataBinder binder) {
        binder.setValidator(registrationFormValidator);
    }

    // Description: Method called by a get request to the registration controller. Returns the
    @RequestMapping(method=RequestMethod.GET)
    public String registration (Model model){
        model.addAttribute(new RegistrationForm());
        return "registration";
    }

     // Description: Method called by a post request to the registration controller. Method calls validation on the registration form using custom validator and returning 
     // any errors back to the user. 
    @RequestMapping(method=RequestMethod.POST)
    public String processRegistration (@Valid RegistrationForm registrationForm, BindingResult bindingResult, Model model){
        logger.info("Received the following registration form details");
        logger.info(registrationForm.toString());

        if (bindingResult.hasErrors()) {
            logger.warn("Registration Validation Failed");
            model.addAttribute("validationError", "Please correct the fields marked with errors");
            return "registration";
        }

        try {
            iRegistrationService.registerUser(registrationForm);
        } catch (Exception e) {
            logger.error("An Exception has occured processing the registration form");
            model.addAttribute("exceptionError", "An exception has occured, please try again.");
            e.printStackTrace();
            return "registration";
        }

        return "redirect:login.htm?registration=sucessful";
    }
}


Registration service:


注册服务

package com.doyleisgod.golfer.services;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.encoding.ShaPasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionSynchronizationManager;

import com.doyleisgod.golfer.dao.IRegistrationDAO;
import com.doyleisgod.golfer.formdata.RegistrationForm;

@Service("IRegistrationService")
public class RegistrationService implements IRegistrationService {
    @Autowired private IRegistrationDAO iRegistrationDAO;
    private final boolean enabled = true;
    private final String roles = "ROLE_USER";


    @Override
    @Transactional (rollbackFor = Exception.class)
    public void registerUser(RegistrationForm registrationForm) throws Exception {
        System.out.println("inside the registerUser method. is wrapped in transaction: "+TransactionSynchronizationManager.isActualTransactionActive());

        String username = registrationForm.getUsername();
        String password = registrationForm.getPassword();
        String firstname = registrationForm.getFirstname();
        String lastname = registrationForm.getLastname();
        String email = registrationForm.getEmail();
        int handicap = Integer.parseInt(registrationForm.getHandicap());
        String encryptedPassword = ((new ShaPasswordEncoder()).encodePassword(password, username));

        iRegistrationDAO.insertUser(username, encryptedPassword, enabled);
        iRegistrationDAO.insertRoles(username, roles);
        iRegistrationDAO.insertUserDetails(username, firstname, lastname, email, handicap);
    }

    @Override
    public boolean checkUser(String username) {
        return iRegistrationDAO.checkUserName(username);
    }
}


Registration DAO:


注册DAO

package com.doyleisgod.golfer.dao;

import javax.annotation.Resource;
import org.apache.commons.dbcp.BasicDataSource;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.support.TransactionSynchronizationManager;

@Repository("iRegistrationDAO")
public class RegistrationDAO extends JdbcTemplate implements IRegistrationDAO {

    @Resource private BasicDataSource dataSource;

    @Override
    public boolean checkUserName(String username) {
        int db_user = queryForInt("select count(username) from users where username = ?", username);

        if (db_user == 1 ){
            return true;
        }

        return false;
    }

    @Override
    public void insertUser(String username, String password, boolean enabled) throws Exception {
        System.out.println("inside the insertuser method. is wrapped in transaction: "+TransactionSynchronizationManager.isActualTransactionActive());

        update("insert into users (username, password, enabled) VALUES (?,?,?)", username, password, enabled);
    }

    @Override
    public void insertRoles(String username, String roles) throws Exception {
        update("insert into user_roles (username, authority) VALUES (?,?)", username, roles);
    }

    @Override
    public void insertUserDetails(String username, String firstname, String lastname, String email, int handicap) throws Exception {
        update("insert into user_detailss (username, first_name, last_name, email_address, handicap)" + 
                "VALUES (?,?,?,?,?)", username, firstname, lastname, email, handicap);

    }

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

    public BasicDataSource getDataSource() {
        return dataSource;
    }
}

回答by MarkOfHall

The reason that moving the context:component-scantags to the application context xml fixed the transactional behavior is: <tx:annotation-driven />is a post-processor that wraps @Transactionalannotated bean methods with an AOP method interceptor which handles transactional behavior. Spring post-processors, only operate on the specific application context they are defined in.

context:component-scan标记移动到应用程序上下文 xml 修复事务行为的原因是: <tx:annotation-driven />是一个后处理器,@Transactional它使用处理事务行为的 AOP 方法拦截器包装带注释的 bean 方法。Spring 后处理器,仅对它们定义的特定应用程序上下文进行操作。

In your case, you have defined the <tx:annotation-driven />post-processor in the application context, while the beans annotated with @Transactionalare in the servlet application context. Thus, the <tx:annotation-driven />post-processor only operated on the application context beans, not the servlet context beans. When the context:component-scantags were moved to the application context, then the <tx:annotation-driven />post-processor wrapped their transactional methods appropriately.

在您的情况下,您已经<tx:annotation-driven />在应用程序上下文中定义了后处理器,而带有注释的 bean@Transactional位于 servlet 应用程序上下文中。因此,<tx:annotation-driven />后处理器只对应用程序上下文 bean 进行操作,而不对 servlet 上下文 bean 进行操作。当context:component-scan标签被移动到应用程序上下文时,<tx:annotation-driven />后处理器会适当地包装它们的事务方法。

Hope that makes some sense.

希望这是有道理的。

[Edit]

[编辑]

What is the difference between the Application Context and a Servlet Context?

应用程序上下文和 Servlet 上下文有什么区别?

What is a Spring post-processor and how does it work?

什么是 Spring 后处理器,它是如何工作的?

What is AOP in Spring?

Spring 中的 AOP 是什么?