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

时间:2011-02-25 13:26:36

标签: java jpa constraints unique

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

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

搜索SO我发现了几个可能性(其中一些来自不同的上下文,所以我不确定它们的有效性) - this postthis post建议捕获异常对我来说没有用,因为我最终在我开始的地方,仍然需要以某种方式持久保存关键字。
同样适用于针对不同情况提出的锁定here
thisthis帖子中提议的自定义插入语句我认为不合适,因为我我正在使用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;
    ...
}

3 个答案:

答案 0 :(得分:4)

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

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

  • 很难知道要捕获哪个异常并使代码可以跨JPA和数据库引擎移植
  • 在这种异常之后,JPA引擎将处于未确定状态,在这种情况下你应该总是回滚。

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

答案 1 :(得分:2)

如果你正在使用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;
}

答案 2 :(得分:0)

我会选择这个选项:

  

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