gpt4 book ai didi

sql - Spring数据jpa插入多表避免锁表

转载 作者:行者123 更新时间:2023-12-04 08:30:22 25 4
gpt4 key购买 nike

你能帮我理解如何有效地将实体插入到多个表中吗?
我相应地有 3 个表和 3 个实体。 Pricebook 有一组 SKU,每个 SKU 有 1 个价格。基本上我想要的是事务性地插入多个实体,如果有约束,我必须更新链中的实体。
enter image description here
一旦我尝试将超过 1 个 Pricebook 并行插入到数据库中,就会出现问题,因此我实际上遇到了 PostgreSQL 死锁。我发现一种解决方法是将它们一个一个地插入队列中,但我知道这不是一个好主意。
这可能是一个愚蠢的问题,之前已经回答过,但我希望有人能给我一个提示。

    @Entity
@NoArgsConstructor
@AllArgsConstructor
@Table(name = "pricebook")
public class Pricebook {
@Id
@GeneratedValue(strategy=GenerationType.AUTO)
private Long id;
//....
}

@Entity
@NoArgsConstructor
@AllArgsConstructor
@Table(name = "sku")
public class Sku {
@Id
@GeneratedValue(strategy=GenerationType.AUTO)
private Long id;
//....
}

@Entity
@NoArgsConstructor
@AllArgsConstructor
@Table(name = "price")
public class Price {
@Id
@GeneratedValue(strategy=GenerationType.AUTO)
private Long id;

@JoinColumn(name = "pricebook_id", referencedColumnName = "id", unique = true)
@ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
private Pricebook pricebook;

@JoinColumn(name = "sku_id", referencedColumnName = "id", unique = true)
@ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
private Sku sku;

//....
}
这是 upsert 的 PricebookService 逻辑。
  @NonNull
@Transactional
public Pricebook createPricebook(@NonNull CreatePricebookRequest request) {
final Instant startDate = PricebookConverter.toDate(request.getStartDate());
final Instant expirationDate = PricebookConverter.toDate(request.getExpirationDate());

if (startDate.isAfter(expirationDate)) {
throw new InvalidParametersException("The pricebook's start date later then its expiration date.");
}

final Region region = regionService.findRegionByName(request.getRegion());

final Optional<Pricebook> isPricebookFound =
pricebookRepository.findByRegionAndPricebookTypeAndStartDateAndExpirationDate(region,
request.getPricebookName(), startDate, expirationDate);

final Pricebook savedOrUpdatedPricebook;
if (isPricebookFound.isPresent()) {
final Pricebook foundPricebook = isPricebookFound.get();

savedOrUpdatedPricebook = pricebookRepository.save(
new Pricebook(foundPricebook.getPricebookId(), request.getName(), foundPricebook.getPricebookName(), foundPricebook.getRegion(), foundPricebook.getStartDate(),
foundPricebook.getExpirationDate());

logger.info("pricebook is updated successfully, pricebook={}", savedOrUpdatedPricebook);
} else {
savedOrUpdatedPricebook = pricebookRepository.save(
new Pricebook(request.getName(), request.getPricebookType(), region, startDate, expirationDate);

logger.info("pricebook is created successfully, pricebook={}", savedOrUpdatedPricebook);
}

final List<Sku> skus = skuService.createSku(savedOrUpdatedPricebook, request.getSkus());
logger.debug("skus are saved successfully, skus={}", skus);
return savedOrUpdatedPricebook;
}
这是 upsert 的 SkuService 逻辑。 skuToCreateOrUpdate基本上只是一个方法,如果它被找到或者是新的,则包装逻辑并返回一个新对象。
    @NonNull
public List<Sku> createSku(@NonNull Pricebook pricebook, @NonNull List<CreateSkuRequest> skus) {
return skus.stream().map(sku -> {
final Optional<Sku> foundSku = skuRepository.findByCode(sku.getCode());

final Sku savedOrUpdatedSku = skuRepository.save(skuToCreateOrUpdate(sku, foundSku.map(Sku::getSkuId).orElse(null)));

final List<Price> prices = priceService.createPrices(pricebook, savedOrUpdatedSku, sku.getPrice());
logger.debug("prices are saved successfully, prices={}", prices);
return savedOrUpdatedSku;
}).collect(toList());
}
这是 upsert 的 PriceService 逻辑。
    @NonNull
public List<Price> createPrices(@NonNull Pricebook pricebook, @NonNull Sku sku, @NonNull CreatePriceRequest price) {
final Optional<Price> foundPrice = priceRepository.findByPricebookAndSku(pricebook, sku);

final Price savedOrUpdatedPrice;
if (foundPrice.isPresent()) {
final Price priceToUpdate = foundPrice.get();
savedOrUpdatedPrice = priceRepository.save(
new Price(priceToUpdate.getPriceId(),
pricebook,
sku);
logger.info("price is updated successfully, price={}", savedOrUpdatedPrice);
} else {
savedOrUpdatedPrice = priceRepository.save(
new Price(pricebook, sku);
logger.info("price is created successfully, price={}", savedOrUpdatedPrice);
}

return Collections.singletonList(savedOrUpdatedPrice);
}
我到处都在使用 JpaRepository。像这样..
@Repository
public interface PricebookRepository extends JpaRepository<Pricebook, Long> {}

@Repository
public interface SkuRepository extends JpaRepository<Sku, Long> {}

@Repository
public interface PriceRepository extends JpaRepository<Price, Long> {}

最佳答案

相信你可能会遇到this issue ,这很有可能,特别是如果两个事务都尝试插入相同的 SKU。
如果是这种情况,我可以想到两种方法来缓解它:

  • 部分解决方案 : 尝试对 List<CreateSkuRequest> skus 中的 SKU 进行排序来自 sku.code和(如果这还不够)使用 saveAndFlush()来存储它们,确保插入的顺序。这应该消除循环等待,这意味着现在至少一个事务应该成功(另一个可能会违反唯一约束)
  • 完整解决方案 :如果您希望两个事务都成功,则必须为 SKU 获取表级锁 table 。您应该能够使用自定义更新查询来做到这一点:
  • @Query(value = "LOCK TABLE SKU IN EXCLUSIVE MODE", nativeQuery = true)
    @Modifying
    void lockTable();
    然后,只需调用该方法作为 createSku 中的第一个操作.请注意,这可能只比将事务放入队列中效率稍高,所以如果我是你,我可能仍然会采用这种方法。
    编辑我也不太明白确切的场景,它为您提供了两个事务冲突的一致结果,这是您尝试并行化的批量插入类型吗?如果您真的很想并行运行事务,也许您可​​以对输入集进行分组,这样 SKU 就不会重叠。或者,删除重复数据并插入 Sku预先。正如我所说,我不知道用例是什么,所以不确定这是否有意义。

    关于sql - Spring数据jpa插入多表避免锁表,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/65049057/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com