Java JBDC-跨并发线程以原子方式执行SELECT和INSERT

Java JBDC-跨并发线程以原子方式执行SELECT和INSERT,java,multithreading,jdbc,Java,Multithreading,Jdbc,我在网上搜索了一些简单的例子,但没有找到。我需要在Java中以原子单元的形式运行选择和插入操作,在Oracle数据库中使用JDBC 实际上,我需要做以下工作: 从用户中选择代码 通过所有代码,直到找到一个未被使用的代码,因为用户可以被删除。 插入具有该可用代码的新用户 这通常是一个简单的操作,但由于我的应用程序是多线程的,所以我不确定该如何操作。因为同时运行的并发线程可以使用相同的代码值进行尝试和插入 我可以想出一些解决方法或黑客来完成这项工作,但一般来说,我如何锁定表以使此操作原子化?我看到的

我在网上搜索了一些简单的例子,但没有找到。我需要在Java中以原子单元的形式运行选择和插入操作,在Oracle数据库中使用JDBC

实际上,我需要做以下工作:

从用户中选择代码 通过所有代码,直到找到一个未被使用的代码,因为用户可以被删除。 插入具有该可用代码的新用户 这通常是一个简单的操作,但由于我的应用程序是多线程的,所以我不确定该如何操作。因为同时运行的并发线程可以使用相同的代码值进行尝试和插入


我可以想出一些解决方法或黑客来完成这项工作,但一般来说,我如何锁定表以使此操作原子化?我看到的大部分内容都涉及行锁,但由于我没有更新,所以我不知道这是如何应用的

这是一个很难完全用SQL完成的问题。任何解决方案都会有竞争条件问题。如果我要完全用SQL来做,我会使用一个已删除的代码表。当用户被删除时,您将使用一些服务将其代码添加到已删除的表中。如果删除的代码表为空,线程将使用序列号获取新代码。由于get和set具有多个SQL操作的性质,从已删除的数据库中获取代码需要位于同步块中。我认为SQL事务在这方面没有帮助。它们可以保持数据的一致性,但是如果两个线程使用相同的代码,那么两个提交中的一个将抛出异常

我认为一个更好、更快的机制是让一个单独的线程管理这些删除的代码。它可以将其写入数据库,但也可以保留一个已删除代码的阻塞队列,供其他线程使用。如果必须没有漏洞,并且您担心崩溃,那么它需要在启动时查询用户表来验证可用漏洞的列表。它不需要同步或执行任何SQL事务,因为只有它会从已删除的代码表中删除


希望这有帮助。

我倾向于将逻辑放入存储过程中。使用“选择更新”锁定,然后使用“提交”解锁

您可以在insert语句中添加筛选器,然后在客户端重试逻辑,我想:

确定可用的代码和建议的代码 使用筛选器执行插入确定executeUpdate结果中的行数0表示并发线程捕获了此代码,请重试 插入将沿着以下几行进行查看,其中3是您的新id,“Joe”是您的新用户,并建议编码您认为可用的代码:

INSERT INTO users
  SELECT 3, :proposedCode, 'Joe' 
    FROM dual
    WHERE :proposedCode NOT IN (SELECT code FROM users)
那么:

insert into usertable (
  id, 
  code, 
  name
) values (
  user_id_sequence.nextval,
  (
      select min(newcode) 
        from usertable, (
           select level newcode 
             from dual 
          connect by level <= (select max(code)+1 from usertable))
       where not exists (select 1 from usertable where code = newcode)
  ),
  'mynewusername'
)
编辑:


已更改为maxcode+1,因此如果没有可用间隙,则会有一个新代码可用。

为什么不使用“插入-选择”方式。我不确定它是否符合您的要求,因为我不知道表的结构。但我认为如果你能选择这样一种方式,它将用于插入,那么你的问题将得到解决。嗨。我认为这行不通。我的桌子结构基本上是这样的。身份证,代码,姓名,说数据是1,1,John;2,2,弗兰克;4,4,Bob所以我想做一个select来发现代码3没有被使用,然后做一个insert值5,3,Bill并发多个线程可能会得到3作为下一个可用的代码,如果select和insert不是原子的,那么第二个线程在尝试插入时会失败,如果只想在Java端执行逻辑,可以使用可更新的ResultSet。“如果我错了,请纠正我。”老实说,我不知道。我已经好几年没试过了。我通常会将这种逻辑放入存储过程中。这样,api就可以用于任何类型的客户机,而无需客户机担心实现细节,也无需服务器端担心客户机可能如何实现。对于CRUD类型的应用程序,我会在表上放置一个version字段,进行读取,然后在回写修改后的记录时,添加一个WHERE version=。如果没有行被更新,msg将向用户提示并发更新失败。可能应该有一个包含所有代码的表,其中有一个布尔分配字段。然后获取代码将是更新代码集allocated=true,其中code=select mincode from codes where allocated=false将代码返回到。。。。如果代码有意义,那么可能应该列出它们。如果它们没有意义,那么就不必担心差距……谢谢@Glenn。我不确定如何从该语句中得到代码结果,但类似的东西会起作用。即使使用它,线程也将循环,直到更新行为止。我仍然认为分配器线程是一个不错的选择。