春季@事务TransactionRequiredException或RollbackException [英] Spring @Transactional TransactionRequiredException or RollbackException

查看:243
本文介绍了春季@事务TransactionRequiredException或RollbackException的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我已经读了很多@Transactional注解的,我看到了计算器的答案,但它并不能帮助我。所以我创造我的问题。

我的情况是为了节省用户具有独特的电子邮件。在DB我有电子邮件xxx@xxx.com用户,我现在的储蓄与相同的电子邮件地址的用户。为了节省我必须使用 entityManager.merge(),因为这个帖子的 thymeleaf绑定集合并不重要。

第一个例子:

  @Controller
公共类EmployeeController扩展AbstractCrudController {    // code其余(...)    @RequestMapping(值= urlFragment +/制造,方法= RequestMethod.POST)
    公共字符串processNewEmployee(型号模型,@ModelAttribute(雇员)用户的雇员,BindingResult结果,HttpServletRequest的请求){
        prepareUserForm(模型);
        如果(!result.hasErrors()){
            尝试{
                saveEmployee(员工);
                model.addAttribute(成功,真正的);
            }赶上(例外五){
                model.addAttribute(错误,真正的);
            }
        }        回归CRUD /员工/创造;
    }    @Transactional
    公共无效saveEmployee(用户员工){
        entityManager.merge(员工);
    }    私人无效prepareUserForm(型号模型){
        HashSet的<地位与GT;位置=新的HashSet<&定位GT;(positionRepository.findByEnabledTrueOrderByNameAsc());
        HashSet的<的角色和GT;角色=新的HashSet<的角色和GT;(roleRepository.findAll());
        用户员工=新用户();        model.addAttribute(员工,员工);
        model.addAttribute(allPositions位置);
        model.addAttribute(allRoles角色);
    }
}

这code抛出TransactionRequiredException,我不知道为什么?它看起来像@Transactional注解没有工作,让我感动注释 processNewEmployee()

第二个例子:

  @Controller
公共类EmployeeController扩展AbstractCrudController {    // code其余(...)    @Transactional
    @RequestMapping(值= urlFragment +/制造,方法= RequestMethod.POST)
    公共字符串processNewEmployee(型号模型,@ModelAttribute(雇员)用户的雇员,BindingResult结果,HttpServletRequest的请求){
        prepareUserForm(模型);
        如果(!result.hasErrors()){            尝试{
                entityManager.merge(员工);
                model.addAttribute(成功,真正的);
            }赶上(例外五){
                model.addAttribute(错误,真正的);
            }
        }        回归CRUD /员工/创造;
    }    私人无效prepareUserForm(型号模型){} /*(.....)*/
}

这code抛出(因为ConstraintViolationException的)的PersistenceException当然我得到了交易标为rollbackOnlyexeption。

当我试图挽救它不存在这个code工作正常邮件,所以我东西@Transactional注解配置好。

如果这是我把我的TransationManagersConfig重要的:

  @Configuration
@EnableTransactionManagement
公共类TransactionManagersConfig实现TransactionManagementConfigurer {    @Autowired
    私人EntityManagerFactory的电动势;    @Autowired
    私人数据源数据源;    @豆
    公众的PlatformTransactionManager transactionManager在(){
        JpaTransactionManager TM =
                新JpaTransactionManager();
        tm.setEntityManagerFactory(EMF);
        tm.setDataSource(数据源);
        返回TM;
    }    公众的PlatformTransactionManager annotationDrivenTransactionManager(){
        返回transactionManager在();
    }
}

你能解释我什么,我做错了,认为这个问题可能的解决方案?

解决方案:

由于现在)来 R4J 我已经创建UserService,在我EmployeeController我使用它,而不是entityManager.merge的(它正常工作

  @Service
公共类UserService {    @PersistenceContext
    私人EntityManager的EntityManager的;    @Transactional
    公共无效合并(用户用户){
        entityManager.merge(用户);
    }
}

和EmployeeController:

  @Controller
公共类EmployeeController扩展AbstractCrudController {    @Autowired
    私人UserService userService;    @RequestMapping(值= urlFragment +/制造,方法= RequestMethod.POST)
    公共字符串processNewEmployee(型号模型,@ModelAttribute(雇员)用户的雇员,BindingResult结果,HttpServletRequest的请求){
         //(.....)
         userService.merge(员工);
         //(.....)
    }}


解决方案

您交易不会因为你直接打电话从公共字符串processNewEmployee工作方法this.saveEmployee(......)。

如何来的?

当您添加@Transactional,春天会为您的组件和代理所有公共方法代理。因此,当Spring本身调用你的方法作为HTTP REST请求则认为去正确通过代理和新交易的要求,并启动外部调用code ++工程。

但是,当你有一个代理的组件,你叫'this.saveEmployee'(有@Transactional注解)类code你实际上是绕过代理春天已经创造了新的交易未启动里面。

解决方案:
提取整个数据库的逻辑某种服务或DAO的,只是它自动装配到你的休息控制器。然后,一切都应该工作就像一个魅力。

您应该避免控制器直接访问数据库呢,因为它不是一个非常好的做法。控制器应尽可能薄,并且不包含业务逻辑,因为它只是一个的方式来访问您的系统。如果你的整个逻辑是在'域',那么你可以添加其他的方式来在code只有几行的事上运行的业务功能(如新用户创建)。

I have read a lot of @Transactional annotation, I saw stackoverflow answers but it does not help me. So I am creating my question.

My case is to save user with unique email. In DB I have user with email xxx@xxx.com, and I am saving user with the same email address. For saving I have to use entityManager.merge() because of this post thymeleaf binding collections it is not important.

First example:

@Controller
public class EmployeeController extends AbstractCrudController {

    // rest of code (...)

    @RequestMapping(value = urlFragment + "/create", method = RequestMethod.POST)
    public String processNewEmployee(Model model, @ModelAttribute("employee") User employee, BindingResult result, HttpServletRequest request) {
        prepareUserForm(model);
        if (!result.hasErrors()) {
            try {
                saveEmployee(employee);
                model.addAttribute("success", true);
            } catch (Exception e) {
                model.addAttribute("error", true);
            }
        }

        return "crud/employee/create";
    }

    @Transactional
    public void saveEmployee(User employee) {
        entityManager.merge(employee);
    }

    private void prepareUserForm(Model model) {
        HashSet<Position> positions = new HashSet<Position>(positionRepository.findByEnabledTrueOrderByNameAsc());
        HashSet<Role> roles = new HashSet<Role>(roleRepository.findAll());
        User employee = new User();

        model.addAttribute("employee", employee);
        model.addAttribute("allPositions", positions);
        model.addAttribute("allRoles", roles);
    }
}

This code is throwing TransactionRequiredException, I do not know why? It looks like @Transactional annotation did not work, so I moved annotation to processNewEmployee()

Second example:

@Controller
public class EmployeeController extends AbstractCrudController {

    // rest of code (...)

    @Transactional
    @RequestMapping(value = urlFragment + "/create", method = RequestMethod.POST)
    public String processNewEmployee(Model model, @ModelAttribute("employee") User employee, BindingResult result, HttpServletRequest request) {
        prepareUserForm(model);
        if (!result.hasErrors()) {

            try {
                entityManager.merge(employee);
                model.addAttribute("success", true);
            } catch (Exception e) {
                model.addAttribute("error", true);
            }
        }

        return "crud/employee/create";
    }

    private void prepareUserForm(Model model) { /*(.....)*/ }
}

And this code is throwing PersistenceException (because of ConstraintViolationException) and of course I got "Transaction marked as rollbackOnly" exeption.

When I try to save email which not exists this code works fine, so I thing that @Transactional annotation is configured well.

If this is important I am putting my TransationManagersConfig:

@Configuration
@EnableTransactionManagement
public class TransactionManagersConfig implements TransactionManagementConfigurer {

    @Autowired
    private EntityManagerFactory emf;

    @Autowired
    private DataSource dataSource;

    @Bean
    public PlatformTransactionManager transactionManager() {
        JpaTransactionManager tm =
                new JpaTransactionManager();
        tm.setEntityManagerFactory(emf);
        tm.setDataSource(dataSource);
        return tm;
    }

    public PlatformTransactionManager annotationDrivenTransactionManager() {
        return transactionManager();
    }
}

Could you explain my what I am doing wrong and suggest possible solution of this problem?

Solution:

Thanks to R4J I have created UserService and in my EmployeeController I am using it instead of entityManager.merge() now it works fine

@Service
public class UserService {

    @PersistenceContext
    private EntityManager entityManager;

    @Transactional
    public void merge(User user) {
        entityManager.merge(user);
    }
}

And EmployeeController:

@Controller
public class EmployeeController extends AbstractCrudController {

    @Autowired
    private UserService userService;

    @RequestMapping(value = urlFragment + "/create", method = RequestMethod.POST)
    public String processNewEmployee(Model model, @ModelAttribute("employee") User employee, BindingResult result, HttpServletRequest request) {
         // (.....)
         userService.merge(employee);
         // (.....)
    }

}

解决方案

Your transactions don't work because you call directly 'this.saveEmployee(...)' from your 'public String processNewEmployee' method.

How come?

When you add @Transactional, Spring creates a Proxy for your Component and proxies all public methods. So when Spring itself calls your method as a HTTP Rest Request it is considered an external call that goes properly through a Proxy and new Transaction is started as required and code works.

But when you have a Proxied Component and you call 'this.saveEmployee' (that has @Transactional annotation) inside your class code you are actually bypassing the Proxy Spring has created and new Transaction is not started.

Solution: Extract entire database logic to some sort of a Service or DAO and just Autowire it to your Rest Controller. Then everything should work like a charm.

You should avoid direct database access from Controllers anyway as it is not a very good practice. Controller should be as thin as possible and contain no business logic because it is just a 'way to access' your system. If your entire logic is in the 'domain' then you can add other ways to run business functionalities (like new user creation) in a matter of just few lines of code.

这篇关于春季@事务TransactionRequiredException或RollbackException的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆