Может ли поле с отложенной выборкой быть изменено перед загрузкой?

#jpa #java-ee-6 #openjpa #openejb

#jpa #java-ee-6 #openjpa #openejb

Вопрос:

Я использую встроенный OpenEJB в одном модульном тестировании. Тест не работает. Во время отладки я обнаружил, что поле с отложенной выборкой ведет себя странно.

Действительно ли это возможно? Если поле уже загружено, все идет обычным способом:

 //field == "something from db"
field = "ahoj";
//field == "ahoj"
  

Но если поле не было загружено:

 //field == null
field = "ahoj";
//field == null
  

В стеке вызовов я увидел, что существует какой-то метод какого-то вышележащего слоя сверху, скорее всего, объекта, управляющего им. Я попробовал Google, но ответа не нашел.

Итак, мой вопрос таков: существует ли какое-то правило, согласно которому не выбранное поле управляемой сущности не может быть назначено? И если существует аналогичное правило, как изменить значение поля, не извлекая его из базы данных?

Комментарии:

1. Используете ли вы доступ к полю, как представлено в коде, или установщики / получатели?

2. Я думаю, доступ к полю. Но поле модифицируется внутри одного метода класса entity. Я использую сеттер, но с другим именем. (Не setField() , но setSomeValue() и внутри среди прочих значение должно быть обновлено, но это не так.)

3. Не могли бы вы обновить свой вопрос и показать нам сам ваш класс сущностей?

4. Нет, я тем временем обновил код. Но я уже выяснил правду.

5. можете ли вы поделиться своим решением с остальными участниками класса? 🙂

Ответ №1:

Ответ, конечно, «да». Но что происходило вчера?

Проблема была в контексте персистентности и могла быть также в имени метода. Поле не было обновлено в его установщике. Контекст сохранения связан с транзакцией. Таким образом, если вам нужно обновить и сохранить несколько полей, вы должны выполнить все операции в рамках одной транзакции. Вы должны запустить транзакцию, вызвать установщики для объекта и вызвать метод обновления в сеансовом компоненте, управляющем доступом к объектам.

 @Basic(fetch=FetchType.LAZY)
String field;

@Override
@TransactionAttribute(TransactionAttributeType.SUPPORTS)
public String getFiled() {
    return field;
}

@Override
@TransactionAttribute(TransactionAttributeType.MANDATORY)
public void setText(String text) {
    //other code
    this.field = text;
    //other code
}
  

Средство получения может быть вызвано в транзакции или нет. Но установщик должен быть вызван в рамках существующей транзакции. Тот же атрибут транзакции применяется к методу обновления сессионного компонента. Кроме того, нам нужен метод reattach() , гарантирующий, что мы работаем с объектом в надлежащем контексте сохранения.

 @Stateless
class AccessBean implements Accessor {
    @PersistenceContext(unitName = "MyUnit")
    private EntityManager manager;

    @TransactionAttribute(TransactionAttributeType.MANDATORY)
    public TheEntity update(TheEntity e) {
        e = manager.merge(e);
        manager.flush();

        return e;
    }

    @TransactionAttribute(TransactionAttributeType.MANDATORY)
    public <T> T reattach(T object) {
        return manager.merge(object);
    }
}
  

Все должно быть сделано в рамках одной транзакции.

 Context context = new InitialContext(your properties);
UserTransaction trans = (UserTransaction)context.lookup("java:comp/UserTransaction");
//AccessBean ab already injected;
//TheEntity e already exists

trans.begin(); //this implicates a new persistent context
e = da.reattach(e); //critical line
e.setText("New text");
e = da.updateEvent(e);
trans.commit();
TestCase.assertEquals("New text", e.getField()); //yes!
  

Критическая строка кажется ненужной, но это не так. Если вы прокомментируете эту строку,
вы будете работать с объектом, отсоединенным или присоединенным к другому постоянному контексту.
Тогда вы увидите забавное поведение, которое было предметом моего вопроса.

Примечание: До сих пор нам также необходимо получать данные в рамках явной транзакции.

 trans.begin();
e = da.reattach(e);
String s = e.getField();
trans.commit();