#hibernate #jpa #dropwizard
#переход в спящий режим #jpa #dropwizard
Вопрос:
Я использую Dropwizard Hibernate, и у меня возникают проблемы с моими тестами. Я максимально упростил этот пример. Я создаю Foo
, обновляю его, а затем пытаюсь извлечь его. Использование необработанного запроса дает правильный результат, но эквивалентный запрос CriteriaBuilder не улавливает обновление. Что я делаю не так?
@Test
public void testFoo() {
String id = "12345";
// Create
Foo foo = Foo.builder()
.id(id)
.name("old-name")
.build();
sessionFactory.getCurrentSession().replicate(foo, ReplicationMode.EXCEPTION);
// Update
database.inTransaction(() -> {
CriteriaBuilder cb = sessionFactory.getCurrentSession().getCriteriaBuilder();
CriteriaUpdate<Foo> update = cb.createCriteriaUpdate(Foo.class);
Root<Foo> root = update.from(Foo.class);
update.set(Foo_.name, "new-name");
update.where(cb.equal(root.get(Foo_.id), id));
int updated = sessionFactory.getCurrentSession().createQuery(update).executeUpdate();
});
// Select
database.inTransaction(() -> {
sessionFactory.getCurrentSession().flush(); // Not sure if this matters
String newName = (String) sessionFactory.getCurrentSession()
.createQuery("select name from Foo where id=:id")
.setParameter("id", id)
.getSingleResult();
assertEquals("new-name", newName);
log.error("New name is " newName);
CriteriaBuilder cb = sessionFactory.getCurrentSession().getCriteriaBuilder();
CriteriaQuery<Foo> cq = cb.createQuery(Foo.class);
Root<Foo> root = cq.from(Foo.class);
cq.where(cb.equal(root.get(Foo_.id), id));
Query query = sessionFactory.getCurrentSession().createQuery(cq);
Foo foo2 = (Foo) query.getSingleResult();
log.error("New name is " foo2.getName()); // Prints "old-name"
});
}
Вот мой установочный код:
@ExtendWith(DropwizardExtensionsSupport.class)
public class UpdateTest {
private SessionFactory sessionFactory;
public DAOTestExtension database = DAOTestExtension.newBuilder()
.addEntityClass(Foo.class)
.build();
@BeforeEach
public void setup() {
sessionFactory = database.getSessionFactory();
}
...
}
Я также могу показать Foo
класс, но это не очень интересно.
Комментарии:
1. Что такое Foo_.id ? является ли подчеркивание опечаткой?
2. @Guillaume Это называется статической метамоделью: docs.jboss.org/hibernate/orm/5.0/topical/html/metamodelgen /… . Foo._id эквивалентно «id» здесь.
Ответ №1:
Foo
Экземпляр все еще находится в кэше L1, т. е. в текущем сеансе, который не затрагивается инструкцией update. Поскольку Hibernate / JPA должен сохранять идентификацию для объектов entity сеанса, он не может очистить сеанс из-за инструкции update . Обычно требуется очистить сеанс или обновить экземпляры после инструкции update, чтобы снова отразить текущее состояние.
Попробуйте вместо этого выполнить sessionFactory.getCurrentSession().clear();
в начале вашей транзакции чтения
Комментарии:
1. Спасибо, это сработало! Таким образом, обновления в сеансе (либо через необработанный запрос, либо через
CriteriaBuilder
) не будут отражены в последующих запросах, если яclear()
? Тогда это обычный шаблон дляclear()
перед каждым запросом? Знакомы ли вы с какими-либо ресурсами, которые подробно объясняют все это? (Быстрый поиск в Google выдал множество незавершенных статей.)2. Извините, если мой последний комментарий был непонятен. Я попытался выполнить обновление с помощью
CriteriaUpdate
, ожидая, что оно коснется кэша (см. Обновленный код). Но я получаю тот же результат. Это ожидаемо? Если это так, я не понимаю, как должен работать кэш!3. Проблема в том, что инструкции JPQL / SQL DML не влияют на объекты entity непосредственно в кэше L1. Если вы запрашиваете объекты после выполнения DML, Hibernate по праву сохранит идентификатор объекта и вместо загрузки состояния из результата запроса просто вернет вам объект из кэша L1, если он доступен. Если вы выполняете инструкции DML, вам следует очистить сеанс, если вы хотите запрашивать объекты с тем же сеансом. Обратите внимание, что обычно это не проблема, потому что сеансы легкие и должны быть недолговечными, т. Е. только для одной транзакции.
4. Спасибо, Кристиан, но я нигде не делаю обновления DML, не так ли? Я полагаю, вы видели мой последний комментарий?
5. Ха, спасибо! Очевидно, я был сбит с толку тем, что представляет собой оператор DML. Если я вызываю session.update() напрямую, это работает так, как ожидалось. Большое спасибо!