I have a hibernate session that retrieved from sessionFactory.openSession(), And some complex computing to the Entities and also I want to persistent(UPDATE, INSERT, DELETE) some Entities during the computing.
Here is a case:
Consider that I have a ProductEntity that represents a product, and an OrderEntity which represents the order record of the product, and a UserEntity which represents the user who book the order of the product.
I know I can process an booking operation this way:
public void addOrder(UserEntity userEntity, ProductEntity productEntity, int quantity){
session = sf.openSession();
Transaction tx = session.beginTransaction();
//do some compution and generate the orderEntity and persistent it to the db.
try{
tx.commit();
}catch(Exception e){
tx.rollback();
}finnaly{
session.close();
}
}
Now I have to add more action to the procedure, such as (Maybe) making a NotifyEntity and stored in db which represents the notify records to the Merchant who owns the product. this notify record could be generated by the orderEntity and has nothing to do with the productEntity or UserEntity, in fact, I want this notifyMerchantByOrderEntity method separated-out of the addOrder procedure so that I can reuse this method and clarify the code( I really don't want to mess-up with a bulk of codes in the same method, in fact if the checking logic is complex enough, the code of the addOrder method could be very-very loooong).
Anyhow, I want to :
Separating a very long transaction to several methods
But these methods should be considered together as a whole transaction(ie, they should rollback together when Exception occurs)
Something like this:
public void addOrder(UserEntity userEntity, ProductEntity productEntity, int quantity){
Session session = sf.openSession();
session.beginTransaction();
invokeOtherMethod(); //invoking other method which also contains some db operation.
try{
tx.commit()
}catch(Exception e){
tx.rollback(); //this should rollback the operation in invokeOtherMethod() too.
}finally{
session.close();
}
}
I found a alternative way to do this which pass the session to the method that will be invoked. And test demos show I can also rollback the db operations in the invoked method when Exception occurs, Although the answer #Vlad given may worth a trial. I post my way of doing this here hoping it may inspire guys meet with similar issue.
public OrderEntity addOrder(UserEntity userEntity, ProductEntity, int quantity){
Session session = sf.openSession();
try{
Transaction tx = session.beginTransaction();
OrderEntity orderEntity = new OrderEntity(userEntity, productEntity, quantity);
session.save(orderEntity);
notifyMerchant(orderEntity, session); //here we will invoke another method with session together.
tx.commit();
return orderEntity;
}catch(Exception e){
e.printStack();
tx.rollback(); // if Exception occurs all the operations making to the db will be rollback, as well as those operations in method notifyMerchant();
return null;
}finally{
session.close();
}
}
private void notifyMerchant(OrderEntity orderEntity, Session session){
NotifyEntity notifyEntity = new NotifyEntity(orderEntity);
notifyEntity.notifyMerchant();
session.save(notifyEntity); // this will save the notifyEntity to db as a sort of log stuff;
}
#Vlad may give a better way of doing this, currently the above implement is the way with less change of my project.
You should switch to using Spring TransactionManager as it allows you to group multiple DAO calls into the same database transaction.
If you don't want to use Spring, you can still implement the transaction management logic using a Service Layer method interceptor (maybe a Dynamic Proxy or Javassist) that employs a ThreadLocal storage to bind the current Thread to the same Hibernate Session
When you enter the transaction boundary you open the transaction and when you leave the transaction boundary, you commit the transaction if there is no exception being thrown or roll it back if you caught any Exception:
private SessionFactory sf;
private final ThreadLocal<Session> sessionStorage =
new ThreadLocal<Session>();
#Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Session session = sessionStorage.get();
if(session == null) {
session = sf.openSession();
}
Transaction tx = session.beginTransaction();
try{
return method.invoke(target, args);
tx.commit();
}catch(Exception e){
tx.rollback();
throw new ServiceException(e);
}finally{
session.close();
sessionStorage.remove();
}
}
Now you'll have to weave this interceptor to all Service method calls.
Related
I imagine that this is a common problem, but after some searching I wasn't able to find anything relevant.
The problem I'm having is that I'm getting a No Hibernate Session bound to thread exception when annotating my resource method with #UnitOfWork and inside my resource method, making an asynchronous DAO call. The idea behind this design is to make the database call on a separate I/O thread so that it frees up the Jersey resource thread.
Unfortunately, as the exception says, this RxIoScheduler-2 thread doesn't have a hibernate session bound to it.
Any suggestions?
Hibernate Session is not thread safe, so we need a strategy how to get the current session for the current thread. Such strategy is called CurrentSessionContext.
The current session is a session which we get by this call:
sessionFactory.getCurrentSession()
Hibernate can be configured with various current session strategies. #UnitOfWork uses this strategy:
hibernate.current_session_context_class = managed
For this strategy you should put a session to the context by an explicit call of the
ManagedSessionContext.bind(session)
So, as we know a Session is not thread safe, you should create a new session for a separate thread and put that session in the ManagedSessionContext. After that you can call your DAO by the same way as in the endpoint methods with #UnitOfWork.
Keep in mind that you should unbind the session before closing it with
ManagedSessionContext.unbind(factory)
You can use this utility class to create a session for a separate thread:
public final class HibernateSessionUtils {
private HibernateSessionUtils() {
}
public static void request(SessionFactory factory, Runnable request) {
request(factory, () -> {
request.run();
return null;
});
}
public static <T> T request(SessionFactory factory, Supplier<T> request) {
Transaction txn = null;
Session session = factory.openSession();
try {
ManagedSessionContext.bind(session);
txn = session.beginTransaction();
T result = request.get();
commit(txn);
return result;
} catch (Throwable th) {
rollback(txn);
throw Throwables.propagate(th);
} finally {
session.close();
ManagedSessionContext.unbind(factory);
}
}
private static void rollback(Transaction txn) {
if (txn != null && txn.isActive()) {
txn.rollback();
}
}
private static void commit(Transaction txn) {
if (txn != null && txn.isActive()) {
txn.commit();
}
}
}
Throwables from guava.
It can be used by this way
List<Campaign> getCampaigns(SessionFactory factory, CampaignDao dao) {
return HibernateSessionUtils.request(
factory,
dao::getCampaigns
);
}
In the dao.getCampaigns() method you can get the session
sessionFactory.getCurrentSession()
You can inject the factory everywhere using Guice.
Other option is to use UnitOfWorkAwareProxyFactory.
Consider this two classes: EmployeeDetailDAOImpl and EmployeeDAOImpl. Assume if I want to create a new employee, I should also create a new record for EmployeeDetail.
Given the below implementation, I wonder if the outer transaction(EmployeeDAOImpl's tx) is rolled back due to any exceptions happened after the detailDAO.create(employeeId) call, will the transaction of new EmployeeDetail be rolled back as well?
public class SessionHandler {
public static getSession() {
return Configuration.buildSessionFactory().openSession(); //ignore the isConnected or other exception handling for now
}
}
public class EmployeeDetailDAOImpl {
public void create(Serializable employeeId) {
Session session = SessionHandler().getSession();
Transaction tx = session.beginTransaction();
try {
EmployeeDetail detail = new EmployeeDetail(employeeId);
session.save(detail );
} catch (Exception e) {
if (tx!= null) {
tx.rollback;
}
}
session.close();
}
}
public class EmployeeDAOImpl {
public void add(String name) {
Session session = SessionHandler().getSession();
Transaction tx = session.beginTransaction();
try {
Employee employee = new Employee(name);
Serializable employeeId= session.save(employee);
EmployeeDetailDAOImpl detailDAO = new EmployeeDetailDAOImpl();
detailDAO.create(employeeId);
//more things here, that may through exceptions.
} catch (Exception e) {
if (tx!= null) {
tx.rollback;
}
}
session.close();
}
}
Actually, none of the given answers is 100% correct.
It depends on the calling party/service.
If you are calling the methods from an EJB, you will have 1 transaction covering both method calls. That way, the transaction will roll back both operations in case of an exception. Reason behind this is that every method in EJB is transaction Required, unless specified otherwise in the annotation or ejb deployment descriptor.
If you are using spring or any other DI framework, then it depends on your configuration. In a normal setup, your calling transaction will be suspended, since the JPA EJB will create its own transaction. You can however use the JTATransactionManager (As specified here) to make sure that both your EJB and your Spring bean share the same transaction.
If you call the JPA methods from a POJO, then you will have to take care of the JTA transaction handling yourself.
Yes, it will rollback the entity Employee as well. It doesn't even depend on whether the other entities are related.
It depends on the scope of the transaction, which here includes both Employee and EmployeeDetails
You are creating two different transaction for each method.Hence rollback can not happen.
To rollback the transaction you require the propogation in Transaction.
You need to write the code like below::
#Transactional(propagation=Propagation.REQUIRED)
public void testRequired(User user) {
testDAO.insertUser(user);
try{
innerBean.testRequired();
} catch(RuntimeException e){
// handle exception
}
}
Below is link for more information of Propogation.
http://docs.spring.io/spring-framework/docs/2.5.6/api/org/springframework/transaction/annotation/Propagation.html
http://www.byteslounge.com/tutorials/spring-transaction-propagation-tutorial
I am using Spring MVC and hibernate. My underlying database is Sybase ASA. Inside my DAO I want to find out what is the transaction level used for the query
This is what my DAO has.
Session session = getSession();
String SQL_QUERY = "select ..... ";
Query query = session.createSQLQuery(SQL_QUERY);
query.executeUpdate();
I referred to this link but this seems to be outdated so doesn't work
What is default isolation level hibernate uses if not explicitely set?
I would say that question is raised incorrectly: a query itself doesn't have any isolation level. An Isolation level is a feature of a transaction.
So you, most probably, want to know what is the isolation level of current transaction? Hence you, most probably, do not manage this level by your code. In this case it is managed by DBMS default or current settings.
One of possible ways to explore it is described here:
try {
session = getSessionFactory().openSession();
txn = session.beginTransaction();
session.doWork(new Work() {
#Override
public void execute(Connection connection) throws SQLException {
LOGGER.debug("Transaction isolation level is {}", Environment.isolationLevelToString(connection.getTransactionIsolation()));
}
});
txn.commit();
} catch (RuntimeException e) {
if ( txn != null && txn.isActive() ) txn.rollback();
throw e;
} finally {
if (session != null) {
session.close();
}
}
I am trying to use Criteria API from Hibernate by getting hibernate session from EntityManager as following
public org.hibernate.Criteria getCriteria() {
HibernateEntityManager hem = em.unwrap(HibernateEntityManager.class);
org.hibernate.Session session = hem.getSession();
return session.createCriteria(getEntityBeanType());
}
In createCriteria return I am getting "session is closed error".
From the same point of code where I call getCriteria if I call createQuery method as
getEntityManager().createQuery(".....");
It is working fine and I can do select on database.
I want to use Hibernate Criteria API becuase I am comfortable with it.
The point here is that Criteria object exists only when Session object is open, if the Session object has been closed Criteria object won't work, there is a class named DetachedCriteria that allows the developer create Criteria queries outside of Session object then attach them to it and be able to run. Read more about Detached here
Detached criteria is very good alternate when the hibernate session is not present
You can use something like this.
//Not required a session open
DetachedCriteria query = DetachedCriteria.forClass(Employee.class);
query.add(Property.forName("name").eq("Som"));
//Here we open the session
SessionFactory sessionFactory = HibernateUtil.getSessionFactory();
Session session = sessionFactory.openSession();
List<Employee> employeeList = new ArrayList<Employee>();
//Then we associate the criteria query with the session and run it
employeeList = query.getExecutableCriteria(session).list();
Iterator it = employeeList.iterator();
Make sure if your session is open before you create criteria
public org.hibernate.Criteria getCriteria() {
HibernateEntityManager hem = em.unwrap(HibernateEntityManager.class);
org.hibernate.Session session = hem.getSession();
if(session.isOpen())
{
return session.createCriteria(getEntityBeanType())
}
return null;
}
Check your log for more information and share the same here and it will be helpful for us to debug...
I've been seeing the same problem. It comes out of the improper use of the #Transactional annotation. Here's what I was doing:
doStuff(String[] ids) {
Criteria criteria = dao.createCriteria(Widget.class); // Session still open here.
criteria.add(Restrictions.in("widgetId", ids)); // This closed the session!
...
}
It turns out that my dao class was annotated with the #Transactional annotation. So as soon as I tried to do something with my Criteria, it first closed the transaction, which closes the session. The solution was get rid of the dao's createCriteria() method and to add a method to the dao that does all the work with that uses the Criteria instance.
doStuff(String[] ids) {
List<Widget> = dao.getWidgetsFromIds(ids); // Do everything inside the transaction
...
}
----
// This is in the DAO:
public List<Widget> getWidgetsFromIds(String[] ids) {
Criteria criteria = dao.createCriteria(Widget.class); // Session is open here.
criteria.add(Restrictions.in("widgetId", ids)); // Session still open
...
}
The way I've structured my code, the Criteria should be an internal implmentation detail of the DAO, and shouldn't be visible to the calling methods.
public String createUser() {
Session session = HibernateUtility.getSession();
Transaction tx = session.beginTransaction();
session.save(this);
tx.commit();
session.close();
return "accountCreated";
}
How to write Unit test case for this method?
It is difficult to unit test code which contains calls to static methods, like HibernateUtility.getSession().
You might consider injecting the Session instance (perhaps by adding a parameter to the createUser() method, or by giving the object that contains this method a Session Factory object which it can use to obtain Session instances). You could then use Mockito or a similar framework to provide a mock Session object to the method, and verify that the expected calls where being made to it.
public String createUser(Session session) {
Transaction tx = session.beginTransaction();
session.save(this);
tx.commit();
session.close();
return "accountCreated";
}
Using Mockito, a unit test might look like this:
public testCreateUser(){
Session session = mock(Session.class);
Transaction trans = mock(Transaction.class);
when(session.beginTransaction()).thenReturn(trans);
obj.createUser(session);
verify(session).save(obj);
verify(session).close();
verify(trans).commit();
}