目前我正在开发一个Spring Boot应用程序,主要从消息队列(~5个并发使用者)中提取产品评论数据,并将它们存储到MySQL数据库中。每个评论可以通过其评论标识符(字符串)进行唯一标识,该标识符是主键,可以属于一个或多个产品(例如,颜色不同的产品)。以下是数据模型的摘录:
public class ProductPlacement implements Serializable{
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name = "product_placement_id")
private long id;
@ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL, mappedBy="productPlacements")
private Set<CustomerReview> customerReviews;
}
public class CustomerReview implements Serializable{
private static final long serialVersionUID = 1L;
@Id
@Column(name = "customer_review_id")
private String reviewIdentifier;
@ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
@JoinTable(
name = "tb_miner_review_to_product",
joinColumns = @JoinColumn(name = "customer_review_id"),
inverseJoinColumns = @JoinColumn(name = "product_placement_id")
)
private Set<ProductPlacement> productPlacements;
}
队列中的一条消息包含1-15个评论和产品放置ID。现在我想要一种高效的方法来保存该产品的评论。每个传入的评论需要考虑以下两种情况:
- 该评论不在数据库中 -> 插入评论并引用消息中包含的产品。
- 该评论已经在数据库中 -> 只需将产品引用添加到现有评论的 Set productPlacements 中。
@Override
@Transactional
public void saveAllReviews(List<CustomerReview> customerReviews, long productPlacementId) {
ProductPlacement placement = productPlacementRepository.findOne(productPlacementId);
for(CustomerReview review: customerReviews){
CustomerReview cr = customerReviewRepository.findOne(review.getReviewIdentifier());
if (cr!=null){
cr.getProductPlacements().add(placement);
customerReviewRepository.saveAndFlush(cr);
}
else{
Set<ProductPlacement> productPlacements = new HashSet<>();
productPlacements.add(placement);
review.setProductPlacements(productPlacements);
cr = review;
customerReviewRepository.saveAndFlush(cr);
}
}
}
问题:
- 有时我会因为违反“reviewIndentifier”的唯一约束而收到“constraintViolationExceptions”。这显然是因为我(并发地)查看审核是否已存在,然后再插入或更新它。我该如何避免这种情况?
- 在我的情况下,使用save()还是saveAndFlush()更好?我每秒大约得到50-80个reviews。如果只使用save(),Hibernate会自动刷新,还是会导致内存使用大幅增加?
问题1的更新:在我的Review Repository上简单使用@Lock能否防止唯一约束异常?
@Lock(LockModeType.PESSIMISTIC_WRITE)
CustomerReview findByReviewIdentifier(String reviewIdentifier);
当findByReviewIdentifier返回null时会发生什么?即使该方法返回null,Hibernate是否可以锁定reviewIdentifier以进行可能的插入操作?谢谢!
saveAllReviews()
方法设为同步方法,要么根据评论的键(受限制的属性)实现显式锁定。在我们的组织中,我们也需要处理这种情况。经过三年多的尝试和测试,我们无法找到比按键进行锁定更好的方法...也许还有其他做法,我也想学习一下。 - Alex Salauyousynchronized
,如果性能不满意,则考虑更高级的技术。 - Alex Salauyou