Spring Data JPA中@Column的scale参数无效问题求助
实体类定义
@Entity @Getter @Setter @ToString @NoArgsConstructor @AllArgsConstructor @Table(name = "ACCOUNT") public class AccountDao { @Id @Column(name = "id", nullable = false) private String id; @Column(name = "balance", scale = 2) private Double balance; @Column(name = "currency") @Enumerated(EnumType.STRING) private Currency currency; @Column(name = "created_at") private Date createdAt; // equals(), hashcode() }
注:已知余额最优类型为BigDecimal而非Double,后续会迭代修正,此代码非生产级
仓库接口
public interface AccountRepository extends JpaRepository<AccountDao, String> {}
控制器POST端点
@PostMapping("/newaccount") public ResponseEntity<EntityModel<AccountDto>> postNewAccount(@RequestBody AccountDto accountDto) { accountService.storeAccount(accountDto); return ResponseEntity.ok(accountModelAssembler.toModel(accountDto)); }
服务层存储方法
public void storeAccount(AccountDto accountDto) throws AccountAlreadyExistsException{ Optional<AccountDao> accountDao = accountRepository.findById(accountDto.getId()); if (accountDao.isEmpty()) { accountRepository.save( new AccountDao( accountDto.getId(), accountDto.getBalance(), accountDto.getCurrency(), new Date())); } else { throw new AccountAlreadyExistsException(accountDto.getId()); } }
测试请求体
{ "id" : "acc2", "balance" : 1022.3678234, "currency" : "USD" }
数据库查询结果(MySQL 8.0.33)
mysql> select * from account where id = 'acc2'; +------+--------------+----------------------------+----------+ | id | balance | created_at | currency | +------+--------------+----------------------------+----------+ | acc2 | 1022.3678234 | 2023-06-30 23:48:10.230000 | USD | +------+--------------+----------------------------+----------+
当前遇到的问题:数据库保留了Double类型的所有小数位,完全忽略了@Column(scale=2)的配置,除了未使用BigDecimal之外,导致scale参数无效的原因是什么?哪里操作有误?
JPA规范中scale参数仅针对精确数值类型:
scale和precision参数是为BigDecimal、BigInteger这类精确数值类型设计的,对于Double、Float这类浮点类型,JPA本身不支持用这两个参数约束小数位数。主流JPA实现(如Hibernate)会直接忽略浮点类型字段上的scale配置,因为浮点类型的设计目的是存储近似值,没有固定小数位的概念。数据库字段类型不匹配:如果MySQL中
balance字段的类型是DOUBLE,而非DECIMAL(precision, scale),那么即使JPA配置了scale=2,数据库也不会对DOUBLE类型的数值做小数位截断或四舍五入。DOUBLE类型本身不支持指定固定小数位数,scale参数只有在数据库字段为DECIMAL/NUMERIC类型时才会生效。代码未主动处理小数位:当前代码直接将DTO中的Double值存入数据库,没有在服务层或DTO转换阶段做四舍五入到两位小数的处理。由于
scale配置对Double无效,JPA不会自动处理小数位,数值会原封不动存储。
如果暂时不想切换到BigDecimal,可在代码中手动处理小数位,比如用Math.round(accountDto.getBalance() * 100) / 100.0保留两位小数后再存入数据库。
内容的提问来源于stack exchange,提问作者Jason

