Spring Transaction on server shutdown - spring

Spring #Transaction does not rollback if the server shuts down in the middle of a transactional method. But that violates the atomicity principle of transactions. Why is that?
That is, if my code looks like this:
#Transactional
public void method() {
write();
// server shuts down
write();
}
The first write is not rolled back.

Related

How to declare `#Transactional` in a Spring Boot service so exception is thrown "immediately"?

Say I have the following spring beans in a spring boot app. My intent is to make createFoo() transactional so:
When barService.bar() throws exceptions, the persist gets rolled back.
When the persist throws exception, the exception propagates up to the caller "immediately" so barService.bar() is NOT called.
#Service
public class FooService
{
#Autowired
private FooRepository fooRepository;
#Autowired
private BarService barService;
#Transactional
public void createFoo(Foo foo) {
fooRepository.save(foo);
// expect to not execute when the above line throws exceptions
barService.bar();
}
}
#Service
public class BarService {
public void bar() {
}
}
So far the 1st requirement works, however the 2nd doesn't. When the persist throws exception, barService.bar() is AlWAYS called.
If I remove #Transactional, 2nd requirement works, the 1st doesn't.
I also tried all Propagation types, none of them work as I expected. For example, if I use #Transactional(MANDATORY), I get the following error:
org.springframework.transaction.IllegalTransactionStateException: No existing transaction found for transaction marked with propagation 'mandatory'
Without #Transactional, each call to a repo method is a standalone transaction and it will be flushed immediately. That's why your 2nd requirement worked without #Transactional.
When you add #Transactional, the whole createFoo() becomes one single unit of transaction. Hence, the changes you make when calling save() will only be flushed when createFoo() completes its execution. That's why your 1st requirement worked with #Transactional.
To achieve what you want, keep #Transactional and call saveAndFlush() instead of save().

Whether and When Spring Lock Table if Method Annotated with Transactional

This question comes from that, if I have a methodA, and methodB in classA like below
public class classA {
public void methodA() {
// some code
...
// time to deal with db
methodB();
}
#Transactional
public void methodB() {
// insert a record
throw new RuntimeException("testing");
}
}
In classB Directly call methodA, there will be no transactional effect on methodB(this is to say spring will not rollback the insert operation even RuntimeException occurred).
When I move the #Transactional from methodB to methodA, and call methodA again, the #Transactional annotation works.
However, if I have really a lot of work to do in methodA, will spring lock the table during the total execution time?
Well, I will add a new class(i.e. classC) and move methodB(annotated with Transactional) to the new class as workaround.
It is the limitation of Springs AOP that #Transactional will not have effect if the method is called within the same class.
Refer to #Transactional method called from another method doesn't obtain a transaction
Spring doesn't handle transaction by itself. #Transactional annotation lets Spring's proxy class to inject transaction begin and commit/rollback methods around your original method.
Proxy wrapper will only be called if you invoke annotated method from outside the classes. Local method call will not use proxy class. So, call from method A to method B will not use transactional features.
Secondly, Spring will simply delegate the transaction handling to underlying database driver. Whether your table acquires a lock or not will be determined by your driver documentation and (atomicity and isolation level) settings.

Spring managed transactions #Transactional annotation

Propagation setting is REQUIRED.
#Transactional(propagation = Propagation.REQUIRED)
Transaction is read/write.
In which scenario those are used? Please give me explain with example
Spring transaction default is
#Transactional(propagation = Propagation.REQUIRED)
So you do not need to specify the propagation property.
So, What does it mean by #Transactional annotation for a spring component ?
Spring framework will start a new transaction and executes all the method and finally commit the transaction.
But If no transaction is exists in the application context then spring container will start a new transaction.
If more than one method configured as Propagation.REQUIRED then transactional behavior assigned in a nested way to each method in logically but they are all under the same physical transaction.
So, What is the result ?
The result is if any nested transaction fail, then the whole transaction will fail and rolled back (do not insert any value in db) instead of commit.
Example:
#Service
public class ServiceA{
#Transactional(propagation = Propagation.REQUIRED)
public void foo(){
fooB();
}
#Transactional(propagation = Propagation.REQUIRED)
public void fooB(){
//some operation
}
}
Explanation :
In this example foo() method assigned a transactional behavior and inside foo() another method fooB() called which is also transactional.
Here the fooB() act as nested transaction in terms of foo(). If fooB() fails for any reason then foo() also failed to commit. Rather it roll back.
This annotation is just to help the Spring framework to manage your database transaction.
Lets say you have a service bean that writes to your database and you want to make sure that the writing is done within a transaction then you use
#Transactional(propagation = Propagation.REQUIRED)
Here is a small example of a Spring service bean.
#Service
class MyService {
#Transactional(propagation = Propagation.REQUIRED)
public void writeStuff() {
// write something to your database
}
}
The Transactional annotation tells Spring that:
This service method requires to be executed within a transaction.
If an exception gets thrown while executing the service method, Spring will rollback the transaction and no data is written to the database.

Spring transaction: requires_new beharivour

May be I am misunderstand Spring Requires_new behavior. Here is my code:
#Transactional(rollbackFor=Exception.class,propagation=Propagation.REQUIRED)
public void outterMethod() throws Exception{
innerMethod1();
innerMethod2();
}
#Transactional(rollbackFor=Exception.class,propagation=Propagation.REQUIRES_NEW)
public void innerMethod1() throws Exception{
testService.insert(new Testbo("test-2", new Date()));
}
#Transactional(rollbackFor=Exception.class,propagation=Propagation.REQUIRES_NEW)
public void innerMethod2() throws Exception{
testService.insert(new Testbo("test-2", new Date()));
throw new Exception();
}
When the innnerMethod2 throws Exception, I thought that innerMethod1 still able to commit. But the all the outer and inner transactions rollback. What am I wrong here? How can I do to commit innerMethod1 when innerMethod2 rollback?
Although you have correctly understood the behavior of Propagation.REQUIRES_NEW, you have stumbled upon a common misconception about Spring 's Transactional behavior.
In order for the transactional semantics to be applied (that is for the annotation of the method to have any effect), the method needs to be called from outside the class. Calling a method annotated with transactional from inside the class has absolutely no effect on the transactional processing (because the Spring generated proxy class that contains the transactional code does not come into play).
In your example innerMethod2 maybe annotated with #Transactional but since it is called from outterMethod, the annotation is not being processed.
Check out this part of the Spring documentation

How to test hibernate lifecycle observers?

I'm having problems whilst trying to test a JPA entity lifecycle observer, registered as POST_COMMIT_INSERT
Because my observer is a spring bean, I wire it into the entity manager as follows:
private void configureHibernateHooks(HistoricEventListener<?> listener) {
SessionFactory sessionFactory = getSessionFactory();
EventListenerRegistry registry = ((SessionFactoryImpl) sessionFactory).getServiceRegistry().getService(
EventListenerRegistry.class);
if (listener instanceof PostInsertEventListener)
{
registry.getEventListenerGroup(EventType.POST_COMMIT_INSERT).appendListener((PostInsertEventListener) listener);
}
}
SessionFactory getSessionFactory() {
Session session = (Session) entityManager.getDelegate();
SessionFactory sessionFactory = session.getSessionFactory();
return sessionFactory;
}
My test is annotated as #Transactional. Therefore, as I understand it, each test runs within it's own transaction. This is preventing my observer method from being invoked, as the transaction doesn't commit during my test, so the POST_COMMIT_XXX listeners are never observed.
I've tried injecting the EntityManager and manually commiting transactions, but that resulted in exceptions.
What's an appropriate strategy for testing these?
You can use the #Rollback(false) annotation on a test method to direct spring to commit the transaction instead of rolling it back.
Even triggering an entityManger.flush() should suffice. I don't think the event triggering is associated with the commit of the transaction (the name might be misleading).

Resources