мы разрабатываем приложение с использованием JSF 2.0 (primefaces) и Hibernate 3.6.1. Мы следуем подходу, позволяющему более высокому уровню приложения быть независимым от структуры DAL .... это означает, что мы неМы не используем подход сеанса к запросу, но мы сконфигурировали Hibernate для обработки сеанса для каждого потока.Мы реализовали класс, роль которого состоит в том, чтобы обрабатывать «атомарную операцию», заботиться об открытии сеанса и транзакции, когда они еще не выполняются, и просто подключать другую операцию БД к существующей транзакции, чтобы сделать их частьюосновной операции, позволяя им знать, выполняются ли они независимо или как часть более крупной операции.Вот код для нашего OperationManager:
public class OperationManager implements IOperationManager {
Transaction tx = null;
boolean isInternalTransaction = false;
/* (non-Javadoc)
* @see alekso.npe.dal.IOperationManager#beginOperation()
*/
@Override
public Session beginOperation(){
Session session = SessionFactoryUtil.getInstance().getCurrentSession();
if (session.getTransaction().isActive()) {
isInternalTransaction = false;
tx = session.getTransaction();
}
else {
isInternalTransaction = true;
tx = session.beginTransaction();
}
return session;
}
/* (non-Javadoc)
* @see alekso.npe.dal.IOperationManager#commitOperation()
*/
@Override
public void commitOperation(){
if (isInternalTransaction)
tx.commit();
}
/* (non-Javadoc)
* @see alekso.npe.dal.IOperationManager#rollbackOperation()
*/
@Override
public void rollbackOperation(){
if (isInternalTransaction)
tx.rollback();
}
}
Класс SessionFactoryUtil - это «классическая» фабричная утилита для спящего режима, предлагаемая везде ... мы выбрали эту реализацию:
public class SessionFactoryUtil {
final static Logger log = Logger.getLogger(SessionFactoryUtil.class);
/** The single instance of hibernate SessionFactory */
private static org.hibernate.SessionFactory sessionFactory;
/**
* disable contructor to guaranty a single instance
*/
private SessionFactoryUtil() {
}
static {
// Annotation and XML
// sessionFactory = new
// AnnotationConfiguration().configure().buildSessionFactory();
// XML only
try {
sessionFactory = new Configuration().configure().buildSessionFactory();
} catch (Exception e) {
log.error("Errore nella creazione del session factory", e);
System.out.println("Errore nella creazione del session factory");
}
}
public static SessionFactory getInstance() {
try{
return sessionFactory;
} catch (Exception ex)
{
log.error("errore nella creazione della session di hibernate", ex);
return null;
}
}
/**
* Opens a session and will not bind it to a session context
*
* @return the session
*/
public Session openSession() {
return sessionFactory.openSession();
}
/**
* Returns a session from the session context. If there is no session in the
* context it opens a session, stores it in the context and returns it. This
* factory is intended to be used with a hibernate.cfg.xml including the
* following property <property
* name="current_session_context_class">thread</property> This would return
* the current open session or if this does not exist, will create a new
* session
*
* @return the session
*/
public Session getCurrentSession() {
return sessionFactory.getCurrentSession();
}
/**
* closes the session factory
*/
public static void close() {
if (sessionFactory != null)
sessionFactory.close();
sessionFactory = null;
}
}
Теперь, как мы используем диспетчер операций?Проще говоря, тот, кто должен отвечать за атомарную операцию в БД, вызывает метод в OperationManager.
public class BL1{
public void highMethod() {
IOperationManager om = new OperationManager();
BL2 bl2 = new BL2();
BL3 bl3 = new BL3();
try {
om.beginOperation();
bl2.midMethod();
bl3.midMethod();
om.commitOperation();
} catch (Exception ex) {
omrollbackOperation();
}
}
}
public class BL2{
public void midMethod() {
IOperationManager om = new OperationManager();
DAL1 dal1 = new DAL1();
DAL2 dal2 = new DAL2();
try {
om.beginOperation();
dal1.lowMethod1();
dal2.lowMethod1();
om.commitOperation();
} catch (Exception ex) {
omrollbackOperation();
}
}
}
public class BL3{
public void midMethod() {
IOperationManager om = new OperationManager();
DAL1 dal1 = new DAL1();
DAL2 dal2 = new DAL2();
try {
om.beginOperation();
dal1.lowMethod2();
dal2.lowMethod2();
om.commitOperation();
} catch (Exception ex) {
omrollbackOperation();
}
}
}
public class DAL1{
public void lowMethod1() {
IOperationManager om = new OperationManager();
Session session = nullM
try {
session = om.beginOperation();
// do some work on session
session.saveOrUpdate(...);
session.load(..);
session.somethingElse(....);
om.commitOperation();
} catch (Exception ex) {
omrollbackOperation();
}
}
public void lowMethod2() {
IOperationManager om = new OperationManager();
Session session = nullM
try {
session = om.beginOperation();
// do some work on session
session.saveOrUpdate(...);
session.load(..);
session.somethingElse(....);
om.commitOperation();
} catch (Exception ex) {
omrollbackOperation();
}
}
}
public class DAL2{
public void lowMethod1() {
IOperationManager om = new OperationManager();
Session session = nullM
try {
session = om.beginOperation();
// do some work on session
session.saveOrUpdate(...);
session.load(..);
session.somethingElse(....);
om.commitOperation();
} catch (Exception ex) {
omrollbackOperation();
}
}
public void lowMethod2() {
IOperationManager om = new OperationManager();
Session session = nullM
try {
session = om.beginOperation();
// do some work on session
session.saveOrUpdate(...);
session.load(..);
session.somethingElse(....);
om.commitOperation();
} catch (Exception ex) {
omrollbackOperation();
}
}
}
}
Если я вызываю BL1.highMethod, каждый из них ниже (все это метод, названный извнутри) будет находиться под транскрипцией, которую он начал: om.beginOperation () в вызываемом методе просто возвращает сессию, начатую BL1.highMethod, а om.commitOperation или om.rollBackOperation вызванного метода просто ничего не делаютоставляя в экземпляре om, созданном в BL1.highMethod, ответственность за фиксацию / откат и закрытие сеанса.Но если мы вызовем напрямую BL2.midMethod, ответственность за управление сессией и транзакцией будет своей.То же самое происходит, если мы вызываем напрямую DAL1.lowMethod1.Шаблон таков: если я (как метод) не вызывал beginOperation, все, что я использую для выполнения своей работы, будет обрабатывать сеанс / транзакцию самостоятельно;если я начну работу, я буду отвечать за управление сессией / транскрипцией.Надеюсь, я ясно дал понять.
Теперь мне показалось, что это очень разумный подход ... пока что-то пойдет не так.Что идет не так, очень сложно сказать.Просто приложение начинает вести себя странно: иногда приложение создает блокировку на БД во время обновления (база данных говорит о блокирующей строке, но, глядя на последовательность операторов sql, сброшенных на БД, это не имеет смысла);иногда нет блокировки и не регистрируется ошибка, но приложение, похоже, читает «старые данные» ... как если бы операции, которую мы делали пару секунд назад, не было ... и она появилась там только через 30 секунд(или 10, или 60 ... это зависит).Я имею в виду очень странное поведение.Мы видим только некоторую ошибку в журнале, когда мы попадаем в блокировку строки на БД, что приводит к длительной действующей транзакции, в противном случае ничего не кажется неправильным.Чтобы усложнить задачу, иногда работает та же самая точная последовательность действий пользователя над приложением (над одной и той же записью, одним и тем же пользователем и т. Д.), Иногда нет.И это происходит из-за того, что мы реорганизуем код, представляющий шаблон, который я показал выше, используя класс OperationManager.
У кого-то есть представление о том, что мы сделали неправильно, почему этот шаблон не работает должным образом?Я прошу прощения за длину вопроса, размещенного ...