目前,我开发了一个Spring Boot应用程序,该应用程序主要从消息队列(约5个并发使用者)中提取产品评论数据,并将其存储到MySQL DB中。每个评论都可以通过其reviewIdentifier(String)进行唯一标识,该评论标志是主键,可以属于一个或多个产品(例如,具有不同颜色的产品)。这是数据模型的摘录:

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条评论和一个productPlacementId。现在,我想要一种有效的方法来保留产品的评论。每次传入审核基本上都需要考虑两种情况:
  • 该评论不在数据库中->插入引用
  • 消息中包含的产品的评论
  • 该评论已经在数据库中->只需将产品引用添加到现有评论的Set productPlacements中即可。

  • 目前,我保留评论的方法不是最佳的。它看起来如下(使用Spring Data JpaRespoitories):
    @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条评论。如果我只使用save(), hibernate 会自动刷新吗?还是会导致内存使用量大大增加?

  • 更新到问题1:我的Review-Repository上的简单@Lock是否会表示unique-constraint异常?
    @Lock(LockModeType.PESSIMISTIC_WRITE)
    CustomerReview findByReviewIdentifier(String reviewIdentifier);
    

    当findByReviewIdentifier返回null时会发生什么?即使方法返回null, hibernate 也可以为潜在的插入锁定reviewIdentifier吗?

    谢谢!

    最佳答案

    从性能的角度来看,我将考虑通过以下更改来评估解决方案。

  • 从双向ManyToMany更改为双向OneToMany

  • 我有一个相同的问题,即从执行的DML语句中哪个更有效。引用Typical ManyToMany mapping versus two OneToMany

    从配置的角度来看,选项一可能更简单,但它会产生效率较低的DML语句。



  • 启用DML语句的批处理

  • 启用批处理支持将减少往返数据库以插入/更新相同数量记录的往返次数。

    batch INSERT and UPDATE statements报价



  • 删除saveAndFlush调用的数目

  • 当前代码获取ProductPlacement,并且对于每个review都执行一个saveAndFlush,这将导致不对DML语句进行批处理。

    相反,我会考虑加载ProductPlacement实体,并将List<CustomerReview> customerReviews添加到Set<CustomerReview> customerReviews实体的ProductPlacement字段中,最后在最后一次调用一次merge方法,并进行以下两个更改:
  • 通过将ProductPlacement属性移到mappedBy实体的Set<ProductPlacement> productPlacements字段上,使CustomerReview实体成为关联的所有者。
  • 通过使用CustomerReview字段中的equals字段来使hashCode实体实现reviewIdentifierreviewIdentifier方法。我相信ojit_code是唯一的并由用户分配。

  • 最后,当您通过这些更改进行性能调整时,请以当前代码为基准来确定性能。然后进行更改,并比较更改是否确实为您的解决方案带来了显着的性能改进。

    09-26 17:02