简体   繁体   English

Javamoney.moneta.Money 序列化/反序列化为 null 尽管我有 jackson-datatype-money 依赖项

[英]Javamoney.moneta.Money serialized/deserialized as null although I have jackson-datatype-money dependency

I just want to provide a rest endpoint where the user can send the money amount.我只想提供一个 rest 端点,用户可以在其中发送金额。 I decided to use javamoney.moneta.Money for the first time and it has been persisted in Postgres as null.我决定第一次使用 javamoney.moneta.Money,它在 Postgres 中被保留为 null。

Here is the model:这是 model:

import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.Lob;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
import javax.validation.constraints.NotNull;

import org.hibernate.annotations.OnDelete;
import org.hibernate.annotations.OnDeleteAction;
import org.javamoney.moneta.Money;


import com.fasterxml.jackson.annotation.JsonIdentityInfo;
import com.fasterxml.jackson.annotation.JsonIdentityReference;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.ObjectIdGenerators;

@Entity
@Table(name = "accounts")
public class Account extends AuditModel  {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @NotNull
    private Money money;

    @ManyToOne(fetch = FetchType.LAZY, optional = false)
    @JoinColumn(name = "person_id", nullable = false)
    @OnDelete(action = OnDeleteAction.CASCADE)
    @JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id")
    @JsonIdentityReference(alwaysAsId = true)
    @JsonProperty("person_id")
    private Person person;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public Money getMoney() {
        return money;
    }

    public void setMoney(Money money) {
        this.money = money;
    }

    public Person getPerson() {
        return person;
    }

    public void setPerson(Person person) {
        this.person = person;
    }
    
}

In case it is relevant, here is the related model for join column如果相关,这里是连接列的相关 model

import java.util.Date;

import javax.persistence.*;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;

@Entity
@Table(name = "persons")
public class Person extends AuditModel {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @NotNull
    @Size(max = 50)
    @Column(unique = true)
    private String name;

    @NotNull
    private String cpf;

    @NotNull
    @Temporal(TemporalType.DATE)
    private Date birthDate;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getCpf() {
        return cpf;
    }

    public void setCpf(String cpf) {
        this.cpf = cpf;
    }

    public Date getBirthDate() {
        return birthDate;
    }

    public void setBirthDate(Date birthDate) {
        this.birthDate = birthDate;
    }
    
   
}

And its AuditModel及其 AuditModel

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;

import javax.persistence.*;
import java.io.Serializable;
import java.util.Date;

@MappedSuperclass
@EntityListeners(AuditingEntityListener.class)
@JsonIgnoreProperties(value = { "createdAt", "updatedAt" }, allowGetters = true)
public abstract class AuditModel implements Serializable {
    @Temporal(TemporalType.TIMESTAMP)
    @Column(name = "created_at", nullable = false, updatable = false)
    @CreatedDate
    private Date createdAt;

    @Temporal(TemporalType.TIMESTAMP)
    @Column(name = "updated_at", nullable = false)
    @LastModifiedDate
    private Date updatedAt;

    public Date getCreatedAt() {
        return createdAt;
    }

    public void setCreatedAt(Date createdAt) {
        this.createdAt = createdAt;
    }

    public Date getUpdatedAt() {
        return updatedAt;
    }

    public void setUpdatedAt(Date updatedAt) {
        this.updatedAt = updatedAt;
    }
}

Repository存储库

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

import com.mybank.accountmanagement.model.Account;

@Repository
public interface AccountRepository extends JpaRepository<Account, Long> {

}

And the AccountController exposing the endpoint以及暴露端点的 AccountController

import javax.validation.Valid;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

import com.mybank.accountmanagement.jpa.exception.*;
import com.mybank.accountmanagement.model.Account;
import com.mybank.accountmanagement.repository.AccountRepository;
import com.mybank.accountmanagement.repository.PersonRepository;

@RestController
public class AccountController {
    @Autowired
    AccountRepository accountRepository;

    @Autowired
    PersonRepository personRepository;

    @PostMapping("/accounts/{personId}")
    public Account createAccount(@PathVariable(value = "personId") Long personId, @Valid @RequestBody Account account) {
        return personRepository.findById(personId).map(person -> {
            account.setPerson(person);
            return accountRepository.save(account);
        }).orElseThrow(() -> new ResourceNotFoundException("PersonId " + personId + " not found"));
    }
}

Here is how I am calling the rest endpoint这是我如何调用 rest 端点

curl --location --request POST 'localhost:2000/accounts/3' --header 'Content-Type: application/json' --data-raw '{
  "amount": 29.95,
  "currency": "EUR",
  "formatted": "29,95 EUR"
}'

Which results in creating a new record in Account table but with Money column as null这导致在Account表中创建一条新记录,但Money列为 null

I tried followed this suggestion by adding jackson-datatype-money Here is my POM我尝试通过添加jackson-datatype-money来遵循这个建议这是我的POM

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.3.2.RELEASE</version>
        <relativePath /> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.mybank</groupId>
    <artifactId>accountmanagement</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>accountmanagement</name>
    <description>Test</description>

    <properties>
        <java.version>11</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.postgresql</groupId>
            <artifactId>postgresql</artifactId>
            <scope>runtime</scope>
        </dependency>


        <dependency>
            <groupId>javax.validation</groupId>
            <artifactId>validation-api</artifactId>
            <version>2.0.1.Final</version>
        </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.javamoney</groupId>
            <artifactId>moneta</artifactId>
            <version>1.3</version>
            <type>pom</type>
        </dependency>


        <dependency>
            <groupId>org.zalando</groupId>
            <artifactId>jackson-datatype-money</artifactId>
            <version>1.2.0</version>
        </dependency>


    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

In case it adds some usefull point, this unit test is passing如果它增加了一些有用的点,则此单元测试通过

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

import org.javamoney.moneta.Money;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
import org.springframework.boot.test.autoconfigure.orm.jpa.TestEntityManager;
import org.springframework.test.context.junit4.SpringRunner;

import com.mybank.accountmanagement.model.Account;
import com.mybank.accountmanagement.model.Person;
import com.mybank.accountmanagement.repository.AccountRepository;
import com.mybank.accountmanagement.repository.PersonRepository;

@RunWith(SpringRunner.class)
@DataJpaTest(showSql = true)
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
public class JpaAccountUnitTest {

    @Autowired
    PersonRepository personRepository;

    @Autowired
    AccountRepository accountRepository;

    @Autowired
    private TestEntityManager entityManager;

    @Test
    public void should_save_an_account() {

        Person p1 = new Person();
        p1.setCpf("12345678901");
        p1.setName("Fulano de tal");
        try {
            p1.setBirthDate(new SimpleDateFormat("yyyy-MM-dd").parse("1978-04-14"));
        } catch (ParseException e) {
            e.printStackTrace();
        }
        p1.setCreatedAt(new Date());
        p1.setUpdatedAt(new Date());

        Person personSaved = personRepository.save(p1);

        Account ac = new Account();
        ac.setPerson(personSaved);
        ac.setMoney(Money.of(1, "EUR"));
        ac.setCreatedAt(new Date());
        ac.setUpdatedAt(new Date());
        
        Account accountSaved = accountRepository.save(ac);

        Assert.assertEquals(accountSaved.getMoney(), Money.of(1, "EUR")); 

    }
}

And finally where I believe I am doing something wrong, on controller endpoint it is fill with null.最后,我认为我做错了什么,在 controller 端点上,它充满了 null。

调试视图

You have to add Serializer and Deserialize in a Config class (annotated with @Configuration)您必须在配置 class 中添加序列化程序和反序列化(使用 @Configuration 注释)

With zalando / jackson-datatype-money, you could just add this bean使用 zalando / jackson-datatype-money,您可以添加这个 bean

    @Bean
    public MoneyModule moneyModule() {
        return new MoneyModule();
    }

cf https://github.com/andrewhj/jackson-money-demo cf https://github.com/andrewhj/jackson-money-demo

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM