Перечитывала книжку про хибернет и спринг и возникло несколько мыслей, которые хотелось бы записать.
Согласно книге, стандартная архитектура приложения, использующего базу данных и какой-либо фреймворк для работы с ней (обычно это ORM - object-relational mapping система) состоит из 3 уровней:
- доменная модель - обьекты предметной области, которые и нужно сохранять в базе
- DAO - уровень, абстрагирующий нас от конкретной реализации операций с БД. Сюда входят интерфейсы, описывающие базовые операции с базой данных, например, сохранение обьекта, нахождение обьекта по условиям и т.д. В книге рекомендуется для каждой сущности доменной модели создавать свой DAO. Ну и конкретная реализация этих интерфейсов, в зависимости от выбраной ORM-системы, JDBC etc. При желании реализацию можно поменять, не меняя бизнес-логику (классы бизнес-логики зависят только от интерфейсов, описывающих общие методы доступа к БД, без ссылок на конкретную реализацию). А используя Spring это можно сделать, просто изменив файл конфигурации.
- Сервисный уровень - здесь реализуется бизнес-логика. Методы представляют собой unit of work с точки зрения предметной области. Ну там всякие снять деньги со счета-положить на другой счет в одной операции. Именно методы этого уровня рекомендуется делать транзакционными.
В связи с этим у меня вопрос. Насколько я понимаю, в каждом DAO для доступа к БД должна быть ссылка на сессию. Однако транзакции мы обьявляем на уровне сервиса, который использует несколько DAO. Таким образом, если в каждом DAO мы будем открывать свою сессию (а откуда же нам ее еще взять, ведь реализация доступа к БД у нас инкапсулирована, то есть извне ссылку на готовую сессию получить нельзя), то как же мы сможем реализовать транзакции уровнем выше? Ведь тогда получается, что сессия "вложена" в транзакцию. А во всех базовых примерах написано, что должно быть наоборот, типа:
Session sess = factory.openSession();
Transaction tx;
try {
tx = sess.beginTransaction();
//do some work
...
tx.commit();
}
catch (Exception e) {
if (tx!=null) tx.rollback();
throw e;
}
finally {
sess.close();
}
И в таком случае получается, чтобы обеспечить описанную выше архитектуру, мы просто обязаны в наших DAO использовать getCurrentSession вместо openSession(). Ведь это даст нам возможность получать ссылку на открытую, уже участвующую в транзакции сессию.
т.е. Если я вас правильно понял, в реализации DAO должно быть что-то такое:
ОтветитьУдалитьSession session = null;
try{
session = factory.getCurrentSession();
session.beginTransaction();
// do work
session.getTransaction().commit();
} catch {//rolback}
finally {// не закрывать сессию ни в коем случае}
А на сервисном уровне
factory.openSession();
//do work
factory.closeSession();
hibernateTemplate.execute =) а сессию хибернэйт сам находит
ОтветитьУдалитьхм... а если проект не на Spring?
ОтветитьУдалитьВообще делаю RESTful приложение с авторизацией. Использую Jersey. Можно, конечно, притянуть spring.data access layer. Либо полностью переключиться на spring...
to SiMBa:
ОтветитьУдалитьсессию открывать не нужно. getCurrentSession() открывает ее автоматически. При каких условиях открывает? Это зависит от реализации интерфейса CurrentSessionContext