Как включить, в спящем режиме Перехватчик, когда у меня есть мой Быть в спящем режиме Транзакция, организованная Spring?

Если у меня есть @OneToMany отношения с @Cascade (CascadeType. SAVE_UPDATE) следующим образом

public class One {

    private Integer id;

    private List manyList = new ArrayList();

    @Id
    @GeneratedValue
    public Integer getId() {
        return this.id;
    }

    @OneToMany
    @JoinColumn(name="ONE_ID", updateable=false, nullable=false)
    @Cascade(CascadeType.SAVE_UPDATE)
    public List getManyList() {
        return this.manyList;
    }        

}

И Многие классифицируют

public class Many {

    private Integer id;

    /**
      * required no-arg constructor
      */ 
    public Many() {}

    public Many(Integer uniqueId) {
        this.id = uniqueId
    }

    /**
      * Without @GeneratedValue annotation
      * Hibernate will use assigned Strategy
      */ 
    @Id
    public Integer getId() {
        return this.id;
    }

}

Если у меня есть следующий сценарий

One one = new One();

/**
  * generateUniqueId method will Take care of assigning unique id for each Many instance
  */
one.getManyList().add(new Many(generateUniqueId()));
one.getManyList().add(new Many(generateUniqueId()));
one.getManyList().add(new Many(generateUniqueId()));
one.getManyList().add(new Many(generateUniqueId()));

И я звоню

sessionFactory.getCurrentSession().save(one);

Перед продолжением

Согласно Переходной персистентности В спящем режиме справочная документация, Вы видите

Если родитель передается для сохранения (), обновление () или saveOrUpdate (), все дети передаются saveOrUpdate ()

хорошо. Теперь Давайте посмотрим, какая Персистентность Java С В спящем режиме книга Переговоры о saveOrUpdate методе

Будьте в спящем режиме запрашивает таблицу MANY для данного идентификатора, и если это найдено, Будьте в спящем режиме, обновляет строку. Если это не найдено, вставка новой строки требуется и делается.

Который может быть переведен согласно

INSERT INTO ONE (ID) VALUES (?)

/**
  * I have four Many instances added To One instance
  * So four select-before-saving
  *
  * I DO NOT NEED select-before-saving 
  * Because i know i have a Fresh Transient instance
  */
SELECT * FROM MANY WHERE MANY.ID = ?
SELECT * FROM MANY WHERE MANY.ID = ?
SELECT * FROM MANY WHERE MANY.ID = ?
SELECT * FROM MANY WHERE MANY.ID = ?

INSERT INTO MANY (ID, ONE_ID) VALUES (?, ?)
INSERT INTO MANY (ID, ONE_ID) VALUES (?, ?)
INSERT INTO MANY (ID, ONE_ID) VALUES (?, ?)
INSERT INTO MANY (ID, ONE_ID) VALUES (?, ?)

Какое-либо обходное решение Для предотвращения выбора перед сохранением??? Да, Вы можете также

  • Добавьте @Version столбец (Не примененный)
  • Реализуйте isTransient метод, предоставленный, в спящем режиме перехватчик (Опция, которую я имею),

Таким образом, как способ избежать поведения значения по умолчанию выбора перед сохранением при использовании этого вида расположения каскадом, я улучшил свой код путем присвоения Быть в спящем режиме Перехватчика Быть в спящем режиме Сессии, Транзакция которой организована Spring.

Здесь идет мой репозиторий

Прежде (Ни с кем В спящем режиме Перехватчик): Это хорошо работает!

@Repository
public class SomeEntityRepository extends AbstractRepository {

    @Autowired
    private SessionFactory sessionFactory;

    @Override
    public void add(SomeEntity instance) {
        sessionFactory.getCurrentSession().save(instance);
    }

}

После (С В спящем режиме Inteceptor): что-то идет не так, как надо (Никакой SQL-запрос не выполняется - Ни НЕ ВСТАВЬТЕ, Ни ВЫБОР ПЕРЕД СОХРАНЕНИЕМ),

@Repository
public class SomeEntityRepository extends AbstractRepository {

    @Autowired
    private SessionFactory sessionFactory;

    @Override
    public void add(SomeEntity instance) {
        sessionFactory.openSession(new EmptyInterceptor() {
            /**
              * To avoid select-before-saving
              */
            @Override
            public Boolean isTransient(Object o) {
                return true;
            }
        }).save(instance);
    }

}

Мой вопрос: Почему Spring не сохраняет мой Объект и его отношения, когда использование В спящем режиме Перехватчик и что я должен сделать как обходное решение, чтобы хорошо работать???

6
задан Arthur Ronald 23 June 2010 в 22:08
поделиться

2 ответа

Spring поддерживает связь между текущим сеансом и текущей транзакцией (см. SessionFactoryUtils.java .) Поскольку для текущего вызова метода DAO уже существует сеанс, вы придется использовать этот сеанс или окунуться в непонятные детали связывания нового сеанса с контекстом предыдущей транзакции. Вероятно, это возможно, но со значительным риском и определенно не рекомендуется. В спящем режиме, если у вас уже открыт сеанс, его следует использовать.

Сказав это, вы можете заставить Spring создать для вас новый сеанс и связать его с текущим контекстом транзакции. Используйте SessionFactoryUtils.getNewSession (SessionFactory, Interceptor) . Если вы используете это, а не sessionFactory hibernate, то это должно сохранить связь с транзакцией.

Изначально вы можете закодировать это прямо в DAO.Когда он будет опробован и протестирован и, надеюсь, окажется, что он работает, вы можете предпринять шаги по перемещению кода Spring из вашего DAO, например, использовать АОП для добавления рекомендаций к методам add (), которые создают и очищают новый сеанс.

Другой альтернативой является использование глобального перехватчика. Несмотря на то, что он глобальный, вы можете дать ему локально управляемое поведение. TransientInterceptor содержит threadLocal . Это флаг текущего потока, указывающий, должен ли перехватчик возвращать истину для isTransient . Вы устанавливаете значение true в начале метода add () и очищаете его в конце. Например.

   class TransientInterceptor extends EntityInterceptor {
      ThreadLocal<Boolean> transientFlag = new ThreadLocal<Boolean)();
      public boolean isTransient() {
         return transientFlag.get()==Boolean.TRUE;
      }
      static public setTransient(boolean b) {
          transientFlag.set(b);
      }
   }

А затем в вашем DAO:

@Override
public void add(SomeEntity instance) {
   try {
       TransientInterceptor.set(true);
       sessionFactory.getCurrentSession().save(instance);
   }
   finally {
      TransientInterceptor.set(false);   
   }
}

Затем вы можете настроить TransientInterceptor в качестве глобального перехватчика на SessionFactory (например, LocalSessionFactoryBean .) Чтобы сделать это менее инвазивным, вы можете создать AOP вокруг совета для применения это поведение для всех ваших методов добавления DAO, где это необходимо.

3
ответ дан 17 December 2019 в 18:10
поделиться

В 'after ', вы создаете новый сеанс, а не сбрасываете его, поэтому обновление в базу данных не отправляется. Это не имеет ничего общего со Spring, это чистое поведение Hibernate.

Вы, вероятно, захотите добавить перехватчик (сущность) в sessionFactory, вероятно, настроенный с использованием Spring. Затем вы можете просто сохранить метод add () вашего репозитория, как и раньше. См. http://static.springsource.org/spring/docs/2.5.x/api/org/springframework/orm/hibernate3/LocalSessionFactoryBean.html#setEntityInterceptor%28org.hibernate.Interceptor%29

0
ответ дан 17 December 2019 в 18:10
поделиться
Другие вопросы по тегам:

Похожие вопросы: