2012-04-04 35 views
17

Anteriormente tuve una publicación sobre este problema que se resolvió. Sin embargo, desde la reconstrucción del proyecto con beans autoalambrados y menos configuración XML, encuentro que estoy volviendo a visitar este tema. He seguido la forma en que mi proyecto anterior implementó esto, pero no funciona. ¿Alguien puede ayudarme con por qué o qué debería cambiar para que funcione?Spring @Transactional no funciona

Estoy a propósito utilizando un nombre de tabla inexistente en el método de insertar detalles de usuario para lanzar deliberadamente una excepción. Sin embargo, las instrucciones para insertar usuario e insertar roles de usuario no se revierten. Por favor ayuda.


Mi diseño actual para el registro es así.

Parte de 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 de contexto de aplicación:

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


Registro controlador:

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


Registro servicio:

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


Registro 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

El problema parecía ser con tener las exploraciones de componentes para los componentes distintos de controladores en el xml servlet. Moví '' context: component-scan base-package = "com.doyleisgod.golfer.dao "/> 'En el contexto xml de la aplicación que parecía resolver el problema. No estoy seguro de por qué si alguien quiere proporcionar la razón detrás de por qué este trabajo se pudo publicar. –

Respuesta

39

La razón por la que mover los context:component-scan etiquetas para el xml contexto de aplicación fija el comportamiento transaccional es : <tx:annotation-driven /> es un postprocesador que envuelve @Transactional métodos de bean anotados con un interceptor de método AOP que maneja el comportamiento transaccional. Primavera post-procesadores, sólo funcionan en el contexto de la aplicación específica que se definen en.

En su caso, se han definido los <tx:annotation-driven /> post-procesador en el contexto de aplicación, mientras que los granos con anotada @Transactional están en el contexto de aplicación servlet . Por lo tanto, el postprocesador <tx:annotation-driven /> solo operaba en los beans de contexto de la aplicación, no en los beans de contexto del servlet. Cuando las etiquetas context:component-scan se movieron al contexto de la aplicación, el postprocesador <tx:annotation-driven /> envolvió sus métodos transaccionales apropiadamente.

Espero que tenga algún sentido.

[Editar]

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

Pensé que Spring MVC * requiere * el' @ Controller's para escanear desde el contexto de DispatcherServlet, en lugar del contexto contextual ContextLoaderListener. Por lo tanto, me sorprende que el escaneo de '@ Controller' también al contexto de la aplicación, haga que funcione' @ RequestMapping'. (De hecho, permitirá uno para usar '@ Transactional' en las clases' @ Controller', simplemente preguntándose sobre los efectos secundarios.) – Arjan

+0

(Una prueba * rápida * para eliminar el escaneo de '@ Controller' del contexto del DestiladorServidor, y escanear todo desde el contexto de la aplicación, de hecho hace que todos mis '@ RequestMapping's sean ignorados, y, por lo tanto, falla.) – Arjan

+2

El OP declara en los comentarios que movió el dao y servicios-escaneo de componentes en el contexto de la aplicación que corrigió su problema de Transacción. La pregunta en el comentario fue por qué, que respondí. El OP NO movió la exploración de componentes para controladores MVC fuera del contexto del servlet. Por lo tanto, no estoy seguro de qué se tratan estos comentarios de @Arjan. – MarkOfHall

Cuestiones relacionadas