Спящий режим Сохранить объект для нескольких сеансов
Я пытаюсь написать несколько баз данных, используя спящий режим. У меня есть инкапсулированные сессии записи и чтения/записи в одном объекте сеанса. Однако, когда я иду на сохранение, я получаю много ошибок, которые объекты уже связаны с другим сеансом: "Незаконная попытка связать коллекцию с двумя открытыми сеансами"
Вот мой код:
public class MultiSessionObject implements Session {
private Session writeOnlySession;
private Session readWriteSession;
@Override
public void saveOrUpdate(Object arg0) throws HibernateException {
readWriteSession.saveOrUpdate(arg0);
writeOnlySession.saveOrUpdate(arg0);
}
}
Я попытался вырезать объект и промыть; однако это вызывает проблемы с "Row был обновлен или удален другой транзакцией"... хотя обе сессии указывают на разные базы данных.
public class MultiSessionObject implements Session {
private Session writeOnlySession;
private Session readWriteSession;
@Override
public void saveOrUpdate(Object arg0) throws HibernateException {
readWriteSession.saveOrUpdate(arg0);
readWriteSession.flush();
readWriteSession.evict(arg0);
writeOnlySession.saveOrUpdate(arg0);
writeOnlySession.flush();
writeOnlySession.evict(arg0);
}
}
В дополнение к вышесказанному, я также попытался использовать функции репликации спящего режима. Это также было безуспешным без ошибок.
Кто-нибудь успешно сохранил объект в двух базах данных, имеющих одну и ту же схему?
Ответы
Ответ 1
saveOrUpdate
пытается повторно привязать данный Entity к текущему сеансу, поэтому Proxies (ассоциации LAZY) привязаны к сеансу Hibernate. Попробуйте использовать merge вместо saveOrUpdate, потому что merge
просто копирует состояние отдельного объекта в вновь полученный управляемый объект. Таким образом, предоставленные аргументы никогда не присоединяются к сеансу.
Другой проблемой является управление транзакциями. Если вы используете транзакцию с привязкой к потоку, вам нужны две явные транзакции, если вы хотите обновить два источника данных из одного и того же потока.
Попробуйте также установить границы транзакций явно:
public class MultiSessionObject implements Session {
private Session writeOnlySession;
private Session readWriteSession;
@Override
public void saveOrUpdate(Object arg0) throws HibernateException {
Transaction readWriteSessionTx = null;
try {
readWriteSessionTx = readWriteSession.beginTransaction();
readWriteSession.merge(arg0);
readWriteSessionTx.commit();
} catch (RuntimeException e) {
if ( readWriteSessionTx != null && readWriteSessionTx.isActive() )
readWriteSessionTx.rollback();
throw e;
}
Transaction writeOnlySessionTx = null;
try {
writeOnlySessionTx = writeOnlySession.beginTransaction();
writeOnlySession.merge(arg0);
writeOnlySessionTx.commit();
} catch (RuntimeException e) {
if ( writeOnlySessionTx != null && writeOnlySessionTx.isActive() )
writeOnlySessionTx.rollback();
throw e;
}
}
}
Ответ 2
Как упоминалось в других ответах, если вы используете Session
, вам, вероятно, придется отделить 2 обновления и две разные транзакции. Отделимый экземпляр объекта (после evict
) должен быть в состоянии повторно использоваться во второй операции обновления.
Другим подходом является использование StatelessSession
как это (я пробовал простую программу, поэтому мне пришлось обрабатывать транзакции. Я предполагаю, что вы должны обрабатывать транзакции по-разному)
public static void main(final String[] args) throws Exception {
final StatelessSession session1 = HibernateUtil.getReadOnlySessionFactory().openStatelessSession();
final StatelessSession session2 = HibernateUtil.getReadWriteSessionFactory().openStatelessSession();
try {
Transaction transaction1 = session1.beginTransaction();
Transaction transaction2 = session2.beginTransaction();
ErrorLogEntity entity = (ErrorLogEntity) session1.get(ErrorLogEntity.class, 1);
entity.setArea("test");
session1.update(entity);
session2.update(entity);
transaction1.commit();
transaction2.commit();
System.out.println("Entry details: " + entity);
} finally {
session1.close();
session2.close();
HibernateUtil.getReadOnlySessionFactory().close();
HibernateUtil.getReadWriteSessionFactory().close();
}
}
Проблема с StatelessSession
заключается в том, что он не использует кеш и не поддерживает каскадирование связанных объектов. Вам нужно обработать это вручную.
Ответ 3
Да,
Проблема в том, что она говорит вам. Путь к успешному достижению этого состоит в том, чтобы рассматривать его как 2 разные вещи с двумя разными коммитами.
Создайте composite Дао. В нем у вас есть
Collection<Dao>
Каждый из этих Dao в коллекции является всего лишь экземпляром вашего существующего кода, настроенного для двух разных источников данных. Затем, в вашем составном dao, когда вы вызываете save, вы фактически независимо сохраняете оба.
Внеполосный вы сказали, что это лучшее усилие. Итак, это достаточно легко. Используйте spring -retry, чтобы создать точку, обрезающую ваши индивидуальные методы сохранения dao, чтобы они пытались несколько раз. В конце концов сдайтесь.
public interface Dao<T> {
void save(T type);
}
Создайте новые экземпляры этого приложения с помощью applicationContext.xml, где каждый экземпляр указывает на другую базу данных. Пока вы используете spring -retry, чтобы воспроизвести точку повтора вокруг вашего метода сохранения. Перейдите в конец примера приложения.
public class RealDao<T> implements Dao<T> {
@Autowired
private Session session;
@Override
public void save(T type) {
// save to DB here
}
}
Композитный
public class CompositeDao<T> implements Dao<T> {
// these instances are actually of type RealDao<T>
private Set<Dao<T>> delegates;
public CompositeDao(Dao ... daos) {
this.delegates = new LinkedHashSet<>(Arrays.asList(daos));
}
@Override
public void save(T stuff) {
for (Dao<T> delegate : delegates) {
try {
delegate.save(stuff);
} catch (Exception e) {
// skip it. Best effort
}
}
}
}
Каждый "материал" сохраняется в отдельном сеансе или нет. Поскольку сеанс находится в экземплярах "RealDao", вы знаете, что к тому моменту, когда первое завершено, оно полностью сохранилось или провалилось. Спящий режим может потребовать, чтобы у вас был другой идентификатор для того, чтобы хэш/равные были разными, но я так не думаю.