I am using a Transactional method using @Transactional
annotation. I am trying to save an object to the database using JpaRepository's save() method. (which will throw an error due to constraints on database relation.)
Now, when I debugged the program, I found out that an exception is thrown at the end of transactional method not when save
method is called. This is quite different behaviour from that of a non-transactional method.
Can someone explain why is that so? Why is exception thrown at the end of transactional method and not when actually it should occur.
My second question is, when the method is Transactional, exception thrown is DataViolationException
and when it is non-transactional, exception thrown is PSQLException
(using Postgres database). Why is that so?
Below is the code
@Transactional
public ResponseType methodA(UserObject userObject) {
//save call
jpaRepoObject.save(userObject);
//next call will fail due to relational constraints on database
jpaRepoObject.save(userObject); //should throw PSQLException/DataViolationException
return new ResponseType("success"); //Error thrown after this line.
}
This is a feature of JPA called transactional write-behind. All the inserts and updates your code is executing get stored by the Jpa implementation until the transaction is flushed. This lets the jpa implementation reorder these actions to take place in whatever order makes the most sense.
Flushing means executing all the actions that have been stored up so far by the entityManager instance. You can tell the entityManager to flush or it will automatically flush once the transaction boundary is reached.
An example of when you would flush explicitly would be if you are inserting an entity and you need it to have an ID generated for it so you can use it later in the same method.
You can always run sql within a transaction and commit or rollback later. Flushing runs the sql but doesn’t commit.