Hibernate sessions management in Spring Boot JPA application with HikariCP data source - java

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.

Related

Could not open JPA EntityManager for transaction nested exception is java.lang.IllegalStateException:Already value for key bound to thread

We are using JdbcTemplate to save data in some DAO's and JPA in some DAO's. We are facing the below issue while calling the #Transaction method(here saveAll method) from another programmatic transaction-related code.
Stack Trace:
org.springframework.transaction.CannotCreateTransactionException: Could not open JPA EntityManager for transaction;
nested exception is
java.lang.IllegalStateException:Already value [org.springframework.jdbc.datasource.ConnectionHolder#303ef11] for key [HikariDataSource (HikariPool-1)] bound to thread [http-nio-8091-exec-3]
Example Code Snippet:
OneServiceImpl:
Here we are using jdbcTemplate to save data and using programmatic transactions. From the programmatic transaction method, we are calling another serviceImpl class method which is using JPA.
public CommonResponse saveCaseConfigurations(List<CaseDTO> caseList){
transactionTemplate.execute(new TransactionCallbackWithoutResult() {
protected void doInTransactionWithoutResult(TransactionStatus status) {
try {
scheduleServiceRepo.saveCases(caseList)); //Using JDBC template
......
serviceTwo.updateCaseLogs(caseLogs); // calling secod service method.
} catch (Exception ex) {
status.setRollbackOnly();
throw new CustomRunTimeException("Error due to"+ex.getMessage());
}
}
});
}
TwoServiceImpl:
Here we are using Jpa and saveAll method have #Transactional annotation.
#Override
public CommonResponse updateCaseLogs(List<CaseLogs> caseLogs) {
caseLogs = caseLogMasterRepo.saveAll(caseLogs);//Using JPA to save all in CaseLogs entity, failing here as saveAll is having #Transactional annotion.
return new CommonResponse("SUCCESS", null,HttpStatus.OK);
}
We are using two transaction managers for two transactions.
Please help me how we can disable the inner transaction and carry the same transaction to the saveAll method call.
Have to add required propagation levels to your JPA transaction. See below post which has all JPA transaction propagation levels in details. Propagation level set to REQUIRED to execute multiple transaction in single scope.
Spring JPA transaction over multiple methods

Using Spring Transaction with Mybatis batch

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.

Spring #Transactional prevent opening of Transaction

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")

Build EntityManagerFactory using Hibernate and JPA

I'm building a web application using SpringFramework. In the data access layer I was using Hibernate to query data in MySQL, which was working fine. My SessionFactory is build from this method:
public SessionFactory sessionFactory() throws HibernateException {
return new org.hibernate.cfg.Configuration()
.configure()
.buildSessionFactory();
}
Today I'm going to integrate my data access with JPA, which needs a EntityManagerFactory, IMO I only need to change the code above into the following:
#Bean
public EntityManagerFactory entityManagerFactory() throws HibernateException {
return new org.hibernate.cfg.Configuration()
.configure()
.buildSessionFactory();
}
simply because SessionFactory extends EntityManagerFactory. However I got an exception
java.lang.ClassCastException:
org.hibernate.internal.SessionFactoryImpl cannot be cast to
javax.persistence.EntityManagerFactory
This is quite weird, because SessionFactoryImpl implements SessionFactory while SessionFactory extends EntityManagerFactory. I don't know why the cast fails.
My question is: 1. why the cast is invalid? 2. what's the correct way to build a EntityManagerFactory using Hibernate?
EDIT Debugger says
factory instanceof SessionFactory //true
factory instanceof EntityManagerFactory //false
and the source of SessionFactory
public interface SessionFactory extends EntityManagerFactory, HibernateEntityManagerFactory, Referenceable, Serializable, Closeable
I'm sure all the above EntityManagerFactory refers to javax.persistence.EntityManagerFactory.
Hmmm, unless you're using Hibernate 5.2 Hibernate's org.hibernate.SessionFactory does not extend javax.persistence.EntityManagerFactory, hibernate-entitymanager was only recently merged into hibernate-core. Seems to me that you're browsing source code from a version newer than the one you're using in your project.
In either case, to see the correct way of using Hibernate with JPA, refer to this link.
You can reuse the Session factory by following the steps here, setting up the SessionFactory bean in Spring.
But since you've already coded in terms of the EntityManagerFactory, you should follow the easier route described in this tutorial It's in German, unfortunately, but the code should speak for itself. You declare Hibernate as your JPA vendor, then the EntityManagerFactory with the vendor and the datasource, which can be defined explicitly or be a JNDI datasource, defined in your container and requested like this:
<jee:jndi-lookup id="dataSource" jndi-name="myDataSource"/>
And then it's very nice to declare a Spring transactionManager to allow the #Transactional annotation so you won't have to deal with explicit transaction management.
Then you can let your UserDAO look like this:
public class UserDAO {
#PersistenceContext
private EntityManager em;
#Transactional
public List<User> findUserByName(String name) {
return em.createNamedQuery("User.findByName")
.setParameter("name", name)
.getResultList();
}
}
where User is something like
#Entity
#NamedQueries({ #NamedQuery( name="User.findByName", query="select u from User where u.name = :name") })
public class User {
#Id
private Long id;
#Column
private String name;
...
}
There's many ways to do this, though.

Hibernate + Spring openSession in Transaction

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.

Categories