Spring @Transactional не работает

У меня ранее был пост по этой проблеме, который был разрешен. Однако, поскольку перестройка проекта с помощью проводной beans и меньше конфигурации 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

Причина, по которой перемещение тегов context:component-scan в xml-контексте приложения фиксировало транзакционное поведение: <tx:annotation-driven /> - это постпроцессор, который обертывает @Transactional аннотированные методы bean с помощью перехватчика метода AOP, который обрабатывает транзакционное поведение, Spring постпроцессоры работают только в конкретном контексте приложения, в котором они определены.

В вашем случае вы определили пост-процессор <tx:annotation-driven /> в контексте приложения, а beans, аннотированный с помощью @Transactional, находятся в контексте приложения сервлета. Таким образом, постпроцессор <tx:annotation-driven /> работает только в контексте приложения beans, а не в контексте сервлета beans. Когда теги context:component-scan были перенесены в контекст приложения, то пост-процессор <tx:annotation-driven /> надлежащим образом завернул свои транзакционные методы.

Надеюсь, что это имеет смысл.

[изменить]

В чем разница между контекстом приложения и контекстом сервлета?

Что такое постпроцессор Spring и как он работает?

Что такое AOP в Spring?