почему Hibernate обрабатывает joda.money.Money как VARBINARY?
Сначала я просто объясню, что я пытаюсь сделать, а не то, что я сделал, чтобы этого добиться.
У меня есть экземпляр Amazon RDS, и я использую Spring + JPA для взаимодействия с ним. В этой базе данных есть таблица под названием "продукт". Раньше я просто использовал сохраненную валюту как INT в базе данных, сохраняя центы и конвертируя в java, и это работало нормально. Вместо этого я пытался использовать тип DECIMAL(13,2) в базе данных для валюты и использовать joda.money.Money на стороне Java, чтобы немного очистить код. Определение столбца валюты продукта таблицы выглядит следующим образом
@Column(name = "prodprice", columnDefinition = "DECIMAL(13,2)")
@Type(type = "org.joda.money.Money",
parameters = {@org.hibernate.annotations.Parameter(name = "currencyCode", value = "CAD")})
private Money prodPrice;
У меня также есть еще один объект, называемый цветком, и его валюта определяется следующим образом
@JsonSerialize(using = MoneySerializer.class)
@JsonDeserialize(using = MoneyDeserializer.class)
@Column(name = "priceperpound")
@Type(type = "org.joda.money.Money",
parameters = {@org.hibernate.annotations.Parameter(name = "currencyCode", value = "CAD")})
private Money pricePerPound;
Эта сущность передается в Spring через этот метод
@PostMapping("/inventory/addproduct/flower")
@Transactional
public ResponseEntity<?> addFlower(
@RequestBody flower newFlower,
@RequestParam String prodName,
@RequestParam Integer qtyOnHand,
@RequestParam BigDecimal prodPrice,
@RequestParam String prodDescription)
{
Integer newProductID = addNewProduct(prodName, qtyOnHand, Money.of(CurrencyUnit.CAD, prodPrice), prodDescription);
newFlower.setProdID(newProductID);
saveEntity(newFlower);
return ResponseEntity.ok(String.format("Product Added.\n ID : %s", newProductID));
}
Проблема в том, что hibernate обрабатывает экземпляр Money в объекте продукта как VARBINARY вместо DECIMAL при генерации операторов вставки. Я выяснил это, отладив подготовленный оператор. В заявлении говорится:
insert
into
product
(proddescription, prodname, prodprice, prodqtyonhand)
values
(?, ?, ?, ?)
2020-09-16 16:18:26.172 TRACE 16576 --- [nio-8080-exec-2] o.h.type.descriptor.sql.BasicBinder :
binding parameter [1] as [VARCHAR] - [Decimal Flower Test]
2020-09-16 16:18:26.173 TRACE 16576 --- [nio-8080-exec-2] o.h.type.descriptor.sql.BasicBinder :
binding parameter [2] as [VARCHAR] - [test3]
2020-09-16 16:18:26.174 TRACE 16576 --- [nio-8080-exec-2] o.h.type.descriptor.sql.BasicBinder :
binding parameter [3] as [VARBINARY] - [CAD 10.25]
2020-09-16 16:18:26.175 TRACE 16576 --- [nio-8080-exec-2] o.h.type.descriptor.sql.BasicBinder :
binding parameter [4] as [INTEGER] - [58]
2020-09-16 16:18:26.359 WARN 16576 --- [nio-8080-exec-2] o.h.engine.jdbc.spi.SqlExceptionHelper :
SQL Error: 1264, SQLState: 22001
2020-09-16 16:18:26.360 ERROR 16576 --- [nio-8080-exec-2] o.h.engine.jdbc.spi.SqlExceptionHelper :
Data truncation: Out of range value for column 'prodPrice' at row 1
Я прошел через класс сериализатора, и, похоже, у него нет проблем с обработкой класса Money внутри объекта цветка.
Вещи, которые я пробовал: добавление тех же аннотаций, что и в сущности цветка, в сущность продукта, изменение способа создания экземпляра Money из @RequestParam, путем попытки взамен двойного, и даже парсинг Money из String (Money.parse(prodPrice)). В конце концов, спящий режим, кажется, всегда оставляет денежную единицу перед фактическим значением, и я думаю, поэтому спящий режим рассматривает его как VARBINARY. Строка, которая заставляет меня думать об этом, в частности,
o.h.type.descriptor.sql.BasicBinder : binding parameter [3] as [VARBINARY] - [CAD 10.25]
поскольку в нем указано 10,25 канадских долларов вместо 10,25
Раньше я пытался создать свой собственный тип для спящего режима, но я не хочу идти по этому пути. Сейчас я доверяю joda.money гораздо больше, чем себе. Должен отметить, что это моя первая попытка поработать с Spring JPA более подробно, и я все еще очень мокрый за ушами. MoneySerializer и MoneyDeserializer, которые я использую, взяты с gist.github, вы можете найти их, выполнив поиск в MoneyDeserializer.java на gist.github.com, оба опубликованы с помощью stickfigure.
Ниже приведены некоторые фрагменты, которые могут быть полезны
-application.properties
spring.jpa.hibernate.ddl-auto=none
spring.datasource.url=jdbc:mysql://DELETEDFORSECURITY
spring.datasource.username=DELETEDFORSECURITY
spring.datasource.password=DELETEDFORSECURITY
logging.level.org.hibernate.SQL=DEBUG
logging.level.org.hibernate.type.descriptor.sql.BasicBinder=TRACE
spring.jpa.properties.hibernate.format_sql=true
-pom.xml зависимости
<dependencies>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.11.2</version>
<exclusions>
<exclusion>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
</exclusion>
<exclusion>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.11.2</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.11.2</version>
</dependency>
<dependency>
<groupId>org.joda</groupId>
<artifactId>joda-money</artifactId>
<version>1.0.1</version>
</dependency>
<dependency>
<groupId>org.joda</groupId>
<artifactId>joda-convert</artifactId>
<version>2.2.1</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.12</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.8.6</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.21</version>
</dependency>
<dependency>
<groupId>org.json</groupId>
<artifactId>json</artifactId>
<version>20180130</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
<version>2.3.2.RELEASE</version>
</dependency>
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
<version>2.3.0</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
-method addNewProduct
private Integer addNewProduct(String productName, Integer qtyOnHand, Money prodPrice, String prodDesc)
{
Product newProd = buildNewProduct(productName, qtyOnHand, prodPrice, prodDesc);
saveEntity(newProd);
return newProd.getProdID();
}
-метод buildNewProduct
private Product buildNewProduct(String productName, Integer qtyOnHand, Money prodPrice, String prodDesc)
{
return new Product(productName, qtyOnHand, prodPrice, prodDesc);
}
-method saveEntity
private void saveEntity(Object obj)
{
entityManager.persist(obj);
}
Как я могу сохранить это значение в базе данных как DECIMAL(13,2), используя JPA и joda.money? Лучший вопрос: что я делаю не так, что я в корне не понимаю, и насколько глупая эта идея? Есть ли способ лучше?
Этот код не используется ни на одной производственной мощности, и я просто пытаюсь изучить его.