休眠列唯一性问题

Set*_*eth 6 java hibernate unique-constraint sanity-check

我还在学习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
}
Run Code Online (Sandbox Code Playgroud)

我想强制保存的每个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
    }
}
Run Code Online (Sandbox Code Playgroud)

我是否以正确的方式解决这个问题?hibernate可以通过编程方式告诉我(即不是错误字符串)哪个值违反了唯一性约束?通过将查询与提交分开,我是在邀请线程安全错误,还是我安全?这通常是怎么做的?

谢谢!

Rac*_*hel 4

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

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

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

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