четверг, 31 марта 2011 г.

getCurrentSession() дубль 2


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

Согласно книге, стандартная архитектура приложения, использующего базу данных и какой-либо фреймворк для работы с ней (обычно это 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(). Ведь это даст нам возможность получать ссылку на открытую, уже участвующую в транзакции сессию.

4 комментария:

  1. т.е. Если я вас правильно понял, в реализации DAO должно быть что-то такое:

    Session session = null;
    try{
    session = factory.getCurrentSession();
    session.beginTransaction();
    // do work
    session.getTransaction().commit();
    } catch {//rolback}
    finally {// не закрывать сессию ни в коем случае}

    А на сервисном уровне

    factory.openSession();
    //do work
    factory.closeSession();

    ОтветитьУдалить
  2. hibernateTemplate.execute =) а сессию хибернэйт сам находит

    ОтветитьУдалить
  3. хм... а если проект не на Spring?
    Вообще делаю RESTful приложение с авторизацией. Использую Jersey. Можно, конечно, притянуть spring.data access layer. Либо полностью переключиться на spring...

    ОтветитьУдалить
  4. to SiMBa:

    сессию открывать не нужно. getCurrentSession() открывает ее автоматически. При каких условиях открывает? Это зависит от реализации интерфейса CurrentSessionContext

    ОтветитьУдалить