春季@事务TransactionRequiredException或RollbackException [英] Spring @Transactional TransactionRequiredException or 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屋!