如何保证事务服务方法中的行级唯一约束?
How to guarantee a row-level unique constraint in a transactional service method?
我在我的服务层进行了一些业务检查,在那里我用一些查询和条件检查它。
他们不能在数据库层做唯一性或检查。但是并发两个请求通过检查并在我的数据中出错。
问题是:我从用户控件中获取商品序列号,如果数据库中不存在商品序列号,我会用数据库检查商品序列号,让它保存记录,否则会抛出异常。
该图像有助于更好地理解问题:
http://pasteboard.co/gPE0BRlRK.jpg
我们可以在 hibernate 上使用悲观锁,但是如果我们在数据库中没有任何编号为 A100 的记录,例如检查通过并在数据库中提交两个事务。
保存方法在这里:
@Override
public Long save(Product entity) {
if (!isUniqueForSave(entity))
throw new ApplicationException(saveUniqueError);
return super.save(entity);
}
方法是 UniqueForSave :
private boolean isUniqueForSave(Product entity) {
return iProductRepository.isUniqueForSave(entity);
}
DAO 层中的查询在这个方法中:
public boolean isUniqueForSave(Product entity) {
Session session = getSession();
String hql = "select c from " + domainClass.getName() + " c "
+ " where c.ProductNumber = :productNumber "
+ " and c.item.id = :itemId "
+ " and c.deleted = 0 ";
if (entity.getId() != null && entity.getId() > 0)
hql += " and c.id != "+entity.getId();
Query query = session.createQuery(hql);
query.setParameter("productNumber ", entity.getProductNumber());
query.setParameter("itemId ", entity.getItem().getId());
List<Product> productsList = query.list();
if(productsList .size() > 1)
return false;
else if(productsList .size() == 0)
return true;
Product product= productsList .get(0);
if(entity.getOldItem() != null && product.getId().equals(entity.getOldItem().getId()))
return true;
return false;
}
您的代码容易受到 SQL 注入攻击。你真的也想修好它!
这里的问题是你要保证没有其他事务会偷偷溜进来,加一个Product
同样的过滤条件
只有在数据库中已有行的情况下,乐观锁定才能为您提供帮助。因此,在您的情况下,乐观锁定仅在行被标记为已删除时才起作用。
但是,即使在执行查询时数据库中没有记录,您也希望此检查有效。在这种情况下,您需要使用 SERIALIZABLE
,因为它可以防止幻读和丢失更新。
因此,您需要将当前交易注释为SERIALIZABLE
。
我在我的服务层进行了一些业务检查,在那里我用一些查询和条件检查它。
他们不能在数据库层做唯一性或检查。但是并发两个请求通过检查并在我的数据中出错。 问题是:我从用户控件中获取商品序列号,如果数据库中不存在商品序列号,我会用数据库检查商品序列号,让它保存记录,否则会抛出异常。 该图像有助于更好地理解问题: http://pasteboard.co/gPE0BRlRK.jpg
我们可以在 hibernate 上使用悲观锁,但是如果我们在数据库中没有任何编号为 A100 的记录,例如检查通过并在数据库中提交两个事务。
保存方法在这里:
@Override
public Long save(Product entity) {
if (!isUniqueForSave(entity))
throw new ApplicationException(saveUniqueError);
return super.save(entity);
}
方法是 UniqueForSave :
private boolean isUniqueForSave(Product entity) {
return iProductRepository.isUniqueForSave(entity);
}
DAO 层中的查询在这个方法中:
public boolean isUniqueForSave(Product entity) {
Session session = getSession();
String hql = "select c from " + domainClass.getName() + " c "
+ " where c.ProductNumber = :productNumber "
+ " and c.item.id = :itemId "
+ " and c.deleted = 0 ";
if (entity.getId() != null && entity.getId() > 0)
hql += " and c.id != "+entity.getId();
Query query = session.createQuery(hql);
query.setParameter("productNumber ", entity.getProductNumber());
query.setParameter("itemId ", entity.getItem().getId());
List<Product> productsList = query.list();
if(productsList .size() > 1)
return false;
else if(productsList .size() == 0)
return true;
Product product= productsList .get(0);
if(entity.getOldItem() != null && product.getId().equals(entity.getOldItem().getId()))
return true;
return false;
}
您的代码容易受到 SQL 注入攻击。你真的也想修好它!
这里的问题是你要保证没有其他事务会偷偷溜进来,加一个Product
同样的过滤条件
只有在数据库中已有行的情况下,乐观锁定才能为您提供帮助。因此,在您的情况下,乐观锁定仅在行被标记为已删除时才起作用。
但是,即使在执行查询时数据库中没有记录,您也希望此检查有效。在这种情况下,您需要使用 SERIALIZABLE
,因为它可以防止幻读和丢失更新。
因此,您需要将当前交易注释为SERIALIZABLE
。