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

时间:2016-10-19 13:20:32

标签: java oracle hibernate web-applications concurrency

我需要在Employee表中插入员工我想要的是避免重复插入,即如果thwo线程试图同时插入同一个员工然后上次交易 应该失败。例如,如果first_name和hire_date对于两个员工(来自两个线程的同一员工)是相同的,那么最后一个事务就失败了。

方法1: - 第一种方法我可以考虑将约束置于列级别(如first_name和hire_date上的组合唯一约束)或查询检查是否 员工存在抛出错误(我相信它可以通过PL / SQL实现)

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

public void  save(Employee emp){
    //hibernate api to save
}

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

5 个答案:

答案 0 :(得分:1)

  
    

我想要的是避免重复插入

  

  
    

但它会影响性能,它也会不必要地持有其他交易

  

因此,您需要高度并发的插入,以确保没有重复。

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

如果您对密钥值创建PRIMARY KEYUNIQUE约束,Oracle数据库将自动为您执行此操作。不重复的同时插入不会干扰或等待彼此。但是,如果两个会话同时尝试重复插入,则第二个会话将等到第一个完成。如果第一个会话通过COMMIT完成,那么第二个事务将失败并且索引违例时会出现重复键。如果第一个会话通过ROLLBACK完成,则第二个交易将成功完成。

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

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

大家都说过,这绝对应该在数据库中使用表上的简单PRIMARY KEY约束和适当的错误处理来完成。

答案 1 :(得分:0)

使用数据库的主要原因之一是他们给予一致性。

您正在志愿将部分责任重新纳入您的申请中。这听起来像是错误的做法。相反,您应该准确研究数据库提供的功能;并尝试“尽可能多地使用它们”。

从这个意义上说,你试图在错误的层面上解决问题。

答案 2 :(得分:0)

Pseudo Code :
void save (Employee emp){
   if(!isEmployeeExist(emp)){
       //Hibernate api to save
   }
}

boolean isEmployeeExist(Employee emp){
    // build and run query for finding the employee
    return true; //if employee exists else return false
}

答案 3 :(得分:0)

好问题。在这种情况下,我强烈建议使用MERGE(在单个DML中使用INSERT和UPDATE)。让Oracle处理txn并锁定。在你的情况下,这是最好的。

您应该创建主键,唯一约束(方法1),无论保留数据完整性的任何解决方案。

- 示例声明

MERGE INTO employees e
    USING (SELECT * FROM hr_records) h
    ON (e.id = h.emp_id)
  WHEN MATCHED THEN
    UPDATE SET e.address = h.address
  WHEN NOT MATCHED THEN
    INSERT (id, address)
    VALUES (h.emp_id, h.address);

答案 4 :(得分:0)

由于尚未插入行,因此隔离级别(例如READ_COMMITED / REPEATABLE_READ)将不适用于它们。

最好是应用数据库约束(唯一),如果该约束不存在,则在多节点设置中 您也无法通过Java锁来实现它。作为请求可以去任何节点。 因此,在这种情况下,我们需要具有分布式锁定类型的功能。 我们可以创建一个表锁,在该表锁中,我们可以为每个表定义一个节点或只能插入一个集合。 例如: 表名,已锁定        emp,“ N”

没有任何代码可以在此行上获得READ_COMMITED并尝试将Lock_acuired更新为'Y' 因此,其他线程或其他节点中的任何其他代码将无法继续进行,并且只有在释放先前的锁定时才会给出锁定。 这将确保可以避免重复的高度并发的系统,但是这会遇到可校准性问题。因此,请相应地确定要达到的目标。