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.
Related
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().
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.
When a service class has no implementation of Jpa repository ,My understanding is that it is wrong to annotate it with Transactional, for example this service class with should not have #Transactional
#Service
public class BillingAddressServiceImpl implements BillingAddressService {
public BillingAddress setByUserBilling(UserBilling userBilling, BillingAddress billingAddress) throws DataAccessException
{
billingAddress.setBillingAddressName(userBilling.getUserBillingName());
billingAddress.setBillingAddressStreet1(userBilling.getUserBillingStreet1());
billingAddress.setBillingAddressStreet2(userBilling.getUserBillingStreet2());
billingAddress.setBillingAddressCity(userBilling.getUserBillingCity());
billingAddress.setBillingAddressState(userBilling.getUserBillingState());
billingAddress.setBillingAddressCountry(userBilling.getUserBillingCountry());
billingAddress.setBillingAddressZipCode(userBilling.getUserBillingZipCode());
return billingAddress;
}
}
The Transactional annotation stands for declaring the database transaction handling of Spring bean methods.
Since your service method is not working with the database it makes no sense to declare a transaction handling.
Transactional annotations are only used with method or classes that makes calls to database,one of the uses of transactional annotation is rollback - which means when we make call to a database there would be a recording of the intermidiate state between the successful call and its original state, this is so that in the case of a failed call the system does not crash but is able to return to its original state
I've got situation like this:
#Transactional
#Override
public void register(String username, UserPasswordNew userPasswordNew, UserAccount userAccount) throws UserNameAlreadyExistsException {
.....
entityManager.merge(userAccountToSave);
}
I made some research but check me if I understand well. I've got entityManager (transaction scope). Method register is #Transactional so it means that this method is wrapped in proxy. When persistence context is created ? During the first call of entityManager.merge () ?? Transaction is commit after method because it's wrapped in proxy. So persistence context is removed after commit ?
Correct me if I am wrong, but you are using transaction scoped entitymanager, so during each call to entitymanager it ensure that persistence context exists, here entitymanager creates a new one and uses it to merge - and,as in transaction-scoped entitymanager, persistence context will be removed after each commit.
I read the Spring doc and it says:
The #PersistenceContext annotation has an optional attribute type, which defaults to
PersistenceContextType.TRANSACTION. This default is what you need to receive a shared
EntityManager proxy.
Does this mean I have to make EntityManager work in a transaction?
How does it work for non-transactional method (reading query), such as the loadProductsByCategory in the below code?
What does the "shared" mean? How can it use EntityManager sharing with others?
Do I need to add #Transactional to the method loadProductsByCategory in order to bind the EntityManager to the thread? Because the class ProductDaoImpl is singleton and works in multi-thread, but entityManager is not thread-safe.
#Service
public class ProductDaoImpl implements ProductDao {
#PersistenceContext
private EntityManager em;
public Collection loadProductsByCategory(String category) {
Query query = em.createQuery("from Product as p where p.category = :category");
query.setParameter("category", category);
return query.getResultList();
}
#Transactional
public void loadProductsByCategory(Product product) {
em.persist(product);
}
}
There is a detailed analysis of this behavior at the following blog link. http://doanduyhai.wordpress.com/2011/11/21/spring-persistencecontext-explained/ Here is my summary of it.
EntityManager is a java interface which allows spring to provide it's own implementation of the interface. The implementation injected by spring use a dynamic proxy to handle the calls to the entity manager. The dynamic proxy behaves the following way.
if there is no #Transactional annotation as in loadProductsByCategory spring will create an instance of the EntityManager em when em.createQuery is called, spring will not return the Query object created by JPA but it will return Spring Proxy of EntityManager this spring proxy forwards all calls to the real implementation of Query and it waits until the getResult or getSingleResult or executeUpdate are called and it immediately closes the Entity Manager.
So when there is no #Transactional Spring will make sure that the entity manager is closed as soon as possible, i.e. after each method call on the entity manager or after a result set is extracted. In your above example if you comment out the query.getResultList() you will end up leaking an entity manager instance that does not get closed
public Collection loadProductsByCategory(String category) {
Query query = em.createQuery("from Product as p where p.category = :category");
query.setParameter("category", category);
return null;
// a leak of an entity manager will happen because getResultList() was never called, so
// spring had no chance to close the entity manager it created when em.creaueQuery was
// invoked.
// return query.getResultList();
}
When there is #Transactional attribute the spring transaction manager will create a transactional context before the transactional method is called. When transactional method calls any method on the entity manager Spring will create a brand new EntityManager instance and associate it with current transnational, if the transactional method, calls another method, which calls another and all those methods use an entity manager then the entity manager is shared across all those calls. here is an example.
main(..)
{
Foo foo = call spring to get foo instance
foo.doFoo();
}
public class Foo {
#PersistenceContext
EntityManager em;
#Autowired
Bar bar;
#Transactional
public doFoo(){
// before this method is called spring starts a spring transaction
em.createQuery(....) // here spring will create an instance of the Entity manager
// and assoicated with the current tx
bar.doBar(); // call bar transactional method
}
}
public calss Bar {
#PersistenceContext
EntityManager em;
#Transactional
public doBar(){
// no tx is started here because one was already started in doFoo
em.createQuery(....) // spring looks under the current tx and finds that it has
// an entity manager, was created in the doFoo() method so this entity manager
// is used, This is what is meant by sharing of the entity manager.
}
}
To answer your last last question.
Do I need to add #Transactional to the method loadProductsByCategory in order to bind the EntityManager to the thread? Because the class ProductDaoImpl is singleton and works in multi-thread, but entityManager is not thread-safe.
The #Transactional causes spring to bind a spring tx to the current thread, and then the entity manager is bound to the spring tx which is bound via thread local to the current thread.
Pro JPA 2 book has a decent explanation of this stuff in chapter 6, it is bit dense and it is explained in the context of Java EE but the steps are the same for spring.