Spring @Transactional无法正常工作

时间:2012-04-04 21:20:30

标签: spring spring-mvc spring-transactions transactional

我之前有一篇关于这个问题的帖子已经解决了。但是,自从使用自动连线bean和较少的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"/>


应用程序上下文的一部分

<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>


注册控制器

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";
    }
}


注册服务

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);
    }
}


注册 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;
    }
}

1 个答案:

答案 0 :(得分:48)

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

在您的情况下,您已在应用程序上下文中定义了<tx:annotation-driven />后处理程序,而使用@Transactional注释的bean位于servlet应用程序上下文中。因此,<tx:annotation-driven />后处理器仅在应用程序上下文bean上运行,而不是在servlet上下文bean上运行。当context:component-scan标记移动到应用程序上下文时,<tx:annotation-driven />后处理程序会适当地包装其事务方法。

希望有道理。

<强> [编辑]

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

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

What is AOP in Spring?