【问题标题】:hibernate column uniqueness questionhibernate 列唯一性问题
【发布时间】:2011-01-30 13:09:38
【问题描述】:

我仍在学习hibernate/hql,我有一个问题,一半是最佳实践问题/一半是健全性检查。

假设我有一个 A 类:

@Entity
public class A
{
    @Id @GeneratedValue(strategy=GenerationType.AUTO)
    private Long id;

    @Column(unique=true)
    private String name = "";

    //getters, setters, etc. omitted for brevity
}

我想强制每个被保存的 A 实例都有一个唯一的名称(因此是 @Column 注释),但我也希望能够处理已经保存了一个具有该名称的 A 实例的情况。我看到了两种方法:

1) 我可以捕获在 session.saveOrUpdate() 调用期间可能引发的 org.hibernate.exception.ConstraintViolationException 并尝试处理它。

2) 在调用 session.saveOrUpdate() 之前,我可以在 DAO 中查询已经具有该名称的 A 的现有实例。

现在我倾向于方法 2,因为在方法 1 中,我不知道如何以编程方式确定违反了哪个约束(A 中还有几个其他唯一成员)。现在我的 DAO.save() 代码大致如下:

public void save(A a) throws DataAccessException, NonUniqueNameException
{
    Session session = sessionFactory.getCurrentSession();

    try
    {
        session.beginTransaction();

        Query query = null;

        //if id isn't null, make sure we don't count this object as a duplicate
        if(obj.getId() == null)
        {
            query = session.createQuery("select count(a) from A a where a.name = :name").setParameter("name", obj.getName());
        }
        else
        {
            query = session.createQuery("select count(a) from A a where a.name = :name " + 
                "and a.id != :id").setParameter("name", obj.getName()).setParameter("name", obj.getName());
        }

        Long numNameDuplicates = (Long)query.uniqueResult();
        if(numNameDuplicates > 0)
            throw new NonUniqueNameException();

        session.saveOrUpdate(a);
        session.getTransaction().commit();
    }
    catch(RuntimeException e)
    {
            session.getTransaction().rollback();
            throw new DataAccessException(e); //my own class
    }
}

我是否以正确的方式处理这件事? hibernate 可以以编程方式(即不是作为错误字符串)告诉我哪个值违反了唯一性约束吗?通过将查询与提交分开,我是否会引发线程安全错误,或者我是否安全?这通常是怎么做的?

谢谢!

【问题讨论】:

    标签: java hibernate unique-constraint sanity-check


    【解决方案1】:

    我认为你的第二种方法是最好的。

    为了能够捕获 ConstraintViolation 异常并确定是该特定对象导致它,您需要在调用 saveOrUpdate 后立即刷新会话。如果您需要一次插入多个此类对象,这可能会导致性能问题。

    即使您会在每次保存操作时测试该名称是否已经存在于表中,但这仍然比每次插入后刷新要快。 (您可以随时进行基准测试来确认。)

    这还允许您以可以从不同层调用“验证器”的方式构建代码。例如,如果此唯一属性是新用户的电子邮件,则可以从 Web 界面调用验证方法来确定该电子邮件地址是否可接受。如果您选择第一个选项,则只有在尝试插入电子邮件后才能知道该电子邮件是否可以接受。

    【讨论】:

    • 第二种方法会有线程安全问题吗?据我了解,ACID DB 原则的“隔离”部分应该对我有所帮助,但我不确定 hibernate/hsqldb 与它的兼容性如何。
    • 我不明白为什么隔离对这种情况很重要。测试名称是否存在将发生在同一个事务中,并且不会改变数据库中数据的状态。
    【解决方案2】:

    方法 1 可以满足以下条件:

    • 实体中只有一个约束。
    • 会话中只有一个脏对象。

    请记住,在调用flush() 或提交事务之前,该对象可能不会被保存。

    为了获得最佳的错误报告,我会:

    1. 对每个约束违规使用方法二,所以我可以为每个约束给出一个特定的错误..
    2. 实施一个拦截器,在出现约束异常时重试事务(最大次数),这样在其中一个测试中就不会发现违规。这仅取决于事务隔离级别。

    【讨论】:

      猜你喜欢
      • 2016-07-25
      • 1970-01-01
      • 1970-01-01
      • 2010-10-10
      • 2018-05-24
      • 1970-01-01
      • 2011-02-17
      • 1970-01-01
      • 2013-02-16
      相关资源
      最近更新 更多