Ответ 1
Вы можете использовать метод session.refresh(). См. 11.3. Загрузка объекта в документацию.
Я использую Hibernate для доступа к базе данных Oracle.
Я думаю, что у меня проблемы с кэшем первого уровня (или сеансом) Hibernate. У меня есть таблицы, представляющие учетные записи: таблица ACCOUNT, таблица INVOICE и таблица PAYMENTS. В базе данных Oracle есть процедуры, поэтому добавление PAYMENT будет автоматически обновлять столбцы в связанных таблицах INVOICE и ACCOUNT.
Проблема заключается в том, что я использую Hibernate для выполнения следующих действий:
Account account = accountDao.get(accountId);
assertEquals(0.00, account.getBalance());
// Saving a payment will trigger stored procedures that
// will update the account balance.
paymentDao.save(createPaymentForAccount(accountId, 20.00));
account = accountDao.get(accountId);
assertEquals(20.00, account.getBalance());
Окончательное утверждение не будет выполнено, потому что account.getBalance()
возвращает 0.00
, а не 20.00
.
Я хочу, чтобы второй вызов accountDao.get(...)
попал в базу данных и получил новый объект ACCOUNT. Но Hibernate, похоже, возвращает объект учетной записи, уже находящийся в кеше (когда я проверяю вывод отладки для вызова поиска, я вижу number of objects hydrated: 0
).
Я предполагаю, что Hibernate не знает, что база данных изменилась из-за вызова хранимой процедуры, поэтому он использует объект в своем кеше.
Итак, я начал думать о решениях. Один из них - удалить любые объекты ACCOUNT и PAYMENT из кеша сеанса гибернации всякий раз, когда ПЛАТЕЖ сохранена. Это приведет к извлечению базы данных (с обновленными значениями) для любой операции ACCOUNT или INVOICE.
Я попробовал следующее:
public void save(Payment payment) {
getSession().persist(payment);
getSessionFactory().evict(Invoice.class);
getSessionFactory().evict(Account.class);
}
Но журнал журнала гибернации показал, что ничего не произошло. Я думаю, что sessionFactory.evict(...)
работает на кэше второго уровня, который не включен и поэтому ничего не выселить.
Затем я попытался вырезать все объекты ACCOUNT и INVOICE из кеша сеанса, выставив каждый экземпляр, который мог найти:
public void save(Payment payment) {
getSession().persist(payment);
for (Invoice invoice: lookupInvoices()) { // e.g. "from Invoice" query
getSession().evict(invoice);
}
for (Account account: lookupAccounts()) { // e.g. "from Account" query
getSession().evict(account);
}
}
Кажется, что это работает, но ужасно неэффективно, потому что он загружает все экземпляры в кеш сеанса hibernate, прежде чем выдворять их, когда все, что я действительно хочу сделать, - выселить любые текущие экземпляры сеанса.
Я не вижу способа очистки кеша первого уровня от всех объектов определенного типа, поэтому какие другие решения доступны?
Вы можете использовать метод session.refresh(). См. 11.3. Загрузка объекта в документацию.
Почему бы вам просто не выселить объект учетной записи, прежде чем читать его снова из базы данных? Я бы сделал это:
Account account = accountDao.get(accountId);
assertEquals(0.00, account.getBalance());
// Saving a payment will trigger stored procedures that
// will update the account balance.
paymentDao.save(createPaymentForAccount(accountId, 20.00));
accountDao.getSession().evict(account)
account = accountDao.get(accountId);
assertEquals(20.00, account.getBalance());
Также вы должны убедиться, что у вас нет уровня изоляции REPEATABLE_READ. В этом режиме изоляции Spring гарантирует, что два чтения в одной транзакции всегда будут возвращать один и тот же результат. Поскольку уровень изоляции имеет приоритет при выселении кеша, выселение кеша не будет иметь видимого эффекта.
Есть способы обойти это поведение: Объявите уровень изоляции транзакций READ_UNCOMMITTED или READ_COMMITTED. Если вы используете стандартный диалект, вы можете столкнуться с известным стандартом "Стандартная JPA не поддерживает особые уровни изоляции". В этом случае вы можете применить следующее обходное решение: spring 3.3: http://amitstechblog.wordpress.com/2011/05/31/supporting-custom-isolation-levels-with-jpa/ spring 3.2: http://shahzad-mughal.blogspot.com/2012/04/spring-jpa-hibernate-support-for-custom.html