Как использовать Hibernate в многопоточном приложении?

Я пытаюсь использовать Hibernate для многопоточного приложения, в котором каждый поток извлекает объект и пытается вставить его в таблицу. Мой код выглядит ниже. У меня есть локальные спящие объекты сеанса на поток, и в каждой InsertData я начинаю транзакцию и фиксирую.

Проблема, с которой я сталкиваюсь, заключается в том, что много раз я получаю "org.hibernate.TransactionException: вложенные транзакции не поддерживаются"

Поскольку я новичок в спящем режиме, я не знаю, правильно ли я это делаю или нет? Пожалуйста, дайте мне знать, каков правильный способ использования спящего режима в многопоточном приложении и как избежать вышеупомянутого исключения.

Спасибо

public class Worker extends Thread {
private Session session = null;

Worker() {
    SessionFactory sf = HibernateUtil.getSessionFactory(); // Singleton
    session = sf.openSession();
    session.setFlushMode(FlushMode.ALWAYS);
}

public void run() {
    // Some loop which will run thousand of times 
    for (....)
    {
        InsertData(b);
    }
    session.close();
}

// BlogPost Table has (pk = id AutoGenerated), dateTime, blogdescription etc. 
private void InsertData(BlogPost b) {
    session.beginTransaction();
    Long id = (Long) session.save(b);
    b.setId(id);
    session.getTransaction().commit();
}
}

Мой конфигурационный файл hibernate имеет c3p0.min_size=10 и c3p0.max_size=20

Ответ 1

С session-objects-per-thread, если вы не используете объекты сеанса между несколькими потоками, вы будете в порядке.

Ошибка, которую вы получаете, не связана с вашим многопоточным использованием или вашим управлением сеансом. Ваше использование session.save(), а также явно задание ID не совсем корректно.

Не видя вашего сопоставления для BlogPost, его сложно сказать, но если вы сказали Hibernate использовать поле id в качестве первичного ключа, и вы используете собственный генератор для первичных ключей, все, что вам нужно do:

session.beginTransaction();
session.persist(b);
session.flush(); // only needed if flush mode is "manual"
session.getTransaction().commit();

Hibernate заполнит идентификатор для вас, persist() приведет к тому, что вставка произойдет в рамках транзакции (save() не заботится о транзакциях). Если ваш режим флеша не настроен на ручной, вам не нужно вызывать flush(), поскольку Transaction.commit() будет обрабатывать это для вас.

Обратите внимание, что при persist() идентификатор BlogPost не будет установлен до тех пор, пока сеанс не будет очищен, что отлично подходит для вашего использования здесь.

Для корректной обработки ошибок:

try {
    session.beginTransaction();
    try {
        session.persist(b);
        session.flush(); // only needed if flush mode is "manual"
        session.getTransaction().commit();
    } catch (Exception x) {
        session.getTransaction().rollback();
        // log the error
    }
} catch (Exception x) {
    // log the error
}

Кстати, я предлагаю сделать private BlogPost.setId() или пакет видимым. Скорее всего, это ошибка реализации, если другой класс явно устанавливает идентификатор (опять же, предполагая собственный генератор, а id - как первичный ключ).