Handling Hibernate Transactions

为君一笑 提交于 2019-12-22 04:13:28

问题


Currently I have this code duplicated in each one of my Controller methods:

Transaction transaction = HibernateUtil.getSessionFactory().getCurrentSession().getTransaction();
if (!HibernateUtil.getSessionFactory().getCurrentSession().getTransaction().isActive()) {
    transaction.begin();
}

Is this the correct way or is there a better way of doing this, perhaps in a separate class that I can reference? If so, how? Every time I've tried to put it in a separate class and reference it from other classes, it failed.

edit: I'm trying to use as few external libraries as possible. I wouldn't use Hibernate if Java had an ORM/JPA implementation built into the JDK


回答1:


I've run into this myself many times. Ordinarily my first recommendation would be Spring transaction management, however I understand you are trying to limit the number of third party libraries you are using.

Since you're using a static API in your HibernateUtil class, you may find it helpful to consolidate your logic in a method, and putting the 'what you want to do in a transaction' code (which varies controller to controller) in a callback.

First, define an interface to describe each controller's inTransaction behavior:

public interface TransactionCallback {
    void doInTransaction();
}

Now, create a static method in your HibernateUtil class to handle beginning, committing, and if necessary rolling back your transactions:

public class HibernateUtil {
    public static void inTransaction(TransactionCallback tc) {
        Transaction transaction = HibernateUtil.getSessionFactory().getCurrentSession().getTransaction();
        if (!HibernateUtil.getSessionFactory().getCurrentSession().getTransaction().isActive()) {
            transaction.begin();
            try {
                tc.doInTransaction();
                transaction.commit();
            } catch (Exception e) {
                transaction.rollback();
            }
        }
    }
}

In your controller, you'd use your new method with an anonymous inner class:

....
    HibernateUtil.inTransaction(new TransactionCallback() {
        void doInTransaction() {
            // do stuff for this controller
        }
    });
....

This approach should at least take care of the duplication you'd like to eliminate, and there's plenty of room for extending it to handle particular exceptions, etc.




回答2:


You have to close hibernate transaction after each transaction (e.g. Controller request). In this case you will not need

if (!HibernateUtil.getSessionFactory().getCurrentSession().getTransaction().isActive()) 

and you WILL need to call .close() each time after request.

It is better to use code like:

class Controller { 
//...
commonActionMethod() {
  begin transaction
  specificActionMethod();
  close transaction
}

And childs of this Controller class should implement specificActionMethod().

Code is clean. Transactions are safe. No third-party libs required.




回答3:


  1. You can Very well use JDK Proxies to implement your own AOP . Ref : Link1 Link2
  2. Have Service Layer to intract with DAO framework such as Hibernate and so. So that your controller is just controll the flow and your service can implement business.
  3. Have SeviceLocator / FactoryPattern to get hold of your Service instances ( In other words return proxies instead of Actual instance).
  4. Define your own Annotations and identify your methods required transaction or not. if required handle transaction around your method call in your proxy handler.

In this way you don't need to depend on any library other than JDK. and you can turn off or on transaction just by having Annotations.
If you start manage the instances ( services) you can lot of magics with combination of FactoryPattern + JDK Proxies ( Actual Interfaces) + AOP Concepts.




回答4:


you can create separate class for connection.

        public class HibernateUtil {

      private static final SessionFactory sessionFactory = buildSessionFactory();

      @SuppressWarnings("deprecation")
    private static SessionFactory buildSessionFactory() {
        try {
          // Create the SessionFactory from Annotation
          return new AnnotationConfiguration().configure().buildSessionFactory();
        }
        catch (Throwable ex) {
          // Make sure you log the exception, as it might be swallowed
          System.err.println("Initial SessionFactory creation failed." + ex);
          throw new ExceptionInInitializerError(ex);
        }
      }

      public static SessionFactory getSessionFactory() {
        return sessionFactory;
      }
    }

On Server Side you can write :-

    Session session=null;
    Transaction tx=null;

     try {
        session =HibernateUtil.getSessionFactory().openSession();
        tx=session.beginTransaction();
        } catch (HibernateException e) {
    e.printStackTrace();
    }finally
    {
        session.close();
    }



回答5:


Avoiding the use of additional, external libraries, you may wish to supply an interceptor that implements that standard J2EE servlet Filter interface. Such implementation is sometimes referred to as the Open Session in View pattern. I cite the following from this page:

When an HTTP request has to be handled, a new Session and database transaction will begin. Right before the response is send to the client, and after all the work has been done, the transaction will be committed, and the Session will be closed.




回答6:


If you are using spring in your project. I will suggest to use the TX using spring AOP, In that you just have to specify the pointcuts for your transactions. The Spring AOP TX will taken care of begin and commit the transaction on basis of your point cut and could also roll-back the TX in case of exception occurred. Please go through the link of example - here




回答7:


package com.project.stackoverflow;

import org.hibernate.Session;
import org.hibernate.SessionFactory;

public class HibernateUtil {

    private static final ThreadLocal threadSession = new ThreadLocal();
    private static SessionFactory sessionFactory;

    /**
     * A public method to get the Session.
     * 
     * @return Session
     */
    public static Session getSession() {
        Session session = (Session) threadSession.get();

        // Open a Session, if this thread has none yet
        if ((null == session) || !session.isOpen()) {
            logger.info("Null Session");
            session = sessionFactory.openSession();
            logger.info("Session Opened");
            threadSession.set(session);
        }

        return session;
    }

    public static void closeSession() {
        Session session = (Session) threadSession.get();

        // Open a Session, if this thread has none yet
        if (null != session) {
            session.close();
            session = null;
            threadSession.set(null);
        }
    }

        return sessionFactory;
    }

    public void setSessionFactory(SessionFactory sessionFactory) {
        logger.info("Inside set session Factory");
        this.sessionFactory = sessionFactory;
        logger.info("After set session Factory");
    }

    public static void save(Object obj) {
        getSession().save(obj);
        getSession().flush();
    }

    public static void saveOrUpdate(Object obj) {
        getSession().saveOrUpdate(obj);
        getSession().flush();
    }
    public static void batchUpdate(Object obj) {
        getSession().saveOrUpdate(obj);
        getSession().flush();
    }

    public static void update(Object obj) {
        getSession().update(obj);
        getSession().flush();
    }

    public static void delete(Object obj) {
        getSession().delete(obj);
        getSession().flush();
    }
}

You can probably go for this solution. I have made a separate JavaClass for Hibernate Instantiation and use. You can get the session from here itself which may suffice your need. Hope it helps :)




回答8:


I used this technique.

My Servlet context is like this:

    <beans:bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
    destroy-method="close" p:driverClassName="${jdbc.driverClassName}"
    p:url="${jdbc.databaseurl}" p:username="${jdbc.username}" p:password="${jdbc.password}" />

    <beans:bean id="sessionFactory"
    class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
    <beans:property name="dataSource" ref="dataSource" />
    <beans:property name="configLocation">
        <beans:value>classpath:hibernate.cfg.xml</beans:value>
    </beans:property>
    <beans:property name="configurationClass">
        <beans:value>org.hibernate.cfg.AnnotationConfiguration</beans:value>
    </beans:property>
    <beans:property name="hibernateProperties">
        <beans:props>
            <beans:prop key="hibernate.dialect">${jdbc.dialect}</beans:prop>
            <beans:prop key="hibernate.show_sql">true</beans:prop>
        </beans:props>
    </beans:property>
</beans:bean>

<beans:bean id="transactionManager"
    class="org.springframework.orm.hibernate3.HibernateTransactionManager">
    <beans:property name="sessionFactory" ref="sessionFactory" />
</beans:bean>

<tx:annotation-driven transaction-manager="transactionManager" />

Then you can simply use

   @Autowired
    private SessionFactory sessionFactory;

Whenever I want to use a session or do any operations I simply do it like this:

        Session session = sessionFactory.openSession();
        Transaction transaction = session.beginTransaction();
        session.save(userAccount);
        transaction.commit();
        session.close();

I think it will help.




回答9:


If you have application server like glassfish, it has imbended eclipselink JPA/ORM implementation and you can manage transaction using standart JEE annotations.



来源:https://stackoverflow.com/questions/21024637/handling-hibernate-transactions

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!