使用 JPA 防止违反唯一约束的最佳方法

发布于 2024-10-19 03:16:41 字数 1503 浏览 2 评论 0 原文

我有一个 Keyword 和一个 KeywordType 作为实体。有很多类型很少的关键词。

当尝试保留类型的第二个关键字时,违反了唯一约束并且事务被回滚。

搜索SO我发现了几种可能性(其中一些来自不同的上下文,所以我不确定它们在这里的有效性) - 这篇文章此处针对不同情况建议的锁定
按照这篇文章我想不会正常工作,因为我正在使用 Oracle 和不是 MySQL,并且希望将实现与 Hibernate 联系起来。

另一种解决方法是尝试首先在生成关键字的代码中检索类型,如果找到则将其设置在关键字上,如果没有则创建一个新类型。

那么,什么是最好的——最强大、最可移植(对于不同的数据库和持久性提供者)和最合理的方法呢?

谢谢。

涉及的实体:

public class Keyword {

    @Id
    @GeneratedValue
    private long id;

    @Column(name = "VALUE")
    private String value;

    @ManyToOne
    @JoinColumn(name = "TYPE_ID")
    private KeywordType type;
    ...
}

以及

@Entity
@Table(uniqueConstraints = {@UniqueConstraint(columnNames = { "TYPE" }) })
public class KeywordType {

    @Id
    @GeneratedValue
    private long id;

    @Column(name = "TYPE")
    private String type;
    ...
}

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 post and this post advise 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 this and this posts 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?

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;
    ...
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

○愚か者の日 2024-10-26 03:16:41

IMO,你的最后一个解决方案是正确的。搜索关键字类型,如果没有找到,则创建它。

捕获异常不是一个好的选择,因为

  • 很难知道要捕获哪个异常并使代码可以跨 JPA 和 DB 引擎移植。
  • 在发生此类异常后,JPA 引擎将处于不确定状态,在这种情况下您应该始终回滚。

但请注意,使用此技术,您可能仍然有两个事务并行搜索相同类型,然后尝试并行插入它。其中一项事务会回滚,但频率会低得多。

Your last solution is the right one, IMO. Search for the keyword type, and if not found, create it.

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.

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.

墨落画卷 2024-10-26 03:16:41

如果您使用 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;
}

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.

@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;
}
小伙你站住 2024-10-26 03:16:41

我会选择这个选项:

将尝试不同的解决方法
首先在代码中检索类型
生成关键字,并将其设置为
关键字(如果找到)或创建一个新的
如果没有的话,就一个。

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文