使用多个线程的JPA持久性

时间:2011-06-28 23:07:06

标签: java multithreading hibernate orm jpa-2.0

当我尝试使用多线程来保持对象时出现问题。

详细信息:

假设我有一个对象PaymentOrder,其中包含PaymentGroup(一对多关系)列表,PaymentGroup包含CreditTransfer列表(一对多关系) )。

由于CreditTransfer的数量很大(以十万分之一为单位),我根据PaymentGroup对其进行了分组(基于某些业务逻辑) 并创建 WORKER 线程(每个PaymentGroup一个线程)以形成PaymentOrder个对象并在数据库中提交。

问题是,每个工作线程正在创建一个PaymentOrder(其中包含一组唯一的PaymentGroup)。

所有权利的主键都是自动生成的。

所以有三个表,1。PAYMENT_ORDER_MASTER,2。PAYMENT_GROUPS,3。CREDIT_TRANSFERS,全部由一对多关系映射。

因此,当第二个线程试图将其组持久保存在数据库中时,框架会尝试持久保存前一个线程提交的相同PaymentOrder,由于某些其他唯一的字段限制(事务的校验和),事务会失败PaymentOrder)。

理想情况下,它必须是1..n..m(PaymentOrder - > PaymentGroup --> CreditTransfer`)

我需要实现的是,如果数据库中没有PaymentOrder的条目进行输入,如果有条目,则不要在PAYMENT_ORDER_MASTER中输入,而只能在PAYMENT_GROUPS中输入{ {1}}。

如何维护split-master-payment-order-using-groups逻辑和多个线程来解决这个问题?

2 个答案:

答案 0 :(得分:2)

你有选择。
1)原始但很简单,在最后捕获密钥违规错误并在没有父母的情况下重试插入。假设你的父母是真正独一无二的,你知道另一个线索只是做了父母......继续孩子。与其他选项相比,这可能表现不佳,但也许你得到了你需要的流行音乐。如果你有一个孩子的父母有很高的百分比,那就可以很好地工作。

2)更改您的阅读一致性级别。它是特定于供应商的,但您有时可以读取未提交的事务。这可以帮助您在提交之前查看其他线程的工作。这不是万无一失的,你仍然必须做#1,因为另一个线程可以在读取后潜入。但它可能会以更复杂的代价提高您的吞吐量。可能是不可能的,基于RDBMS(或者它可能发生,但仅在数据库级别,搞乱其他应用程序!)

3)使用单线程使用者实现工作队列。如果程序的主要昂贵工作在持久性级别之前,则可以让您的线程将其数据“插入”到不强制执行密钥的工作队列中。然后从工作队列中拉出一个线程并保持不变。工作队列可以位于内存,另一个表或特定于供应商的位置(Weblogic队列,Oracle AQ等)。如果程序的主要工作是在持久性之前,那么你可以并行化THAT并返回插入的单个线程。您甚至可以让您的消费者以“批量插入”模式工作。 Sweeeeeeeet。

4)放松你的约束。如果同一个孩子有两个父母持有相同的信息,谁真的在乎呢?我只是问问。如果您以后不需要父信​​息的超快速更新,并且您可以更改您的阅读程序以理解它,它可以很好地工作。它不会让你在数据库设计课上成为“A”,但如果它有效......

5)实施一个傻瓜锁表。我讨厌这个解决方案,但它确实有效 - 让你的线程写下它正在处理父“x”而没有其他人可以作为它的第一个事务(和提交)。通常会导致相同的问题(以及其他问题 - 稍后清理记录等),但是当子插入缓慢且单行插入速度很快时可以工作。你仍然会发生碰撞,但会更少。

答案 1 :(得分:1)

Hibernate会话不是线程安全的。支持Hibernate的JDBC连接不是线程安全的。考虑多线程化您的业务逻辑,以便每个线程都使用它自己的Hibernate会话和JDBC连接。通过使用线程池,您可以通过添加限制同时线程数的功能来进一步改进代码。