使用 JPA + MySQL 进行货币操作的正确方法是什么?

What's the correct approach for monetary operations with JPA + MySQL?

在这种情况下,我需要在执行货币操作减法的字段中执行更新。该列创建为 DECIMAL(19,2) UNSIGNED.

首先我得到值:

static final String LOCK_AMOUNT_FOR_UPDATED = "select amount from Sample where uuid = :uuid for update ";

这是存储库:

    @Query(value = LOCK_AMOUNT_FOR_UPDATED, nativeQuery = true)
    Float getAmountForUpdate(@Param("uuid") String uuid);

这里是第一题,不是返回Float。 建议返回一个 BigDecimal?

然后我检查值并执行更新:

static final String DECREASE_AMOUNT = "update Sample set amount = amount - :amountToSubtract where uuid = :uuid ";

存储库:

    @Modifying
    @Query(value = DECREASE_AMOUNT, nativeQuery = true)
    void decreaseAmount(@Param("amountToSubtract") Float amountNegotiated, @Param("uuid") String uuid);

当值为:1927369.70 时执行此操作。我得到:

Data truncation: Out of range value for column 'amount' at row 1

方法调用是这样的:

repository.decreaseNetAmount(amountNegotiated.floatValue(), uuid);

我注意到当我 select 值和我在 BigDecimal 中调用 .floatValue()1927369.70 变成了 1927369.80

将所有内容都用作 BigDecimal 的正确方法是什么,甚至是 nativeQuery 的参数?

我会推荐给:

  1. 对参数和实体属性使用 BigDecimal(如果您决定使用 BigDecimal)。
  2. 重新考虑为什么要使用本机查询来更新单个记录。获取实体对象并更改属性的值似乎是更简单的方法,并且由于持久性提供程序的内部优化可能会提供更好的性能。
  3. 重新考虑您在获取记录时设置的悲观锁。这也会阻止对该记录(以及同一页上可能的其他记录)的所有读取操作,直到事务结束。乐观锁通常提供更好的可扩展性。

建议 2 和 3 是一般最佳做法。我们需要更广泛地审视整个用例和应用程序的其他部分,以做出明智的决定