0

I have the following DAO that is supposed to model bank accounts.

@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()
}

(I am aware that for balances one ought to best use BigDecimal instead of Float or Double, and it's happening in the next iteration. This is not production - grade code by any means.)

My repository for this DAO simply extends JPARepository<AccountDao, String>:

public interface AccountRepository extends JpaRepository<AccountDao, String> {}

The relevant controller and service class methods are relatively straightforward:

Controller POST endpoint:

 @PostMapping("/newaccount")
  public ResponseEntity<EntityModel<AccountDto>> postNewAccount(@RequestBody AccountDto accountDto) {
    accountService.storeAccount(accountDto);
    return ResponseEntity.ok(accountModelAssembler.toModel(accountDto));
  }

Service method storeAccount():

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());
    }
  }

When I POST the following payload:

{
    "id" : "acc2",
    "balance" : 1022.3678234,
    "currency" : "USD"
}

My database (MySQL Ver 8.0.33-0ubuntu0.20.04.2) persists the Double with all the decimal digits, ignoring the scale = 2 option that I have provided to the @Column annotation.

mysql> select * from account where id = 'acc2';
+------+--------------+----------------------------+----------+
| id   | balance      | created_at                 | currency |
+------+--------------+----------------------------+----------+
| acc2 | 1022.3678234 | 2023-06-30 23:48:10.230000 | USD      |
+------+--------------+----------------------------+----------+

Why does this happen? What am I doing wrong here (besides not using BigDecimal; see above)?

Jason
  • 2,495
  • 4
  • 26
  • 37

2 Answers2

0

Turns out that actually my problem is most likely exactly that I don't use a BigDecimal. Based on the comments in the accepted answer of this thread, the scale parameter of the @Column annotation will NOT work for Double fields, but it WILL work for BigDecimal fields.

Jason
  • 2,495
  • 4
  • 26
  • 37
-1

Here is the answer shown below

In this updated AccountDao, the balance field is now of type BigDecimal, and the @Column annotation includes precision = 15 and scale = 2. The precision attribute represents the total number of digits to be stored, and the scale attribute represents the number of digits to the right of the decimal point.

Here is the AccountDao shown below

@Entity
@Getter
@Setter
@ToString
@NoArgsConstructor
@AllArgsConstructor
@Table(name = "ACCOUNT")
public class AccountDao {

  @Id
  @Column(name = "id", nullable = false)
  private String id;

  @Column(name = "balance", precision = 15, scale = 2)
  private BigDecimal balance;

  @Column(name = "currency")
  @Enumerated(EnumType.STRING)
  private Currency currency;

  @Column(name = "created_at")
  private Date createdAt;

  // equals(), hashcode()
}
S.N
  • 2,157
  • 3
  • 29
  • 78
  • Just to be clear on how this works, it is _theoretically_ possible that _really large_ numbers, i.e with more than 13 non-decimal digits, will be truncated or will overflow, correct? – Jason Jun 30 '23 at 23:32
  • @Jason Did you test it? Does it work? – S.N Jul 02 '23 at 22:08
  • Yes, I used only the `scale` parameter, and it persists as desired. Thanks. – Jason Jul 03 '23 at 17:59