0

I have a problem with a transactional method that changes an entity and wants to update it in some other way.

At first i get the entity A from the database with the entitymanager method "get". Then i get a related entity B where A to B is type of one to one (optional). (So the id field of B is inside the table of A). Now i want to remove the entity B via a service method. Therefore i have to use the ID of B.

Inside the service method i get B from the entity manager (now B'). Then i get A' from the aquired B'. Then i remove the link A' to B' when it is present via A'.setB(null) followed by a serviceOfA.save(A').

Then i delete B' via serviceOfB.delete(B').

When the method for removal of B via the id is completed i want to change properties of the instance A. Create another instance of B for example. Now when i get A via the entitymanager again hibernate throws a org.hibernate.exception.ConstraintViolationException for an object that should be added to the new B'' instance that is added to A.

I think the issue has something to do with the removal method that changed the instance A' and therefore A can not be reloaded. But how can i reload the new state of A? Please have a look below:

@Transactional
    public void compeleteSomething(
            @NonNull String location,
            @NonNull String nameOfA) throws SomeException{
        A a= serviceOfA.get(nameOfA);

        B b= a.getB();
        someService.removeBAndLinkToA(b.getId()); // <-- maybe here is the error

        B newInstanceOfB = someService.createBOn(location);
        someService.setBonA(serviceOfA.get(nameOfA), newInstanceOfB); // <-- serviceOfA.get() throws error

        [...]
    }

And here the method of someService.removeBAndLinkToA(#)

@Transactional
    public void removeBAndLinkToA(
            @NonNull Long id) {
        B b = serviceOfB.get(id);

        A a = b.getA();
        if (a!= null) {
            a.setB(null);
            serviceOfA.save(a); // <-- This should cause the error?
        }

        serviceOfB.delete(b);
    }

How can i avoid this issue? Many thanks!

Noixes
  • 1,158
  • 12
  • 25

1 Answers1

1

When working inside a transaction, entitymanager is expected to deal with all relashionships until commit, provided it is injected with proper scope. You do not need to save the entity at each step nor retrieve it again from database: its state is managed by the entitymanager (no pun intended).

In short, your completeSomething method does not need to call another service method. Just make b.setA(null), a.setB(new B()) and return. Everything should work as expected:

@Transactional
public void completeSomething(String aId) {
    A a = entityManager.find(A.class, aId);
    B b = a.getB();
    a.setB(new B());
    b.setA(null);
} // container should end transaction here, commiting changes to entities

If the transaction is successful, the container will commit changes to entities and the changes will be reflected on database as long the @PersistenceContext has PersistenceContextType.TRANSACTION type, which is the default.

Paulo Araújo
  • 539
  • 3
  • 12
  • Thank you for that answer. Unfortunately i have to use the method of the someService-Class - Since i do not want to repeat that piece of code so many times. Is that an issue? – Noixes Apr 28 '20 at 05:44
  • No, it is not a problem, as long as all belongs to the same transaction and the references from the entitymanager are the same. If you do any change using entitymanager.merge, be sure to get the returned reference to make any new change. – Paulo Araújo Apr 28 '20 at 11:32
  • What can i do if i can not return the changed entity? – Noixes Apr 28 '20 at 11:42
  • You can use entityManager [refresh](https://javaee.github.io/javaee-spec/javadocs/javax/persistence/EntityManager.html#refresh-java.lang.Object-) method. It should pick the most recent state of the entity. – Paulo Araújo Apr 29 '20 at 19:53
  • So even if i save the entity in the same transaction but with different object reference the refresh method will sync them correctly? Isnt the state dirty or something like that? – Noixes Apr 30 '20 at 05:34
  • If you did flush changes prior to the refresh method (I suppose so, from your service), the method is supposed to retrieve the current state at the database. – Paulo Araújo Apr 30 '20 at 15:34