java 使用 JPA 防止违反唯一约束的最佳方法
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/5117783/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Best way to prevent unique constraint violations with JPA
提问by kostja
I have an Keyword
and a KeywordType
as entities. There are lots of keywords of few types.
When trying to persist the second keyword of a type, the unique constraint is violated and the transaction is rolled back.
Searching SO i found several possibilies (some of them from different contexts, so I'm not sure of their validity here) - this postand this postadvise catching the Exception which would be of no use to me as I end up where I started and still need to somehow persist the keyword.
Same applies to locking proposed for a different situaltion here
Custom insert statements as proposed in thisand thisposts wouldn't work proper I guess, since I'm using Oracle and not MySQL and woulnd like to tie the implementation to Hibernate.
A different workaround would be trying to retrieve the type first in the code generating the keywords, and set it on the keyword if found or create a new one if not.
So, what would be the best - most robust, portable (for different databases and persistence providers) and sane approach here?
我有一个Keyword
和一个KeywordType
作为实体。有很多类型的关键字。
当尝试持久化类型的第二个关键字时,违反了唯一约束并回滚了事务。
搜索 SO 我发现了几种可能性(其中一些来自不同的上下文,所以我不确定它们在这里的有效性)-这篇文章和这篇文章建议捕获异常,因为我最终到达了我开始的地方并且仍然需要以某种方式保留关键字。
同样适用于提出了不同的situaltion锁定在这里
定制的INSERT语句中提出的这个和这个我猜帖子不会正常工作,因为我使用的是 Oracle 而不是 MySQL,并且希望将实现与 Hibernate 联系起来。
另一种解决方法是尝试在生成关键字的代码中首先检索类型,如果找到则将其设置在关键字上,否则创建一个新的。
那么,什么是最好的——最健壮、最便携(对于不同的数据库和持久性提供者)和理智的方法?
Thank you.
谢谢你。
The involved entities:
涉及的实体:
public class Keyword {
@Id
@GeneratedValue
private long id;
@Column(name = "VALUE")
private String value;
@ManyToOne
@JoinColumn(name = "TYPE_ID")
private KeywordType type;
...
}
and
和
@Entity
@Table(uniqueConstraints = {@UniqueConstraint(columnNames = { "TYPE" }) })
public class KeywordType {
@Id
@GeneratedValue
private long id;
@Column(name = "TYPE")
private String type;
...
}
采纳答案by JB Nizet
Your last solution is the right one, IMO. Search for the keyword type, and if not found, create it.
你的最后一个解决方案是正确的,IMO。搜索关键字类型,如果未找到,则创建它。
Catching the exception is not a good option because
捕获异常不是一个好的选择,因为
- it's hard to know which exception to catch and make your code portable across JPA and DB engines
- The JPA engine will be in an undetermined state after such an exception, and you should always rollback in this case.
- 很难知道要捕获哪个异常并使您的代码在 JPA 和 DB 引擎之间可移植
- JPA 引擎在发生此类异常后将处于不确定状态,在这种情况下您应该始终回滚。
Note however that with this technique, you might still have two transactions searching for the same type in parallel, and then try to insert it in parallel. One of the transaction will rollback, but it will be much less frequent.
但是请注意,使用此技术,您可能仍然有两个事务并行搜索相同类型,然后尝试并行插入它。其中一项事务将回滚,但频率会低得多。
回答by Distortum
If you're using EJB 3.1 and you don't mind serializing this operation, a singleton bean using container managed concurrency can solve the problem.
如果您使用的是 EJB 3.1 并且不介意序列化此操作,则使用容器管理并发的单例 bean 可以解决该问题。
@Singleton
@ConcurrencyManagement(ConcurrencyManagementType.CONTAINER)
public class KeywordTypeManager
{
@Lock(LockType.WRITE)
public void upsert(KeywordType keywordType)
{
// Only one thread can execute this at a time.
// Your implementation here:
// ...
}
@Inject
private KeywordTypeDao keywordTypeDao;
}
回答by Timoteo Ponce
I would go for this option:
我会选择这个选项:
A different workaround would be trying to retrieve the type first in the code generating the keywords, and set it on the keyword if found or create a new one if not.
另一种解决方法是尝试在生成关键字的代码中首先检索类型,如果找到则将其设置在关键字上,否则创建一个新的。