如何在带有@Transactional注解的Spring方法中读取已保存的实例

huangapple go评论74阅读模式
英文:

How to read saved instance in spring method with @Transactional

问题

我在我的Spring Boot应用程序的控制器方法中使用@Transactional注解来实现回滚目的,这样只有在一切都没有错误发生时,我才会添加这个注解:

@PostMapping(value = "/add/people")
@Transactional
public ResponseEntity<?> addGroundToPerson(@RequestParam("ground") String newGround, @RequestParam("person") String person) {
    try {
        personService.addPerson(person, ground);
        Ground addedGround = groundService.addGround(newGround);
    } catch (NoSuchElementException e) {
        return new ResponseEntity<>(new CustomErrorType(e.getMessage()), HttpStatus.NOT_IMPLEMENTED);
    }
}

addPersonToGround在数据库中保存该人员及其地址。

addGround将新场地与人员引用一起保存在数据库中,而场地本身也有一个地址。

我的失败的Service方法:

public void registerAddress(HasAddress hasAddress) throws NoSuchElementException {

    Address address = hasAddress.getAddress();
    State state = address.getState();

    // 这里是我得到错误的地方
    State dbState = searchState(state); // stateRepository.getStateByName();

    address.setState(state);
    hasAddress.setAddress(address);

    addressRepository.save(address);
}

registerAddress被调用了两次,第一次正常工作,但因为我已经在第一次保存了,所以第二次不起作用(请记住,我的代码比这个例子要复杂得多,我不能只是去掉保存,我已经尝试过,相信我)。

我尝试在服务层中放置@Transactional,但是它会发送给我这个错误:

org.hibernate.TransientPropertyValueException:对象引用了未保存的瞬态实例

当我只保存对象时,它正常工作,但当我在保存后需要获取一个对象时,它不正常工作。这就是我的问题,我有一个最好的例子:

  1. 每个人都有一个地址,地址中包含了州的信息。
  2. 如果州相同,我希望每个地址都可以重用该州。
  3. 我注册一个人员,它会注册一个新的地址(有时使用数据库中已存在的州)。
  4. 当我尝试注册场地并尝试获取该州以重用它时,我会得到一个错误:

嵌套异常是org.hibernate.TransactionException:事务标记为仅回滚;无法提交

我认为这是因为第一次“保存”对象时它不会提交,因此它保持瞬态状态,而我无法获取瞬态对象。我正在努力在逻辑结束时保存所有的逻辑,并在出现异常时回滚,但由于注解的原因,我得到了那个错误。

英文:

I'm using @Transactional annotation for the rollback purpose, I've added in my controller methods in my Spring-Boot application, so my saves will only happen when everything happened without any error, this is how I've added the annotation:

    @PostMapping(value = &quot;/add/people&quot;)
    @Transactional
    public ResponseEntity&lt;?&gt; addGroundToPerson(@RequestParam(&quot;ground&quot;) String newGround, @RequestParam(&quot;person&quot;) String person {
        try {
             personService.addPerson(person, ground);
             Ground addedGround = groundService.addGround(pnewGround);
        } catch (NoSuchElementException e) {
            return new ResponseEntity&lt;&gt;(new CustomErrorType(e.getMessage()), HttpStatus.NOT_IMPLEMENTED);
    }

addPersonToGround is saving that person in database and with his address.

addGround is saving the new ground in database with that person reference, and the ground itself has a address.

My Service method that is failing:

public void registerAddress(HasAddress hasAddress) throws NoSuchElementException {

        Address address = hasAddress.getAddress());
        State state = address.getState();

        //here&#39;s where I&#39;m getting the error
        State dbState = searchState(state); //stateRepository.getStateByName();

        address.setState(state);
        hasAddress.setAddress(address);

        addressRepository.save(address);
    }

RegisterAddress is being called two times, the first one works fine, but because I've saved at the first time, the second one doesn't work. (keep in mind that my code is way bigger than this one and I can't just take the save off, and I've tried, trust me).

I've tried putting the @Transactional in the service layer but it sends me this error:
> org.hibernate.TransientPropertyValueException: object references an
> unsaved transient instance

It's working fine when I just save objects, but it doesn't work fine when I have to get a object after saving it. And that's my problem, The best example I have is this:

  1. I have a address in each person, and it has the state in it.
  2. I want to reuse the state for each address if it's the same.
  3. I register a person and it register a new address (somethimes using states that already exists in the database).
  4. I try to register the ground and when I try to get that state to reuse it I get a error:
    > nested exception is org.hibernate.TransactionException: Transaction
    > was marked for rollback only; cannot commit

I think that's happening because when I "save" the object for the first time it doesn't commit, so it stays transient, and I just can't get a transient object. I'm trying to make all the logic and save at the end of it, or rollback if it occours some exception, but I'm getting that error because of the annotation.

答案1

得分: 2

有很多问题。让我从一个事实开始,你把 @Transactional 放在了控制器层而不是服务层。方法的逻辑也应在服务层中。这个 rollbackFor = Exception.class 实际上是不必要的,因为Spring默认在所有标有 @Transactional 的方法上加上了 rollbackFor = { RuntimeException.class, Error.class }。现在你碰到这个异常的原因是因为你在其中放置了一个 try/catch 块。Spring的事务在抛出异常时会被标记为回滚,无论是否捕获异常。你要么删除 try/catch,要么找一种方法将其放在事务之外。

英文:

So many things wrong with this. Let me start off with the fact you've put @Transactional in the Controller layer instead of the Service layer. Your method's logic should also be in the Service layer. This - rollbackFor = Exception.class isn't really necessary because Spring puts rollbackFor = { RuntimeException.class, Error.class } by default on all methods annotated with @Transactional. And now the reason you're getting the mentioned exception is because you've put a try/catch block in there. Spring's transactions are marked as rollback only when an exception is thrown regardless if it was caught. You have to either remove the try/catch or find a way to do it outside of the transaction.

huangapple
  • 本文由 发表于 2020年3月4日 03:09:42
  • 转载请务必保留本文链接:https://go.coder-hub.com/60514027.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定