2012-04-04 88 views
17

我以前曾就此问题发布了一条已解决的帖子。但是,由于使用自动连线bean和较少的XML配置重建项目,我发现我正在重新讨论这个问题。我遵循我之前的项目实施这个方式,但它不起作用。有人可以帮我解释为什么或者我应该改变以使其工作吗?Spring @Transactional无法正常工作

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


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

部分

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; 
    } 
} 
+0

问题似乎是与具有比在servlet XML控制器的其他组件的组件扫描。我移动了' '进入应用上下文xml似乎解决了这个问题我不确定为什么所以如果有人想提供为什么这个作品可以免费发布的原因 – 2012-04-04 21:30:35

回答

39

该移动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?

+0

我认为Spring MVC *需要* @ Controller's从DispatcherServlet上下文中扫描,而不是根ContextLoaderListener上下文。因此,我很惊讶移动'@ Controller'到应用上下文的扫描,仍然会使'@ RequestMapping'工作(事实上,它会允许一个在@ @ Controller类中使用'@Transactional';只是想知道副作用。) – Arjan 2013-02-03 12:20:07

+0

(A * quick * test从DispatcherServlet的上下文中删除对@ Controller的扫描,并扫描应用程序上下文中的所有内容,确实使我所有的@ @ RequestMapping被忽略,并因此失败。) – Arjan 2013-02-03 12:30:23

+2

OP在评论中声明他将dao和services组件扫描移到了解决其交易问题的应用上下文中。评论中的问题是为什么,我回答了。 OP没有将MVC控制器的组件扫描移出servlet上下文。因此,我不确定@Arjan的这些评论是关于什么的。 – MarkOfHall 2013-03-20 02:40:40