With container-managed JPA persistence, container-managed JTA transactions, and an entity manager injected into stateless local session beans, what does invoking a transaction-not-supported method do to the managed state of an entity? I have read this: https://community.jboss.org/thread/183007 and similar threads, but there seems to be some ambiguity around whether a PC gets propagated to a NOT_SUPPORTED method if they both use injected EMs from the same factory?
Consider this snippet from a a bean that is going to retrieve an item and then invoke a method from another bean:
#PersistenceContext
private EntityManager em;
#Inject
private LeanBean leanBean;
#TransactionAttribute(TransactionAttributeType.REQUIRED)
public void startHere() {
MyItem item = em.find(MyItem.class, key);
leanBean.txMethod(item);
leanBean.nonTxMethod(item);
}
Now here is LeanBean.java. Note that its two methods have different transaction propagation
#Stateless
#LocalBean
public Class LeanBean {
#PersistenceContext
private EntityManager em;
#TransactionAttribute(TransactionAttributeType.REQUIRED)
public void txMethod(MyItem item) {
doSomething(item); // item is managed; persistence context propagated with
} // transaction context; em is the same as my caller's em
#TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
public void nonTxMethod(MyItem item) {
doSomething(item); // caller's transaction context has been suspsended.
} // did em propagate? is item managed?
}
What I can't figure out from the spec(s):
Inside nonTxMethod, is item in a detached state? What is supposed to happen if I reference em?
What if the method in the calling snippet had been annotated TransactionAttributeType.NOT_SUPPORTED? would would be the state of item in either of the methods inside LeanBean?
What I'm trying to accomplish here is to keep an entity attached inside a method that I do not want to have as part of a transaction.
Inside nonTxMethod, is item in a detached state? What is supposed to
happen if I reference em?
TransactionAttributeType.NOT_SUPPORTED is just a way to tell that your method does not need transaction and you will not be able call persist, merge or remove in EntityManager. But you can still call other methods like em.find(Someclass.class,objectId) . So i think the entity is not in a detached state .
What if the method in the calling snippet had been annotated
TransactionAttributeType.NOT_SUPPORTED? would would be the state of
item in either of the methods inside LeanBean?
This will work exactly the same as previous. Just make sure that , whenever you will put TransactionAttributeType.NOT_SUPPORTED in your calling method eg startHere(), all the subsequent method calls transaction state would be suspended , and if you need trasaction in any of the subsequent calls you have to explicitely specify TransactionAttributeType.REQUIRED above that method .
And by default when you call any session bean's method from outside of the EJB , let from the JSP , the trasaction will automatically started unless you specify explicitly that you don't need transaction.
Related
I'm learning about how to create REST API with JPA and Hibernate and a MySQL database and I see this #Transactional annotation. Can someone explain what is the use of this annotation?
For example I have this simple DAO class:
#Repository
public class EmployeeDAOHibernateImpl implements EmployeeDAO {
// define field for entitymanager
private EntityManager entityManager;
// set up constructor injection
#Autowired
public EmployeeDAOHibernateImpl(EntityManager entityManager) {
this.entityManager = entityManager;
}
#Override
#Transactional
public List<Employee> findAll() {
// get the current hibernate session
Session currentSession = entityManager.unwrap(Session.class);
// create a query
Query<Employee> theQuery =
currentSession.createQuery("from Employee", Employee.class);
// execute query and get result list
List<Employee> employees = theQuery.getResultList();
// return the results
return employees;
}
}
You can see the #Transactional used for findAll() method, but if I delete this #Transactional I get the same output... then what is the use of this #Transactional?
#Transactional annotation is used when you want the certain method/class(=all methods inside) to be executed in a transaction.
Let's assume user A wants to transfer 100$ to user B. What happens is:
We decrease A's account by 100$
We add 100$ to B's account
Let's assume the exception is thrown after succeeding 1) and before executing 2). Now we would have some kind of inconsistency because A lost 100$ while B got nothing.
Transactions means all or nothing. If there is an exception thrown somewhere in the method, changes are not persisted in the database. Something called rollback happens.
If you don't specify #Transactional, each DB call will be in a different transaction.
Generally the #Transactional annotation is written at the service level.
It is used to combine more than one writes on a database as a single atomic operation.
When somebody call the method annotated with #Transactional all or none of the writes on the database is executed.
In the case of read operations it is not useful and so it is in case of a single atomic write. You are using it in a single read (select) so adding or removing the #Transactional annotation has no impact.
The class declares #Transactional on itself or its members, Spring creates a proxy that implements the same interface(s) as the class you’re annotating. In other words, Spring wraps the bean in the proxy and the bean itself has no knowledge of it.
A proxy provides a way for Spring to inject behaviors before, after, or around method calls into the object being proxied.
Internally, its the same as using a transaction advice (using AOP), where a proxy is created first and is invoked before/after the target bean’s method.
The generated proxy object is supplied with a TransactionInterceptor, which is created by Spring. So when the #Transactional method is called from client code, the TransactionInterceptor gets invoked first from the proxy object, which begins the transaction and eventually invokes the method on the target bean. When the invocation finishes, the TransactionInterceptor commits/rolls back the transaction accordingly
Suppose I have a Singleton bean with an EntityManager in it. The singleton also specifies (on method or class level) a transaction attribute REQUIRED. The entity manager is obtained via an #PersistenceContext injection which specifies persistence context type TRANSACTION. For all intents and purposes, if a method on this singleton is invoked with an existing transaction, the entity manager should join the transaction or possibly provide an already existing one linked to that transaction via proxy. If such a method is invoked outside of a transaction a new transaction will be started for the duration of the method invocation.
Now suppose we have a second bean which uses bean-managed transactions and injects the singleton. If it explicitly starts a user transaction and then invokes a method on the singleton, will the entity manager in that method join that user transaction? Will the jump from bean-managed to container-managed transaction context even work? I know the other way around doesn't and forms a barrier.
The singleton class:
#Singleton
#TransactionAttribute(TransactionAttributeType.REQUIRED)
public class PersistenceSingleton {
#PersistenceContext(unitName = "test", type = PersistenceContextType.TRANSACTION)
private EntityManager em;
public void doStuff() {
// perform actions with the entity manager that imply changes in the database
}
}
The bean with user transactions (might as well be stateless or stateful):
#Singleton
#TransactionManagement(TransactionManagementType.BEAN)
public class PersistenceFacade {
#EJB
private PeristenceSingleton ps;
#Resource
private UserTransaction userTx;
public void doStuff() {
userTx.begin();
ps.doStuff();
userTx.commit();
}
}
Does the transaction started in method doStuff() of the PersistenceFacade get taken into account when invoking doStuff() on the PersistenceSingleton? Does the entity manager automatically join the transaction and behave as expected from transaction isolation during concurrent access?
UserTransaction is used for changing the default transaction demarcation but we still control the JTA transactions.
https://www.javacodegeeks.com/2013/03/types-of-entity-managers-application-managed-entitymanager.html says:
If you have UserTransaction you can start demarcating what is to be executed within the transaction. Notice that you’re still controlling
the JTA transactions
so The persistence context propagation rule will be applied for UserTransaction demarcation.
pro JPA book says:
When a method is invoked on the transaction-scoped entity manager, it
must first see whether there is a propagated persistence context. If
one exists, the entity manager uses this persistence context to carry
out the operation. All subsequent transaction-scoped entity manager
operations, in this component or any other, will thereafter use this
newly created persistence context. This behavior works independently
of whether container-managed or bean-managed transaction demarcation
has been used.
the answer to your questions is yes(first question)
Does the entity manager automatically join the transaction and behave
as expected from transaction isolation during concurrent access?
entity manager checks the existence of a propagated persistence context and uses it.
Let's say I have a stateless session bean, Foo. I have injected an EntityManager into Foo using CDI.
I've read that by default, the EntityManager will be transaction-scoped and that therefore any entities that are in the EntityManager's persistence context will be managed by the EntityManager until the end of the transaction.
But what if the transaction starts outside of Foo, in class Bar? So Foo has been injected into Bar, the transaction starts in Bar and then Foo is called within that transaction. Of course, the EntityManager can't manage objects before Foo is created, but what about after the method in Foo returns?
After Foo returns, is the EntityManager somehow still managing the entities it was managing in Foo or have they been detached at that point even though the transaction is still continuing? If I change a value on an entity in Bar after the method in Foo returned, should that change get propagated to the database?
Many thanks!
Edit: Some code to make it clearer
class Bar {
#Inject
Foo foo;
// Transaction starts here
public void doSomething(){
foo.doSomethingElse();
// Transaction is still uncommitted here
// Make a change to an entity here that was in foo's
// entityManager's persistence context
// Does it get picked up and propagated to the db?
// Or is the entityManager gone by this point?
}
// Transaction commits after return of this method
}
#Stateless
class Foo {
#PersistenceContext
EntityManager em;
// This method by default joins the transaction that was already started in Bar
public void doSomethingElse(){
// Do something with entities
}
}
depends on how your transaction boundaries are managed. are you using bean managed transactions? container managed with REQUIRES_NEW? container managed with default boundaries?
i recommend:
https://docs.oracle.com/javaee/5/tutorial/doc/bncij.html
I need to commit transactions from CMT bean by hand. There is a loop which processes multiple records and each record should be processed in its own transaction. I wanted to mark method transaction support as NOT_SUPPORTED and then control transaction from method. However I could not retrieve a UserTransaction instance neither from SessionContext neither injecting it as a JNDI resource java:/module/UserTransaction.
Are there any chance to process multiple records in CMT bean in their own transactions without introducing new BMT bean for such processing?
You should not mess around transactions yourself if you use CMT.
I recommend you create a method for the operation needs to be in transaction, mark it as REQUIRES_NEW, then call it from the loop.
Everytime the method is called, the current transaction (if any) will be suspended and a new transaction will be started for the operation.
Something like this:
#EJB
SomeEJBLocal anotherme;
public void loop() {
for(/* something */) {
anotherme.single();
}
}
#TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
public void single() {
// do stuff
}
You will have to inject another instance of the EJB and call single in order for the container to process the transaction aspects.
I've seen a method in a Service class that was marked as #Transactional, but it was also calling some other methods in that same class which were not marked as #Transactional.
Does it mean that the call to separate methods are causing the application to open separate connections to DB or suspend the parent transaction, etc?
What's the default behavior for a method without any annotations which is called by another method with #Transactional annotation?
When you call a method without #Transactional within a transaction block, the parent transaction will continue to the new method. It will use the same connection from the parent method (with #Transactional) and any exception caused in the called method (without #Transactional) will cause the transaction to rollback as configured in the transaction definition.
If you call a method with a #Transactional annotation from a method with #Transactional belonging to the same Spring Bean, then the called methods transactional behavior will not have any impact on the transaction. But if you call a method with a transaction definition from another method with a transaction definition, and they belong to different Spring Beans, then the code in the called method will follow its own transaction definitions.
You can find more details in the section Declarative transaction management of spring transaction documentation.
Spring declarative transaction model uses AOP proxy, so the AOP proxy is responsible for transactions creation. The AOP proxy will be active only if the called method belong to a different Spring Bean than the caller one.
Does that mean the call to separate methods are causing the application to open separate connections to DB or suspend the parent transaction, etc?
That depends on a propagation level. Here are all the possible level values.
For example in case a propagation level is NESTED a current transaction will "suspend" and a new transaction will be created ( note: actual creation of a nested transaction will only work on specific transaction managers )
What's the default behavior for a method without any annotations that is called by another method with #Transactional annotation?
The default propagation level ( what you call "behavior" ) is REQUIRED. In case an "inner" method is called that has a #Transactional annotation on it ( or transacted declaratively via XML ), it will execute within the same transaction, e.g. "nothing new" is created.
#Transactional marks the transaction boundary (begin/end) but the transaction itself is bound to the thread. Once a transaction starts it propagates across method calls until the original method returns and the transaction commits/rolls back.
If another method is called that has a #Transactional annotation then the propagation depends on the propagation attribute of that annotation.
The inner method will affect the outer method if the inner method is not annotated with #Transactional.
In case inner method is also annotated with #Transactional with REQUIRES_NEW, following will happen.
...
#Autowired
private TestDAO testDAO;
#Autowired
private SomeBean someBean;
#Override
#Transactional(propagation=Propagation.REQUIRED)
public void outerMethod(User user) {
testDAO.insertUser(user);
try{
someBean.innerMethod();
} catch(RuntimeException e){
// handle exception
}
}
#Override
#Transactional(propagation=Propagation.REQUIRES_NEW)
public void innerMethod() {
throw new RuntimeException("Rollback this transaction!");
}
The inner method is annotated with REQUIRES_NEW and throws a RuntimeException so it will set its transaction to rollback but WILL NOT EFFECT the outer transaction. The outer transaction is PAUSED when the inner transaction starts and then RESUMES AFTER the inner transaction is concluded. They run independently of each other so the outer transaction MAY commit successfully.