Обработка Транзакций Hibernate

В настоящее время у меня этот код дублируется в каждого один из моих методов контроллера:

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

это правильный способ или есть лучший способ сделать это, возможно, в отдельном классе, на который я могу ссылаться? Если да, то как? Каждый раз, когда я пытался поместить его в отдельный класс и ссылаться на него из других классов, он не удался.

редактировать: Я пытаюсь использовать как можно меньше внешних библиотек. Я бы не использовал Hibernate, если бы Java Реализация ORM/JPA, встроенная в JDK

9 ответов


Я сам сталкивался с этим много раз. Обычно моей первой рекомендацией было бы управление транзакциями Spring, но я понимаю, что вы пытаетесь ограничить количество сторонних библиотек, которые вы используете.

поскольку вы используете статический API в своем классе HibernateUtil, вам может быть полезно консолидировать свою логику в методе и поместить код "что вы хотите сделать в транзакции" (который варьируется от контроллера к контроллеру) в обратный вызов.

во-первых, определите интерфейс для описания поведения inTransaction каждого контроллера:

public interface TransactionCallback {
    void doInTransaction();
}

Теперь создайте статический метод в классе HibernateUtil для обработки начала, фиксации и при необходимости отката транзакций:

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();
            }
        }
    }
}

в вашем контроллере вы бы использовали свой новый метод с анонимным внутренним классом:

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

этот подход должен по крайней мере позаботиться о дублировании, которое вы хотели бы устранить, и есть много места для расширения его для обработки конкретных исключений и т. д.


вы должны закрыть транзакцию гибернации после каждой транзакции (например, запрос контроллера). В этом случае вам не понадобится

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

и вам нужно будет позвонить .закрыть () каждый раз после запроса.

лучше использовать код:

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

и дочерние элементы этого класса контроллера должны реализовать specificActionMethod ().

код сканируется. Транзакции безопасны. Никаких сторонних библиотек не требуется.


  1. вы можете очень хорошо использовать прокси JDK для реализации собственного AOP . Ref:link1 и Link2
  2. есть уровень обслуживания для взаимодействия с DAO framework, например Hibernate и т. д. Так что ваш контроллер просто контролирует поток, и ваш сервис может реализовать бизнес.
  3. имейте SeviceLocator / FactoryPattern, чтобы получить ваши экземпляры службы ( другими словами, возвращайте прокси вместо фактического экземпляра).
  4. определить собственные аннотации и определить ваши методы требуется транзакция или нет. при необходимости обработайте транзакцию вокруг вызова метода в обработчике прокси-сервера.

таким образом, вам не нужно зависеть от любой библиотеки, кроме JDK. и вы можете отключить или включить транзакцию, просто имея аннотации.
Если вы начнете управлять экземплярами (службами), вы можете много магии с комбинацией Прокси FactoryPattern + JDK ( фактические интерфейсы) + концепции AOP.


вы можете создать отдельный класс для подключения.

        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;
      }
    }

на стороне сервера вы можете написать : -

    Session session=null;
    Transaction tx=null;

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

избегая использования дополнительных внешних библиотек, вы можете указать перехватчик это реализует этот стандартный сервлет J2EE фильтр интерфейс. Такая реализация иногда упоминается как открыть сеанс в View узор. Я цитирую следующее из этого страница:

когда HTTP-запрос должен быть обработан, начнется новый сеанс и транзакция базы данных. Прямо перед отправкой ответа клиенту, и после того, как вся работа будет выполнена, транзакция будет зафиксирована, и сессия будет закрыта.


Если вы используете spring в своем проекте. Я предложу использовать TX, используя spring AOP, в том, что вам просто нужно указать pointcuts для ваших транзакций. Весна AOP TX позаботится о начале и фиксации транзакции на основе вашего точечного разреза, а также может откатить TX в случае возникновения исключения. Пожалуйста, перейдите по ссылке пример - здесь


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();
    }
}

Вы, наверное, можете пойти на это решение. Я сделал отдельный JavaClass для создания и использования Hibernate. Вы можете получить сеанс отсюда, который может удовлетворить ваши потребности. Надеюсь, это поможет:)


я использовал эту технику.

мой контекст сервлета выглядит так:

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

затем вы можете просто использовать

   @Autowired
    private SessionFactory sessionFactory;

всякий раз, когда я хочу использовать сеанс или делать какие-либо операции, я просто делаю это так:

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

Я думаю, это поможет.


Если у вас есть сервер приложений, такой как glassfish, он внедрил реализацию eclipselink JPA/ORM, и вы можете управлять транзакцией с помощью стандартных аннотаций JEE.