如何避免通过Java在DB中重复插入?

emi*_*lly 5 java oracle concurrency hibernate web-applications

我需要在员工表中插入员工我想要的是避免重复插入,即如果两个线程尝试同时插入同一个员工,那么最后一个事务应该失败。例如,如果两个员工(来自两个线程的同一员工)的 first_name 和hiring_date 相同,则最后一个事务失败。

方法 1:-我能想到的第一种方法是将约束放在列级别(例如 first_name 和hiring_date 上的组合唯一约束)或在查询中检查员工是否存在抛出错误(我相信这可以通过 PL/SQL 实现)

方法 2:-是否也可以在 Java 级别完成,例如创建一个首先检查员工是否存在然后抛出错误的方法。在这种情况下,我需要使方法同步(或同步块),但它会影响性能,它也会不必要地保持其他事务。有没有办法可以放置锁(可重入锁)或使用基于名称/雇用日期的同步方法,以便只有那些具有相同名称和雇用日期的特定事务被搁置

public void  save(Employee emp){
    //hibernate api to save
}
Run Code Online (Sandbox Code Playgroud)

我认为应该首选方法 1,因为它简单且易于实现。对 ?即使是,我想知道它是否可以在 Java 级别有效处理?

Mat*_*eak 4

我想要的是避免重复插入

但这会影响性能,并且会不必要地保留其他交易

因此,您需要高度并发的插入来保证不重复。

无论是在 Java 中还是在数据库中执行此操作,避免重复插入的唯一方法是序列化(或者用 Java 来说,同步)。也就是说,让一项事务等待另一项事务。

如果您对键值创建PRIMARY KEYor约束,Oracle 数据库将自动为您执行此操作。UNIQUE不重复的同时插入不会相互干扰或等待。但是,如果两个会话同时尝试重复插入,第二个会话将等待第一个会话完成。如果第一个会话通过 完成COMMIT,则第二个事务将失败,并因索引违规而出现重复键。如果第一个会话通过 完成ROLLBACK,第二个事务将成功完成。

您也可以在 Java 中执行类似的操作,但问题是您需要一个所有会话都可以访问的锁定机制。 synchronize仅当所有会话都在同一个 JVM 中运行时,类似的替代方案才有效。

此外,在 Java 中,最大化并发性和最小化等待的关键是仅等待实际的重复项。您可以通过对传入的键值进行散列,然后仅在该散列上进行同步来实现类似的效果。也就是说,例如将 65,536 个对象放入列表中。然后,当要进行插入时,将传入的键值散列为 1 到 65536 之间的数字。然后从列表中获取该对象并对其进行同步。当然,您也可以同步实际的键值,但哈希通常同样好并且更容易使用,特别是当传入的键值难以处理或敏感时。

总而言之,这绝对应该在数据库中使用PRIMARY KEY表上的简单约束和适当的错误处理来完成。