I have this code and I have a question :
#Service
class SimpleServiceImpl implements SimpleService{
#PersistenceContext
private EntityManager em;
#Transactional
public void doSmth(){
// here I want to have a new session
Session session = em.unwrap(Session.class).getSessionFactory().openSession();
// do smth in new session
session.close();
}
What about a transaction ??? Actually as I understand if I open session I have to manage this session explicitly and I have to close it. But what's going on with transaction? Should I begin a new transaction and manage it by myself or my action s continue in the same transaction which method doSmth began?
You have annotated with #Transactional that's why Spring will handle transaction using AOP concept.
If you use a Transactional annotation your method will be done within one transaction.
For more information you could also check this: spring transactional what happens in background.
Related
In Spring Boot application with HikariCP dataSource I've execute HQL queries with helper class:
public class QueryExecutor {
private Session session;
#Autowired
private SessionFactory sessionFactory;
public QueryExecutor getConnection() {
session = sessionFactory.openSession();
session.beginTransaction();
return this;
}
public void closeConnection() {
session.getTransaction().commit();
session.close();
}
public List execute(String hql, Long limit, Long offset) {
getConnection();
Query query = session.createQuery(hql);
List list = query.list();
closeConnection();
return list;
}
It works ok, but when I start using class widely, application starts freezes because Hibernate Session is closed randomly and transaction wait 30 seconds (default timeout value for HikariCP transactions) before get Session is closed error (or currentPersistenceContext is null if I used getCurrentSession() instead openSesssion()).
First of all, I changed open session to getCurrentSession function. But I also need to specify context with #PersistenceContext or hibernate.current_session_context_class=thread in hibernate.cfg.xml.
I read that this property better to use with default value.
Also I specify hibernate.connection.release_mode=AFTER_TRANSACTION.
But that isn't solve a problem.
After all, I changed class like that:
#PersistenceContext
private EntityManager entityManager;
#Transactional
public List execute(String hql, Long limit, Long offset) {
Query query = entityManager.createQuery(hql);
return query.getResultList();
}
and use javax.persistence.Query instead Hibernate queries.
Now it works ok. But is that a correct modifications?
All functions worked with execute method of QueryExecutor annotated with #Transactional. As I uderstood, in that case no beginTransaction() needed.
But need I close entityManager after execute() ?
SessionFactory used with Hibernate without JPA and EntityManager used with Hibernate JPA technologies?
How can I solve problem without using EntityManager?
You don't need to close transactions manually if you use #Transactional annotation.
But if you use it, I will reccomend you try to use JPA Repositories and wrap in #Transactional annotation the methods of business logic only.
In which case you will no longer need EntityManager and you will be able to create custom complex queries with JpaSpecificationExecutor and JPA Criteria API Queries.
I have a #Transactional method which invokes another method on a common spring bean, that has long been used to perform batch operations, across mappers. The problem now is that, if there is an error in the #Transactional method, the DMLs executed on the batch are not rolled back, as they were executed on a different session, with it's own transaction.
public class HamsterRepo{
...
#Autowired
BatchOperationBean<Hamsters> idioticBatchBean;
#Transactional
public void saveHamsters(List<Hamsters> hams){
idioticBatchBean.executebatch("saveHamsters", hams);
}
}
public class BatchOperationBean<T>{
#Autowired
SqlSessionFactory sqlFactory;
public void executebatch(String mapperId, List<T> ts){
SqlSession sqlSession =
this.sqlSessionFactory.openSession(ExecutorType.BATCH,
TransactionIsolationLevel.SERIALIZABLE);
try(sqlSession){
for(T t in ts){
sqlSession.update(mapperId , t);
}
sqlSession.commit();
// Clean Up stuff and Exception Handling...
}
}
}
Now, Is there a way to relate the two Spring Tx and SqlSessionFactory? Will injecting SqlSession, instead of the factory help? Or is there a way to obtain the SqlSession from Spring Tx? Or a way in Spring to identify & execute queries across mappers without an SqlSesion?
PS: Using Spring 4.1.7, Mybatis: 3.4.4, Mybatis-spring : 1.3.1
Looks like the the way to link Spring Transaction and Mybatis SqlSession is the SqlSessionTemplate
Thread safe, Spring managed, SqlSession that works with Spring transaction management to ensure that that the actual SqlSession used is the one associated with the current Spring transaction. In addition, it manages the session life-cycle, including closing, committing or rolling back the session as necessary based on the Spring transaction configuration.
Iam using Spring's #Transactional annotation for my DAO classes/methods for DB operations.
Hibernate ORM configured with PostgreSQL
SessionFactory is created with #Bean in configuration and retreived using #Autowired in DAO classes.
#Autowired
SessionFactory sessionFactory;
#Transactional(value = "myTransactionManager")
public int getTotalClientsInDatabase() {
Query countTotalClientsQuery = sessionFactory.getCurrentSession()
.getNamedQuery(DeviceInfo.COUNT_TOTAL_CLIENTS);
return Integer.parseInt(countTotalClientsQuery.uniqueResult().toString());
}
Whenever getTotalClientsInDatabase is called, a Transaction is opened always.
I want to prevent opening of a Transaction as it is a SELECT(DDL) query.
If #Transactional is removed Exception is thrown saying sessionFactory is not synchronized.
If readOnly=true is added performance is reduced.
#Transactional(value = "myTransactionManager", readOnly=true)
Is there any way to stop Session from opening a Transaction??
Good practice would be in your case to mark the transaction with a propagation level of SUPPORTS:
Support a current transaction, execute non-transactionally if none
exists.
So if the parent method is not in a transactional context, this method will also not be, but can.
#Transactional(propagation=Propagation.SUPPORTS, value = "myTransactionManager")
I am trying to understand the transactions management and try to use its power in my already existing application developed in Struts 2, EJB 3 and hibernate 5.2.
Now I have ejb in my business layer like below
#Stateless
#TransactionManagement(TransactionManagementType.CONTAINER)
public class MyEJb implements ejbxyz {
#Override
public void method(){
Dao dao=new Dao() //Dao class is simple java class
dao.fooMethod(); //this method updates some record
dao.barMethod(); // this method updates some other record
}
}
public class Dao{
fooMethid(){
Session session=sessFactory.openSession();
session.beginTransaction();
session.update(x);
}
barMethod(){
try{
Session session=sessFactory.getCurrentSession();
session.getNamedQuery("xyz").executeUpdate();
}catch(HibernateException ex){
session.getTransaction.rollback();
}
}
}
I understand that Transaction management should be done at service layer(at ejb in my case). But how can I achieve this over there. ?
Now the dependency is if barMethod() fails to update the record then I need to rollback the changes made in fooMethod. So basically I need both the methods to be done in one transaction.
When I execute the application it throws the below exception
Exception while barMethod getNamedQuery is not valid without active transaction
Its because I am not beginning any transaction in barMethod. But then I really dont want to start a new transaction and want to continue with the older transaction started in fooMethod.
Container managed transactions are indeed suported out of the box for EJB beans. However, your Dao class is not a managed bean - it is a regular pojo that you instantiate manualy - therefore it does not participate in any transaction started by your other ejb.
So move your Dao to separate file, annotate it with #Stateless and then inject it into your service using #EJB private Dao dao;
There is more to transactions in Ejb container though. You can control the transaction support on method level via #TransactionAttribute annotation, that specifies how should the container invoke your method with regard to transaction. That way you can control, whether your method requires its own transaction, or if it shall participate in a transaction initiated by the caller(e.g. when invoked from ejb bean). For more info have a look at official Java EE tutorial
I am trying to create a testcase for my DAO classes that use plain Hibernate API (no Spring stuff like HibernateTemplate,HibernateDaoSupport), just like this:
sessionFactory.getCurrentSession().save(obj);
I have the appropriate sessionFactory and transactionManager definition in spring context as shown in the spring docs.
What I want is to open a transaction in my start up code and rollback at the end.
So this is different from the default Spring unit testing supporting concept of transaction for every test method call and so I could not extend AbstractTransactionalTestNGSpringContextTests.
I need a way to start a transaction and somehow feed it in session factory. I feel this should be extremely easy but could not achieve after lot of reading and experiments.
Any help would be greatly appreciated.
If you don't want to use HibernateTemplate, you can use transactionManager directly as described in 10.6.2 Using the PlatformTransactionManager.
try {
Session session = factory.openSession();
Transaction tx = session.beginTransaction();
...
tx.commit();
session.close();
} catch (SomeException e) {
tx.rollback();
...
}
#Transactional(readOnly = false, propagation = Propagation.REQUIRED)
annotate the test method using above