简体   繁体   English

Spring @Transactional TransactionRequiredException或RollbackException

[英]Spring @Transactional TransactionRequiredException or RollbackException

I have read a lot of @Transactional annotation, I saw stackoverflow answers but it does not help me. 我已经阅读了很多@Transactional批注,我看到了stackoverflow的答案,但对我没有帮助。 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. 在数据库中,我有一个使用电子邮件xxx@xxx.com的用户,并且正在使用相同的电子邮件地址保存该用户。 For saving I have to use entityManager.merge() because of this post thymeleaf binding collections it is not important. 为了保存,我必须使用entityManager.merge()因为此后胸腺叶绑定集合不重要。

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? 这段代码抛出TransactionRequiredException,不知道为什么? It looks like @Transactional annotation did not work, so I moved annotation to processNewEmployee() 看来@Transactional注释无效,因此我将注释移至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. 并且此代码引发PersistenceException(由于ConstraintViolationException),当然我得到了“交易标记为rollbackOnly”示例。

When I try to save email which not exists this code works fine, so I thing that @Transactional annotation is configured well. 当我尝试保存不存在的电子邮件时,此代码可以正常工作,因此我认为@Transactional批注配置得很好。

If this is important I am putting my TransationManagersConfig: 如果这很重要,我要放置我的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 多亏了R4J,我创建了UserService,并在EmployeeController中使用它代替了EntityManager.merge(),现在可以正常使用了

@Service
public class UserService {

    @PersistenceContext
    private EntityManager entityManager;

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

And EmployeeController: 和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. 您的交易无效,因为您直接从“公共字符串processNewEmployee”方法中调用“ this.saveEmployee(...)”。

How come? 怎么会?

When you add @Transactional, Spring creates a Proxy for your Component and proxies all public methods. 当添加@Transactional时,Spring为您的组件创建一个代理,并代理所有公共方法。 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. 因此,当Spring本身将您的方法作为HTTP Rest Request调用时,它被认为是通过代理正确进行的外部调用,并且根据需要启动了新的Transaction,并且代码可以正常工作。

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. 但是,当您具有代理组件并在类代码中调用“ this.saveEmployee”(具有@Transactional批注)时,实际上实际上是在绕过已创建的代理Spring,并且不会启动新的Transaction。

Solution: Extract entire database logic to some sort of a Service or DAO and just Autowire it to your Rest Controller. 解决方案:将整个数据库逻辑提取到某种服务或DAO中,然后将其自动连接到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. 无论如何,您都应避免从Controllers直接访问数据库,因为这不是很好的做法。 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. 如果您的整个逻辑都在“域”中,那么您只需几行代码就可以添加其他方式来运行业务功能(例如创建新用户)。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 Hibernate Spring JPA javax.persistence.TransactionRequiredException:没有可用的事务性EntityManager - Hibernate Spring JPA javax.persistence.TransactionRequiredException: No transactional EntityManager available TransactionRequiredException:@Transactional方法中没有可用的事务EntityManager - TransactionRequiredException: No transactional EntityManager available within @Transactional method Spring 引导:javax.persistence.TransactionRequiredException:使用 @Transactional 注释时执行更新/删除查询 - Spring Boot : javax.persistence.TransactionRequiredException: Executing an update/delete query When Using @Transactional Annotation Spring webmvc框架中的TransactionRequiredException - TransactionRequiredException in spring webmvc framework Spring TransactionRequiredException异常 - Spring TransactionRequiredException exception Glassfish 4-使用Hibernate和CDI @Transactional的TransactionRequiredException - Glassfish 4 - TransactionRequiredException using Hibernate and CDI @Transactional 为什么在缺少@Transactional时没有抛出TransactionRequiredException? - Why is no TransactionRequiredException thrown when @Transactional is missing? 在Spring服务中持久存在会导致TransactionRequiredException - Persisting inside the Spring service results into TransactionRequiredException JPA + Spring Boot:CRUD操作上的TransactionRequiredException - JPA + Spring Boot: TransactionRequiredException on CRUD operations TransactionRequiredException:Spring Data JPA正在进行任何事务 - TransactionRequiredException: no transaction is in progress with Spring Data JPA
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM