Обработка Транзакций 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 ().
код сканируется. Транзакции безопасны. Никаких сторонних библиотек не требуется.
- вы можете очень хорошо использовать прокси JDK для реализации собственного AOP . Ref:link1 и Link2
- есть уровень обслуживания для взаимодействия с DAO framework, например Hibernate и т. д. Так что ваш контроллер просто контролирует поток, и ваш сервис может реализовать бизнес.
- имейте SeviceLocator / FactoryPattern, чтобы получить ваши экземпляры службы ( другими словами, возвращайте прокси вместо фактического экземпляра).
- определить собственные аннотации и определить ваши методы требуется транзакция или нет. при необходимости обработайте транзакцию вокруг вызова метода в обработчике прокси-сервера.
таким образом, вам не нужно зависеть от любой библиотеки, кроме 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.