почему 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? Лучший вопрос: что я делаю не так, что я в корне не понимаю, и насколько глупая эта идея? Есть ли способ лучше?

Этот код не используется ни на одной производственной мощности, и я просто пытаюсь изучить его.

0 ответов

Другие вопросы по тегам