2012-04-04 87 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