java - Hibernate:从不同线程插入具有唯一约束的对象

标签 java multithreading hibernate concurrency

我有以下问题。我有 10 个线程创建插入数据库的对象。每个 Thread 都有一个 ThreadLocal 和它自己的 session 。所有对象在创建后一起插入。这些对象有一个标记为唯一的列。但是,我有一个问题,两个不同的线程可能会创建同一个对象。这种行为是需要的,但我不知道如何将它们插入到我的数据库中。

目前,每个线程查询所有插入到数据库中的对象,检查查询到的对象是否存在,并将不存在的对象插入到数据库中。但是,由于该对象可能不存在于所有对象的查询中,因此当我插入对象并且它们已被另一个线程添加时,我得到了一个ConstraintViolationException。然而,对每个对象进行数据库(或缓存)查询会降低性能,因为我们试图在每线程和每分钟添加 1000 个对象。如果我尝试在每次插入后刷新数据库,则会收到以下错误:尝试获取锁时发现死锁;尝试重启交易

所以我的问题是:如何同时插入具有来自不同线程的唯一约束的对象。

//编辑:目前我正在使用 Hibernate 和 MYSQL InnoDB

//Edit2:最后是我用来写单品的代码。

public class ItemWriterRunnable implements Callable<Object> {

    private final ThreadLocal<Session> session = new ThreadLocal<Session>();

    private Item item;

    public ItemWriterRunnable(Item item) {
        super();
        this.item= item;
    }

    protected Session currentSession() {
        Session s = this.session.get();
        // Open a new Session, if this thread has none yet
        if (s == null || !s.isOpen()) {
            s = HibernateUtils.getSessionFactory().openSession();
            // Store it in the ThreadLocal variable
            this.session.set(s);
        }
        return s;
    }

    @Override
    public Object call() throws Exception {
        Session currentSession = currentSession();
        try {
            currentSession.beginTransaction();
            currentSession.save(this.item);
            currentSession.getTransaction().commit();
        } catch (ConstraintViolationException e) {
            currentSession.getTransaction().rollback();
        } catch (RuntimeException e) {
            currentSession.getTransaction().rollback();
        } finally {
            currentSession.close();
            currentSession = null;
            this.session.remove();
        }
        return null;
    }
}

最好的问候, 安德烈

最佳答案

如果你在一个线程中写入多个对象,其中一个因为重复而失败,那么你将不得不找出哪个是重复的,从集合中删除它,然后重试将它写入数据库(随着另一个失败的改变)。这需要很多时间。或者,您可以在写入集合之前读取数据库以查看是否有任何重复项,并在写入之前删除重复项。如果不包含在同步块(synchronized block)中,这种读取/检查/写入模式是有缺陷的,因为其他线程可能会在步骤之间写入重复项。修复此问题所需的同步将在每次写入时停止您的服务器,暂停所有现有线程,可能会损害性能。

相反,为每个对象生成一个线程,并在该线程中写入对象(不进行读取/检查)。大多数对象将毫无问题地写入,因为大多数对象都没有重复(一个假设,但它可能是正确的)。重复的对象将因异常而失败,此时您可以终止该线程,因为相关工作已经完成。

关于java - Hibernate:从不同线程插入具有唯一约束的对象,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24766184/

相关文章:

c# - C# 中的多线程启动画面?

Android/Linux 线程加入超时

xml - 正则表达式过滤器 log4j2

java - hibernate4.1+spring3.2+boneCP

Java RMI : Determining Caller process from Callee

java - JButton 需要更改 JTextfield 文本

java - Tomcat 数据库连接,我这样做对吗?

Java ServiceExecutor 终止条件

java - 从另一个包添加实体时为 "QuerySyntaxException: User is not mapped"

java - 是否可以从 Web 浏览器访问 JAX-WS?