我还在学习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
}
}
我是否以正确的方式解决这个问题?休眠可以通过编程方式告诉我(即不是错误字符串)哪个值违反了唯一性约束?通过将查询与提交分开,我是在邀请线程安全错误,还是我安全?这通常是怎么做的?
谢谢!
答案 0 :(得分:3)
我认为你的第二种方法是最好的。
为了能够捕获此特定对象导致它的任何确定性的ConstraintViolation异常,您需要在调用saveOrUpdate后立即刷新会话。如果您需要一次插入许多这些对象,这可能会引入性能问题。
即使您要测试每个保存操作的表中是否已存在该名称,这仍然比每次插入后刷新更快。 (您可以随时进行基准确认。)
这也允许您以这样一种方式构建代码,即可以从不同的层调用“验证器”。例如,如果此唯一属性是新用户的电子邮件,则可以从Web界面调用验证方法以确定电子邮件地址是否可接受。如果您使用第一个选项,则只有在尝试插入电子邮件后才能知道该电子邮件是否可接受。
答案 1 :(得分:1)
如果出现以下情况,方法1就可以了:
请记住,在调用flush()
或提交事务之前,可能无法保存对象。
为了获得最佳错误报告,我会: