2012-04-04 21 views
17

In precedenza avevo un post su questo problema risolto. Tuttavia, da quando ho ricostruito il progetto con auto wired beans e meno configurazione XML, trovo che sto rivisitando questo problema. Ho seguito il modo in cui il mio progetto precedente l'ha implementato ma non funziona. Qualcuno può aiutarmi con il perché o cosa dovrei cambiare per farlo funzionare?Spring @Transactional not working

Sono intenzionalmente utilizzando un nome tabella inesistente nel metodo di inserimento dei dettagli utente per generare deliberatamente un'eccezione. Tuttavia, le istruzioni per inserire utente e inserire i ruoli utente non vengono ripristinate. Per favore aiuto.


Il mio disegno corrente per la registrazione è come questo.

Parte della 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"/> 


Parte della contesto di applicazione:

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


Registrazione regolatore:

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


Registrazione servizio:

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


Registrazione 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

Il problema sembrava essere con avere le scansioni dei componenti per i componenti diversi da controllori del xml servlet. Ho spostato il ' 'Nel contesto applicativo xml che sembrava risolvere il problema.Non sono sicuro del motivo per cui se qualcuno vuole fornire il motivo del perché questo lavoro è caduto libero per postare. –

risposta

39

La ragione per cui spostare i context:component-scan tag al contesto di applicazione xml fissato il comportamento transazionale è : <tx:annotation-driven /> è un post-processore che include i metodi di bean annotati @Transactional con un intercettore di metodo AOP che gestisce il comportamento delle transazioni. I post-processori Spring funzionano solo sullo specifico contesto applicativo in cui sono definiti.

Nel tuo caso, hai definito il postprocessore <tx:annotation-driven /> nel contesto dell'applicazione, mentre i bean annotati con @Transactional si trovano nel contesto dell'applicazione servlet . Pertanto, il postprocessore <tx:annotation-driven /> operava solo sui bean di contesto dell'applicazione, non sui bean di contesto del servlet. Quando i tag context:component-scan venivano spostati nel contesto dell'applicazione, il postprocessore <tx:annotation-driven /> avvolgeva i loro metodi transazionali in modo appropriato.

Spero che abbia un senso.

[Edit]

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

Ho pensato che Spring MVC * richiede * il' @ Controller's da scansionare dal contesto DispatcherServlet, piuttosto che dal contesto ContextLoaderListener root, quindi sono sorpreso che spostare anche la scansione di '@ Controller' nel contesto dell'applicazione, rende comunque' @ RequestMapping' lavoro. (Infatti, consentirà uno per usare '@ Transactional' nelle classi' @ Controller', chiedendo solo gli effetti collaterali.) – Arjan

+0

(A * quick * test rimuove la scansione di '@ Controller' dal contesto di DispatcherServlet e scansiona tutto dal contesto dell'applicazione, in effetti rende tutti i miei '@ RequestMapping' da ignorare, e quindi non riescono.) – Arjan

+2

L'OP afferma nei commenti che ha spostato la scansione dei componenti dao e servizi nel contesto dell'app che ha risolto il problema di Transazione. La domanda nel commento era perché, a cui ho risposto. L'OP NON ha spostato il componente-scan per i controller MVC fuori dal contesto servlet. Quindi, non sono sicuro di cosa siano questi commenti di @Arjan. – MarkOfHall