Управление транзакциями с помощью аннотаций
От: Аноним  
Дата: 17.02.12 10:08
Оценка:
Используется Spring+hibernate

Как использовать @Transactional вообщем знаю, вешаем на метод бина @Transactional и спринг оборачивает метод в транзакцию.

как указать что нужно создать новую сессию, чтобы в рамках одного метода организовать child транзакции, использую HibernateTransactionManager? Пробывал REQUIRES_NEW, REQUIRES_CHILD вешать и на do() и на doInInnerTransaction(), но commit/rollback в doInInnerTransaction, закрывает внешнюю транзакцию, т.е. лок скидывается..

Сейчас делаю так:

public void do() {
    Session session = null;
    Transaction transaction = null;
    try {
           session = sessionFactory.openSession();
           transaction = session.beginTransaction();
           session.createSQLQuery("select ... from some_table where id = ? for update nowait");

           try {
                  doInInnerTransaction(); 
           } catch(DataAccessException ex) {
                // exception
           }
    } finally() {
           transaction.rollback();
           session.close();
    }
}

@Transactional
public void doInInnerTransaction() {
    // do some actions with db
}
Re: Управление транзакциями с помощью аннотаций
От: Blazkowicz Россия  
Дата: 17.02.12 10:20
Оценка:
Здравствуйте, Аноним, Вы писали:
public void doOuter() {
                  doInInnerTransaction(); 
}

@Transactional
public void doInInnerTransaction() {
    // do some actions with db
}

Проблема в том как работают эти аннотации. Spring создаёт прокси. И при вызове метода, обработчик прокси проверяет а не надо ли чего сделать с транзакцией.
В случае вызова метода того же класса. Как у вас. Вызов идёт мимо прокси. Поэтому всё что вы пишете в @Transactional для doInInnerTransaction — игнорируется.
Достаточно геморная задача.
Re[2]: Управление транзакциями с помощью аннотаций
От: Аноним  
Дата: 17.02.12 11:14
Оценка:
Здравствуйте, Blazkowicz, Вы писали:

А если разнести по разным классам данные методы тогда как решается?

Правильно понимаю о таком решении идет речь?

@Component
public class Outer {
     @Autowired
     private Inner inner;


     @Transactional
     public void do() {
         inner.do();
     }


}

@Component
public class Inner {
      @Transactionl(propagation = Propagation.REQUIRES_NEW)
      public void do() {
      }
}



B>Здравствуйте, Аноним, Вы писали:

B>
B>public void doOuter() {
B>                  doInInnerTransaction(); 
B>}

B>@Transactional
B>public void doInInnerTransaction() {
B>    // do some actions with db
B>}
B>

B>Проблема в том как работают эти аннотации. Spring создаёт прокси. И при вызове метода, обработчик прокси проверяет а не надо ли чего сделать с транзакцией.
B>В случае вызова метода того же класса. Как у вас. Вызов идёт мимо прокси. Поэтому всё что вы пишете в @Transactional для doInInnerTransaction — игнорируется.
B>Достаточно геморная задача.
Re: Управление транзакциями с помощью аннотаций
От: vsb Казахстан  
Дата: 17.02.12 11:17
Оценка:
Попробуйте вызывать doInnerTransaction не через this, а явным образом вытащить объект текущего класса (например через @Autowired или ApplicationContext) и вызывать через него. Тогда вызовы пойдут через прокси.
Re[3]: Управление транзакциями с помощью аннотаций
От: Blazkowicz Россия  
Дата: 17.02.12 11:18
Оценка:
Здравствуйте, Аноним, Вы писали:

А>А если разнести по разным классам данные методы тогда как решается?

А>Правильно понимаю о таком решении идет речь?
Да, только возможно нужно NESTED вместо REQUIRES_NEW, если у вас просто JDBC транзакция. Посмотрите в JavaDoc
Re: Управление транзакциями с помощью аннотаций
От: tavr  
Дата: 17.02.12 13:24
Оценка:
Здравствуйте, Аноним, Вы писали:

А>Используется Spring+hibernate


А>Как использовать @Transactional вообщем знаю, вешаем на метод бина @Transactional и спринг оборачивает метод в транзакцию.


А>как указать что нужно создать новую сессию, чтобы в рамках одного метода организовать child транзакции, использую HibernateTransactionManager? Пробывал REQUIRES_NEW, REQUIRES_CHILD вешать и на do() и на doInInnerTransaction(), но commit/rollback в doInInnerTransaction, закрывает внешнюю транзакцию, т.е. лок скидывается..

Вы смешиваете ручное управление транзакциями Hibernate и декларативное с использованием Spring.
Спринг ничего не знает про иерархию вызовов транзакционных методов, если вы начинаете транзакцию руками.
Для того чтобы он это узнал надо использовать спринговые классы HibernateTemplate, JdbcTemplate или TransactionTemplate.

А>Сейчас делаю так:

вызывы методов должны просходить из внешнего по отношению к DAO слоя

Здесь doMethod2 выполниться в контексте основной транзакции, а doMethod1 — в новой
public class MyService
{
   // DAO with transaction annotation
   private ILocalDao localDao;
   ...

   @Transactional
   public void doWork {
       localDao.doMethod1();
       ...
       localDao.doMethod2();
    }
}

public class LocalDao implements ILocalDao 
{
   // DAO with transaction annotation
   private ILocalDao localDao;
   ...

   @Transactional(propagation = Propagation.REQUIRES_NEW)
   public void doMethod1{
       ...
   }

   @Transactional
   public void doMethod2{
       ...
   }
}
Re[2]: Управление транзакциями с помощью аннотаций
От: pagrus  
Дата: 17.02.12 17:50
Оценка:
B>Проблема в том как работают эти аннотации. Spring создаёт прокси. И при вызове метода, обработчик прокси проверяет а не надо ли чего сделать с транзакцией.
B>В случае вызова метода того же класса. Как у вас. Вызов идёт мимо прокси. Поэтому всё что вы пишете в @Transactional для doInInnerTransaction — игнорируется.
B>Достаточно геморная задача.

Есть вариант с aspectj-компиляцией, тогда работает и для вложенных методов. Хотя ради одого случая я б не стал.
Re[2]: Управление транзакциями с помощью аннотаций
От: elmal  
Дата: 20.02.12 09:50
Оценка:
Здравствуйте, Blazkowicz, Вы писали:

B>Проблема в том как работают эти аннотации. Spring создаёт прокси. И при вызове метода, обработчик прокси проверяет а не надо ли чего сделать с транзакцией.

B>В случае вызова метода того же класса. Как у вас. Вызов идёт мимо прокси. Поэтому всё что вы пишете в @Transactional для doInInnerTransaction — игнорируется.
B>Достаточно геморная задача.
А что геморного? Я вроде делал когда то подобную задачу здесь
Автор: elmal
Дата: 26.07.11
. До сих пор пользуюсь, внес в либы, доволен как слон. Соответственно единственное, что нужно сделать, это отнаследоваться от этого класса плюс вызвать self.doInInnerTransaction();
Re[3]: Управление транзакциями с помощью аннотаций
От: Blazkowicz Россия  
Дата: 20.02.12 10:11
Оценка:
Здравствуйте, elmal, Вы писали:

E>А что геморного? Я вроде делал когда то подобную задачу здесь
Автор: elmal
Дата: 26.07.11
. До сих пор пользуюсь, внес в либы, доволен как слон. Соответственно единственное, что нужно сделать, это отнаследоваться от этого класса плюс вызвать self.doInInnerTransaction();

Ваше решение портит всю концепцию IoC. На кой черт мне в моём дорогом и любимом бине ссылка на контекст? Мало того что мне нужно убедится что туда будет этот контекст заинжекчен, так ещё мне нужно думать, что будет с моим бином в отсутствие контекста. И, не дай бог, бин нужно сериализовать. Значит мне придется восстанавливать ссылку на контекст при десериализации? А что делать, если у меня такая ситуация в 2-х, 3-х классах? Копипастить transient поле, init и десериализацию во все классы?
Re[3]: Управление транзакциями с помощью аннотаций
От: Blazkowicz Россия  
Дата: 20.02.12 11:00
Оценка:
Здравствуйте, elmal, Вы писали:

E>А что геморного? Я вроде делал когда то подобную задачу здесь
Автор: elmal
Дата: 26.07.11
. До сих пор пользуюсь, внес в либы, доволен как слон. Соответственно единственное, что нужно сделать, это отнаследоваться от этого класса плюс вызвать self.doInInnerTransaction();

А на счет вашего решения, возможно, было бы проще через ApplicationContextAware сделать?
Re[4]: Управление транзакциями с помощью аннотаций
От: elmal  
Дата: 20.02.12 11:21
Оценка:
Здравствуйте, Blazkowicz, Вы писали:

B>А на счет вашего решения, возможно, было бы проще через ApplicationContextAware сделать?

А приблизительно так и сделано — вот окончательный вариант:
public abstract class SelfContainedBean<T> implements BeanNameAware {
    @Resource
    private ApplicationContext context;

    private String beanName;

    protected T self;

    @PostConstruct
    private void init() {
        self = (T) context.getBean(beanName);
    }

    public void setBeanName(String name) {
        beanName = name;
    }
}
Re[4]: Управление транзакциями с помощью аннотаций
От: elmal  
Дата: 20.02.12 12:13
Оценка:
Здравствуйте, Blazkowicz, Вы писали:

B>Ваше решение портит всю концепцию IoC. На кой черт мне в моём дорогом и любимом бине ссылка на контекст? Мало того что мне нужно убедится что туда будет этот контекст заинжекчен, так ещё мне нужно думать, что будет с моим бином в отсутствие контекста. И, не дай бог, бин нужно сериализовать. Значит мне придется восстанавливать ссылку на контекст при десериализации? А что делать, если у меня такая ситуация в 2-х, 3-х классах? Копипастить transient поле, init и десериализацию во все классы?

IOC будет нужен только для юнит тестов. Соответственно, заинжектить self особо труда не составит. Если же требуется проверить обработку аннотаций, то именно юнит тестами это сделать проблематично, придется писать интеграционные тесты и поднимать спринговый контекст — в этом случае на этапе создания бина все будет просетано. Относительно сериализации бина — лично я никогда в жизни по доброй воле сериализовать бины не буду. Давно уже сериализую исключительно DTO и Entity, в которых логика если и есть, то простейшая. В бинах же состояние вообще не храню. Пришел к такому я далеко не сразу, но после того, как пришел, избавился от очень многих проблем. Число багов и, как результат, время на реализацию фичи, уменьшилось на порядок.
Re[5]: Управление транзакциями с помощью аннотаций
От: Blazkowicz Россия  
Дата: 20.02.12 12:53
Оценка:
Здравствуйте, elmal, Вы писали:

E>IOC будет нужен только для юнит тестов.

E>Соответственно, заинжектить self особо труда не составит.
Вы недооцениваете всё многообразие архитектур и проектов.

E>Относительно сериализации бина — лично я никогда в жизни по доброй воле сериализовать бины не буду.

EJB это часто делает за нас.

E>Давно уже сериализую исключительно DTO и Entity, в которых логика если и есть, то простейшая.

Сам точно так же делаю. Но это никак не исключает факта существования Rich Domain Model в природе.

E>В бинах же состояние вообще не храню.

Это странно. Потому что JavaBeans это технология свойств и слушателей. Собсвтенно в "бинах" состояние и хранится. Но это уже недорабоки терминологии.

E>Пришел к такому я далеко не сразу, но после того, как пришел, избавился от очень многих проблем. Число багов и, как результат, время на реализацию фичи, уменьшилось на порядок.

Это флеймовый вопрос.
Re[6]: Управление транзакциями с помощью аннотаций
От: elmal  
Дата: 20.02.12 13:18
Оценка:
Здравствуйте, Blazkowicz, Вы писали:

E>>В бинах же состояние вообще не храню.

B>Это странно. Потому что JavaBeans это технология свойств и слушателей. Собсвтенно в "бинах" состояние и хранится. Но это уже недорабоки терминологии.
Не точно выразился. Я говорил про спринговые бины. Со scope="singleton". Именно в них имеет смысл ставить аннотации. Если же брать бины, подразумевающие хранение состояния (prototype, session), то там уж точно не место для аннотации @Transactional, и уж туда я б точно контекст не стал пихать, а также наследоваться от вышеупомянутого класса. Боюсь даже представить, что будет, если бины с такими хаками засериализовать . У меня как то аж JVM рухнула, когда я подобный экземпляр по ошибке сериализовать решил
Re[7]: Управление транзакциями с помощью аннотаций
От: Blazkowicz Россия  
Дата: 20.02.12 13:43
Оценка:
Здравствуйте, elmal, Вы писали:

E>Не точно выразился.

Всё точно. Просто терминология уже запутанная в Java.

E>Я говорил про спринговые бины. Со scope="singleton". Именно в них имеет смысл ставить аннотации. Если же брать бины, подразумевающие хранение состояния (prototype, session), то там уж точно не место для аннотации @Transactional, и уж туда я б точно контекст не стал пихать, а также наследоваться от вышеупомянутого класса. Боюсь даже представить, что будет, если бины с такими хаками засериализовать .

У меня просто рука не подымается курочить классы, только потому что "фреймверк иначе не работает". Совершенно лишняя и не нужная зависимость. Совершенно не очевидная для постороннего читателя логика. Когда у меня возникла такая проблема, я предпочел, все же, разбить на разные классы.
Re: Управление транзакциями с помощью аннотаций
От: tealex  
Дата: 28.02.12 18:25
Оценка:
aspectj
решает всякие проблемы и бесполезнве тонкости с прокси
хана
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.