Перезагрузка объектов из базы данных, а не кеша, после базы данных обновлений хранимых процедур?

Я использую 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, прежде чем выдворять их, когда все, что я действительно хочу сделать, - выселить любые текущие экземпляры сеанса.

Я не вижу способа очистки кеша первого уровня от всех объектов определенного типа, поэтому какие другие решения доступны?

Ответы

Ответ 2

Почему бы вам просто не выселить объект учетной записи, прежде чем читать его снова из базы данных? Я бы сделал это:

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