Спящий режим Сохранить объект для нескольких сеансов

Я пытаюсь написать несколько баз данных, используя спящий режим. У меня есть инкапсулированные сессии записи и чтения/записи в одном объекте сеанса. Однако, когда я иду на сохранение, я получаю много ошибок, которые объекты уже связаны с другим сеансом: "Незаконная попытка связать коллекцию с двумя открытыми сеансами"

Вот мой код:

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