Java Spring数据JPA-并发批量插入/更新
目前,我开发了一个Spring引导应用程序,它主要从消息队列(~5个并发消费者)中提取产品审查数据,并将其存储到MySQL数据库中。每个评审都可以通过其评审标识符(字符串)进行唯一标识,该标识符是主键,可以属于一个或多个产品(例如,具有不同颜色的产品)。以下是数据模型的摘录:Java Spring数据JPA-并发批量插入/更新,java,hibernate,spring-data,spring-data-jpa,Java,Hibernate,Spring Data,Spring Data Jpa,目前,我开发了一个Spring引导应用程序,它主要从消息队列(~5个并发消费者)中提取产品审查数据,并将其存储到MySQL数据库中。每个评审都可以通过其评审标识符(字符串)进行唯一标识,该标识符是主键,可以属于一个或多个产品(例如,具有不同颜色的产品)。以下是数据模型的摘录: public class ProductPlacement implements Serializable{ private static final long serialVersionUID = 1L;
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;
}
findByReviewIdentifier返回null时会发生什么?即使方法返回null,hibernate是否可以为可能的插入锁定reviewIdentifier
谢谢大家! > P>从性能的角度来看,我将考虑使用以下更改来评估解决方案。
hibernate.order\u inserts=true
hibernate.order\u updates=true
hibernate.jdbc.batch_version_data=true
ProductPlacement
,并对每个review
执行saveAndFlush
,这不会导致DML语句的批处理
< P> >我将考虑加载<代码>产品布局>代码>实体,并将<>代码>清单Cuffer-Reals/<代码>添加到<代码> SET Cuffer-Reals/<代码>字段>代码>产品布局> /代码>实体,最后调用“代码>合并/代码>方法一次,这两个变化:
- 使
实体成为关联的所有者,即通过将ProductPlacement
属性移动到mappedBy
实体的设置productPlacements
字段中
- 通过在这些方法中使用
字段,使reviewIdentifier
实体实现CustomerReview
和equals
方法。我相信,hashCode
是唯一的,由用户指定reviewIdentifier
最后,在使用这些更改进行性能调优时,请使用当前代码对性能进行基线测试。然后进行更改并比较这些更改是否真的导致解决方案的性能显著提高。要消除竞争条件,请同步
saveAllReviews()
或基于review键(受约束的属性)实现显式锁定。在我们的组织中,我们还需要处理这种情况。经过3年多的尝试和测试,我们找不到比按键锁定更好的方法。。。也许还有另外一种做法,我也想学习一下。谢谢你的回复。您认为同步方法和锁定密钥(性能方面)有区别吗?当然,密钥锁定会更有效,因为您可以安全地允许对不同的密钥进行并发写入。但这一方法需要实施努力。您可以先尝试synchronized
,然后在性能不满足时考虑更高级的技术。虽然这一切都提高了性能,但毫无疑问,这有助于避免并发查找-插入周期上的争用条件?@SashaSalauyou确实如此。这主要解决问题的性能方面。对于比赛条件,我倾向于同步进近,但我想知道是否有更好的方法,但目前还不确定。@Madhusudanareddysunapu感谢您的输入。我也考虑过让productPlacement成为关系的所有者,但假设一个产品有2.5k的评论。这会不会导致在集合中增加10个,从而获得2.5万条评论?是否可以将项目添加到延迟加载的集合中?@JuHarm89是的,在这种情况下会获取所有评论。如何-由于在代码中的某个点手动为新的Customerreview
指定了reviewIdentifier
,因此我们可以在Customerreview
中添加boolean isNew
瞬态字段,该字段将根据是否是新的审阅设置为true/false
。在保存上述代码中的reviews
时,我们可以使用HQL
对new
执行insert review并生成映射行。看起来效率更高,但也可以解决constraintViolationException。不过,一个缺点是SLC使用HQL无效。
@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);
}
}
}
@Lock(LockModeType.PESSIMISTIC_WRITE)
CustomerReview findByReviewIdentifier(String reviewIdentifier);