I know, that there is a way to descend to a low level - get connection and perform two transaction's by hand in a single hibernate session.
But the question is - how to invoke second nested transaction in the same Session via #Transactional annotations (not using "low level hacks" or handwrited custom transaction management)?
Some possible code:
#Service
public class DoubleTransaction {
#Autowired
private SessionFactory sf;
#Autowired
private NestedTeHandler nestedHandler;
#Transactional
void invokeTransaction() {
Session cs = sf.getCurrentSession();
Session nestedCs = nestedHandler.invokeNested(sf);
System.out.println(cs == nestedCs);
}}
#Service
public class NestedTeHandler {
#Transactional
Session invokeNested(SessionFactory sf) {
return sf.getCurrentSession();
}}
You might be able to do that with
#org.springframework.transaction.annotation.Transactional(propagation = Propagation.NESTED)
on NestedTeHandler.invokeNested. See documenation at https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/transaction/annotation/Transactional.html.
See also this question: Differences between requires_new and nested propagation in Spring transactions.
Related
I am learning JDBS template and wonder if there is a way to rollback operation.
It is easy to do it using JDBC, just
conn.setAutoCommit(false);
// DoinП stuff
con.rollback();
But is there a way do the same using JDBS template?
You should use the spring-boot-starter-data-jdbc like in this guide
Thanks to this spring-boot-starter-data-jdbc and to the spring boot autoconfiguration mechanism, a jdbc template bean and a transaction manager bean will be created for you.
Then you can use #Transactional annotation on non private method to wrap your code into a transaction :
#Transactional
public void methodA() {
jdbcTemplate.update(...);
jdbcTemplate.update(...);
}
or you can use a transacionTemplate to handle the transaction programatically
#Service
public class ServiceA {
private final TransactionTemplate transactionTemplate;
public SimpleService(PlatformTransactionManager transactionManager) {
this.transactionTemplate = new TransactionTemplate(transactionManager);
}
public void methodA() {
transactionTemplate.execute(new TransactionCallback<>() {
public Object doInTransaction(TransactionStatus status) {
jdbcTemplate.update(...);
jdbcTemplate.update(...);
return jdbcTemplate.query(...);
}
});
}
}
If you want to execute multiple statements and want to be sure all or none executed, you can use transactions.
First, you have to create a session and begin the transaction.
After, you can execute statements and commit the transaction.
But, in case of problems, you can rollback (undo previous statements).
With spring you can use #Transactional annotation
#Transactional
public void transaction() { // Spring will begin transaction
doTransactionalStatements(); // Spring will rollback in case of RuntimeException
} // Spring will commit transaction
Hi I have two different methods and they use different datasources and transaction manager.I use #Transactional attribute and what I want, if my second method throws exception than my first method do its rollback. But it is not working, first method cant rollback. What am I missing?
#Transactional(rollbackFor = Exception.class, isolation = Isolation.READ_COMMITTED,
transactionManager = myTransactionManager", propagation = Propagation.REQUIRED)
public void saveTest(TblTest testEntity) {
mySecondDBSource.saveTest2(testEntity);(use MyTransactionManager2) //Do job
testTableRepository.save(testEntity); (Use myTransactionManager) //throws Exception
}
//in mySecondDBSource class there is another method
#Transactional(rollbackFor = Exception.class, isolation = Isolation.READ_COMMITTED,
transactionManager = "MyTransactionManager2", propagation = Propagation.REQUIRED)
public void saveTest2(TblTest2 testEntity) {
testTableRepository2.save(testEntity);
}
Spring Data offers a way to handle so called chained/distributed transactions via ChainedTransactionManager.
See spring-transactional-with-a-transaction-across-multiple-data-sources.
Here is also a simple guide on medium.
Is it secure to pass a Injected EntityManager created on an EJB, to a method that will return an Object, and after, persist that Object on a Web Session for web clients use it?
Like in this example:
the EJB
#Stateless(mappedName = "MyService")
#LocalBean
public class MyService implements MyServiceLocal {
#PersistenceContext(unitName="primary")
private EntityManager em;
/**
* Default constructor.
*/
public MyService() {
}
#Override
public Service newServiceX(User user) {
return new ServiceX(user,em); // here, passing the EntityManager
}
}
After, I persist this Service in a web client (using struts):
The base action
public class YAction extends ActionSupport implements SessionAware{
#Inject
private MyServiceLocal service;
public String execute(){
Service x = service.newServiceX();
persistInCookie("ServiceX",x);
}
public void persistInCookie(String, Object){
// persist
}
}
And after, using another Action:
// another Action that
class XAction{
public String useService(){
getService().doSomething();
}
protected Service getService(){
Service service = (Service) getSessionMap().get("ServiceX");
return service;
}
}
the POJO class ServiceX using the EntityManager:
public class ServiceX extends Service{
EntityManager em;
public ServiceX(User user, EntityManager em){
this.em = em;
}
public void doSomething(){
// do something with the EntityManager passed by the EJB
}
}
First, the action that would be call is the Y action to persist the Service on the Session, next, the X action will return the Service persisted on the Session and try to use it.
I believe that the EJB Stateless Session Bean can close My EntityManager and this ServiceX POJO class can't use it. This can happen? I found similar question HERE, but in this question, the EntityManager is passed to a helper class. In my case is different because I want to persist this Object on a session cookie, and use later.
I don't think It is a good idea to store a EntityManager in SessionMap. What is more, I don't even think that it is a good idea to perform EntityManager operations outside the EJB container.
Have read about transaction-boundaries in JPA?
By default, EJB container is using CMT (Container Managed Transactions). In this case, container uses entitymanager-per-request pattern which means that the transaction begins and ends when one of the business methods of MyService starts and ends (transaction is committed or rollbacked in case of RuntimeException). For whole transaction time, EntityManager is connected with the same PersistenceContext. After the transaction is ended the container closes EntityManager which means that the EntityManager is disconnected with recent PersistenceContext:
// transaction begins
Service x = service.newServiceX();
// transaction ends
This might be crucial if you were going to do some update/insert operations outside the transaction.
Now, when you call EntityManager operation (like find) outside the transaction, for every each operation the EntityManager will create new PersistentContext. This may cause some issues, as two entities that represent the same record will be treated as different entities:
// each operation occurs in a separate persistence context, and returns
// a new detached instance
Magazine mag1 = em.find(Magazine.class, magId);
Magazine mag2 = em.find(Magazine.class, magId);
assertTrue(mag2 != mag1);
Some more articles to read:
Persistent Context
Transactions and Concurrency
Entity Lifecycle Management
I have a spring application which works with hibernate and annotations.
I open a session by hand which is done like in this How do I manually open a hibernate session? stack overflow answer.
Now i want to use a method of a service in the method that opens the session itself. This service however is annotated with the transactional statement. Is it possible to tell the method to use the transaction i just opened by hand?
e.g.
#Transactional("TransactionManager")
class Service1 {
public LazyObject someMethod();
}
class MetaService {
#Autowired
SessionFactory sf;
Service1 s1;
public someMethod() {
Session s = sf.openSession();
s.beginTransaction();
// tell this method to use s's transaction
// without annotating someMethod() with #transactional
LazyObject lo = s1.someMethod();
for ( LazyAtt la : lo.getLazyAtt() ) {
la.doSomething();
}
s.flush();
s.getTransaction().commit();
s.close();
}
}
For those wondering why I want to do it, check this question:
https://stackoverflow.com/questions/29363634/how-to-open-two-sessions-in-one-function-in-hibernate-spring
Consider I have following spring beans
CompositeService:
#Service
public class CompositeService {
#Resource
private ServiceA serviceA;
#Resource
private ServiceB serviceB;
public ResultBean compositeMethod() {
ResultBean result = new ResultBean();
result.setA(serviceA.getA());
result.setB(serviceB.getB());
return result;
}
}
ServiceA:
#Service
public class ServiceA {
#Transactional
#Cacheable
A getA() {
// calls DAO layer and makes a query to the database
}
}
ServiceB:
#Service
public class ServiceB {
#Transactional
#Cacheable
B getB() {
// calls DAO layer and makes a query to the database
}
}
The cacheable aspect has a higher order
The problem with this code is that it will start two transactions (and take two connections from the pool) in case of cache-miss in both services.
Can I configure Spring to use the same transaction in this use case?
I.E. propagate the transaction from ServiceA to the CompositeService and after that down to ServiceB?
I cannot set the CompositeService as transactional because I do not want to start a transaction (and borrow a connection from pool) in case of cache-hit both in ServiceA and ServiceB
Spring will only propagate a transaction if everything is under the same transaction. So the short answer is that you should annotate your CompositeService with #Transactional.
#Service
public class CompositeService {
#Transactional
public ResultBean compositeMethod() {
ResultBean result = new ResultBean();
result.setA(serviceA.getA());
result.setB(serviceB.getB());
return result;
}
}
Generally this is fast enough as it only does a checkout from the underlying connection pool. However if you experience latency or don't always need a connection you can wrap your actual DataSource in a LazyConnectionDataSourceProxy. This will obtain a Connection when first needed.
What you can do is you can annotate the compositeMethod with #Transactional as well. The default propagation level of a transaction is set to Required
Support a current transaction, create a new one if none exists.
So even though its not exactly what you asked, the transaction demarcation starts with the compositeMethod, but it should be exactly the semantics you want.