#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();