簡體   English   中英

<SpringBoot / Hibernate>調用 JpaRepository.findAll 時的 InvocationException(示例)

[英]<SpringBoot / Hibernate> InvocationException on calling JpaRepository.findAll(Example example)

我注意到在 H2 數據庫上執行 JpaRepository.findAll(Example example) 時返回了 InvocationException。

當我嘗試配置“帳戶”和“交易”這兩個表之間的外鍵關系時會發生這種情況(即一個帳戶可以有多個交易,但一個交易只能屬於一個帳戶)。

在我添加 @OneToMany 和 @ManyToOne 注釋之前,沒有任何問題。

歡迎任何幫助,謝謝。

要求:

在此處輸入圖片說明

查詢成功,但它給出了一個 InvocationException,它反過來給出了 HTTP500。

在此處輸入圖片說明

服務:

賬戶服務.java

...
......
public List<Transaction> getAllTransactions(Account account) {

    TransactionPK inputTransactionPK = new TransactionPK();
    inputTransactionPK.setAccountNum(account.getAccountNum());

    Transaction inputTransaction = new Transaction();
    inputTransaction.setTransactionPK(inputTransactionPK);

    ExampleMatcher matcher = ExampleMatcher.matchingAll().withIgnorePaths("debitAmt", "creditAmt");

    Example<Transaction> example = Example.of(inputTransaction, matcher);

    List<Transaction> transactionList = transactionRepository.findAll(example);

    log.info("##################################################\n"
            + "Retrieved transaction list for account with account number " + account.getAccountNum()
            + "\n##################################################");

    return transactionList;
}
...
......

表型號:

賬號.java

package com.somecompany.account.model;

import java.sql.Timestamp;
import java.util.Set;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.validation.constraints.DecimalMin;
import javax.validation.constraints.Max;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.Pattern;
import javax.validation.constraints.Size;

import lombok.Data;

@Entity
@Data
public class Account {

    @OneToMany(mappedBy = "account")
    private Set<Transaction> transaction;

    @Column(name = "cust_id")
    @NotEmpty(message = "Customer ID cannot be null nor empty")
    @Pattern(regexp = "^[0-9]+$", message = "Customer ID must be a number")
    @Min(value = 1L, message = "Customer ID must not be less than 1")
    @Max(value = 9999999999L, message = "Customer ID must not be larger than 9999999999")
    private long custId;

    @Column(name = "account_num")
    @Id
    @NotEmpty(message = "Account number cannot be null nor empty")
    @Pattern(regexp = "^[0-9]+$", message = "Account number must be a number")
    @Min(value = 1L, message = "Account number  must not be less than 1")
    @Max(value = 9999999999L, message = "Account number must not be larger than 9999999999")
    private long accountNum;

    @Column(name = "account_name")
    @NotEmpty(message = "Account name cannot be null nor empty")
    @Size(min = 1, max = 30, message = "Account name must have length between 1 and 30")
    private String accountName;

    @Column(name = "account_type")
    @NotEmpty(message = "Account type cannot be null nor empty")
    @Size(min = 1, max = 7, message = "Account type must have length between 1 and 7")
    private String accountType;

    @Column(name = "balance_date")
    @NotEmpty(message = "Balance date cannot be null nor empty")
    private Timestamp balanceDate;

    @Column(name = "currency")
    @NotEmpty(message = "Currency cannot be null nor empty")
    @Size(min = 3, max = 3, message = "Currency must have length exactly equal to 3")
    private String currency;

    @Column(name = "opening_available_balance", columnDefinition = "Decimal(20,2) default '0.0'")
    @NotEmpty(message = "Opening available balance cannot be null nor empty")
    @Pattern(regexp = "^[0-9.]+$", message = "Opening available balance must be a decimal number")
    @DecimalMin(value = "0.0", message = "Opening available balance cannot be negative")
    private float openingAvailableBalance;
}

事務.java

package com.somecompany.account.model;

import javax.persistence.Column;
import javax.persistence.EmbeddedId;
import javax.persistence.Entity;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.validation.constraints.DecimalMin;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.Size;

import lombok.Data;

@Entity
@Data
public class Transaction {

    @ManyToOne
    @JoinColumn(name = "account_num", referencedColumnName = "account_num", insertable = false, updatable = false, nullable = false)
    private Account account;

    @EmbeddedId
    private TransactionPK transactionPK;

    @Column(name = "account_name")
    @NotEmpty(message = "Account name cannot be null nor empty")
    @Size(min = 1, max = 30, message = "Account name must have length between 1 and 30")
    private String accountName;

    @Column(name = "currency")
    @NotEmpty(message = "Currency cannot be null nor empty")
    @Size(min = 3, max = 3, message = "Currency must have length exactly equal to 3")
    private String currency;

    @Column(name = "debit_amt", columnDefinition = "Decimal(20,2) default '0.0'")
    @NotEmpty(message = "Debit amount cannot be null nor empty")
    @DecimalMin(value = "0.0", message = "Debit amount cannot be negative")
    private float debitAmt;

    @Column(name = "credit_amt", columnDefinition = "Decimal(20,2) default '0.0'")
    @NotEmpty(message = "Credit amount cannot be null nor empty")
    @DecimalMin(value = "0.0", message = "Credit amount cannot be negative")
    private float creditAmt;

    @Column(name = "debit_credit")
    @NotEmpty(message = "Debit/Credit cannot be null nor empty")
    @Size(min = 1, max = 6, message = "Debit/Credit must have length between 1 and 6")
    private String debitCredit;

    @Column(name = "transaction_narrative")
    @Size(min = 0, max = 50, message = "Transaction narrative must have length between 0 and 50")
    private String transactionNarrative;
}

事務PK.java

package com.somecompany.account.model;

import java.io.Serializable;
import java.sql.Timestamp;

import javax.persistence.Column;
import javax.persistence.Embeddable;
import javax.validation.constraints.Max;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.Pattern;

import lombok.Data;

@Embeddable
@Data
public class TransactionPK implements Serializable {

    /**
    * 
    */
    private static final long serialVersionUID = 1L;

    @Column(name = "account_num")
    @NotEmpty(message = "Account number cannot be null nor empty")
    @Pattern(regexp = "^[0-9]+$", message = "Account number must be a number")
    @Min(value = 1L, message = "Account number  must not be less than 1")
    @Max(value = 9999999999L, message = "Account number must not be larger than 9999999999")
    private long accountNum;

    @Column(name = "value_date")
    @NotEmpty(message = "Value date cannot be null nor empty")
    private Timestamp valueDate;
}

H2 DB 主外鍵信息:

在此處輸入圖片說明

SpringBoot 應用程序啟動時的示例數據庫數據 (data.sql):

INSERT INTO ACCOUNT (cust_id, account_num, account_name, account_type, balance_date, currency, opening_available_balance) VALUES
(1111111111, 1111111111, 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA', 'Savings', TIMESTAMP '2020-11-01 11:01:01', 'SGD', 99999.99),
(2, 2, 'B', 'Savings', TIMESTAMP '2020-11-02 11:02:02', 'AUD', 0.0),
(1111111111, 3333333333, 'CCCCCCCCCCCCCCCCCCCCCCCCCCCCCC', 'Current', TIMESTAMP '2020-11-03 11:03:03', 'USD', 99999.99);

INSERT INTO TRANSACTION (account_num, account_name, value_date, currency, debit_amt, credit_amt, debit_credit, transaction_narrative) VALUES
(1111111111, 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA', TIMESTAMP '2012-11-01 11:01:01', 'SGD', 0.0, 99999.99, 'Credit', 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'),
(2, 'Savings Account', TIMESTAMP '2012-11-02 11:02:02', 'USD', 0.1, 0.0, 'Debit', null),
(1111111111, 'CCCCCCCCCCCCCCCCCCCCCCCCCCCCCC', TIMESTAMP '2012-11-03 11:03:03', 'USD', 99999.99, 0.0, 'Debit', 'CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC');

在此處輸入圖片說明

經過一番調查,我進行了以下更改,應用程序最終按預期運行。

賬號.java

import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.List;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.validation.constraints.DecimalMin;
import javax.validation.constraints.Max;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.Pattern;
import javax.validation.constraints.Size;

import com.fasterxml.jackson.annotation.JsonBackReference;

import lombok.Getter;
import lombok.Setter;

@Entity
@Getter
@Setter
/**
* The model class for "Account" table.
* 
* @author patrick
*
*/
public class Account {

    @OneToMany(mappedBy = "transactionPK.account")
    @JsonBackReference
    private List<Transaction> transactions = new ArrayList<>();

    @Column(name = "cust_id")
    @NotEmpty(message = "Customer ID cannot be null nor empty")
    @Pattern(regexp = "^[0-9]+$", message = "Customer ID must be a number")
    @Min(value = 1L, message = "Customer ID must not be less than 1")
    @Max(value = 9999999999L, message = "Customer ID must not be larger than 9999999999")
    private long custId;

    @Column(name = "account_num")
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @NotEmpty(message = "Account number cannot be null nor empty")
    @Pattern(regexp = "^[0-9]+$", message = "Account number must be a number")
    @Min(value = 1L, message = "Account number  must not be less than 1")
    @Max(value = 9999999999L, message = "Account number must not be larger than 9999999999")
    private long accountNum;

    @Column(name = "account_name")
    @NotEmpty(message = "Account name cannot be null nor empty")
    @Size(min = 1, max = 30, message = "Account name must have length between 1 and 30")
    private String accountName;

    @Column(name = "account_type")
    @NotEmpty(message = "Account type cannot be null nor empty")
    @Size(min = 1, max = 7, message = "Account type must have length between 1 and 7")
    private String accountType;

    @Column(name = "balance_date")
    @NotEmpty(message = "Balance date cannot be null nor empty")
    private Timestamp balanceDate;

    @Column(name = "currency")
    @NotEmpty(message = "Currency cannot be null nor empty")
    @Size(min = 3, max = 3, message = "Currency must have length exactly equal to 3")
    private String currency;

    @Column(name = "opening_available_balance", columnDefinition = "Decimal(20,2) default '0.0'")
    @NotEmpty(message = "Opening available balance cannot be null nor empty")
    @Pattern(regexp = "^[0-9.]+$", message = "Opening available balance must be a decimal number")
    @DecimalMin(value = "0.0", message = "Opening available balance cannot be negative")
    private float openingAvailableBalance;
}

事務.java

import javax.persistence.Column;
import javax.persistence.EmbeddedId;
import javax.persistence.Entity;
import javax.validation.constraints.DecimalMin;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.Size;

import lombok.Getter;
import lombok.Setter;

@Entity
@Getter
@Setter
/**
* The model class for "Transaction" table.
* 
* @author patrick
*
*/
public class Transaction {

    @EmbeddedId
    private TransactionPK transactionPK;

    @Column(name = "account_name")
    @NotEmpty(message = "Account name cannot be null nor empty")
    @Size(min = 1, max = 30, message = "Account name must have length between 1 and 30")
    private String accountName;

    @Column(name = "currency")
    @NotEmpty(message = "Currency cannot be null nor empty")
    @Size(min = 3, max = 3, message = "Currency must have length exactly equal to 3")
    private String currency;

    @Column(name = "debit_amt", columnDefinition = "Decimal(20,2) default '0.0'")
    @NotEmpty(message = "Debit amount cannot be null nor empty")
    @DecimalMin(value = "0.0", message = "Debit amount cannot be negative")
    private float debitAmt;

    @Column(name = "credit_amt", columnDefinition = "Decimal(20,2) default '0.0'")
    @NotEmpty(message = "Credit amount cannot be null nor empty")
    @DecimalMin(value = "0.0", message = "Credit amount cannot be negative")
    private float creditAmt;

    @Column(name = "debit_credit")
    @NotEmpty(message = "Debit/Credit cannot be null nor empty")
    @Size(min = 1, max = 6, message = "Debit/Credit must have length between 1 and 6")
    private String debitCredit;

    @Column(name = "transaction_narrative")
    @Size(min = 0, max = 50, message = "Transaction narrative must have length between 0 and 50")
    private String transactionNarrative;
}

事務PK.java

import java.io.Serializable;
import java.sql.Timestamp;

import javax.persistence.Column;
import javax.persistence.Embeddable;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.validation.constraints.NotEmpty;

import com.fasterxml.jackson.annotation.JsonManagedReference;

import lombok.Data;

@Embeddable
@Data
/**
* The model class for the EmbeddedId (i.e. primary key) of the "Transaction" table.
* 
* @author patrick
*
*/
public class TransactionPK implements Serializable {

    /**
    * 
    */
    private static final long serialVersionUID = 1L;

    @ManyToOne
    @JoinColumn(name = "account_num", referencedColumnName = "account_num", insertable = false, updatable = false, nullable = false)
    @JsonManagedReference
    private Account account;

    @Column(name = "value_date")
    @NotEmpty(message = "Value date cannot be null nor empty")
    private Timestamp valueDate;
}

我在 TransactionPK 中創建了一個“account”字段來替換“account_num”字段(無論如何,帳戶對象已經具有“account_num”信息),並用@ManyToOne 對其進行了注釋。 這是因為關系是“一個賬戶可以有多個交易(即交易列表),但一個交易只屬於一個賬戶”。 關系是在對象級別而不是字段級別。

對於“Account”中的“List transactions”和“TransactionPK”中的“Account account”,它們僅用於指示外鍵關系,它們不必存在於JSON文件中。 如果我們就這樣保留它,在序列化為 JSON 時會出現無限遞歸錯誤(因為每個都有另一個元素,它永遠無法完成生成 JSON)。

為了解決這個問題,我們可以用@JsonIgnore 標記這些字段,這將跳過序列化這兩個字段。 或者,如果我們需要顯示一個而不是另一個(例如,在交易 JSON 中顯示“帳戶”,但在帳戶 JSON 中不顯示“交易”),那么我們可以用@JsonManagedReference 注釋我們想要保留的那個,並標記我們不想用@JsonBackReference 在JSON 中顯示的那個。

參考: https ://www.baeldung.com/jackson-bidirectional-relationships-and-infinite-recursion

我所做的另一個更改是不使用@Data。 當您處理實體時,避免使用 lombok 中的 @Data,如果它們將與 JPA 等 ORM 一起使用。@Data 的 equals() 和 hashCode() 實現可能會干擾對象比較。

參考: https ://thorben-janssen.com/lombok-hibernate-how-to-avoid-common-pitfalls/

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM