简体   繁体   English

<SpringBoot / Hibernate>调用 JpaRepository.findAll 时的 InvocationException(示例)

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

I am noticing an InvocationException being returned when I execute a JpaRepository.findAll(Example example) on H2 database.我注意到在 H2 数据库上执行 JpaRepository.findAll(Example example) 时返回了 InvocationException。

It occurs when I try to configure the foregin key relationship between the 2 tables "Account" and "Transaction" (ie An account can have many transactions, but a transaction can only belong to one account).当我尝试配置“帐户”和“交易”这两个表之间的外键关系时会发生这种情况(即一个帐户可以有多个交易,但一个交易只能属于一个帐户)。

Before I add the @OneToMany and @ManyToOne annotations, there were no issues.在我添加 @OneToMany 和 @ManyToOne 注释之前,没有任何问题。

Welcome for any help, thank you.欢迎任何帮助,谢谢。

Request:要求:

在此处输入图片说明

Query is successful but it gives an InvocationException, which in turns give a HTTP500.查询成功,但它给出了一个 InvocationException,它反过来给出了 HTTP500。

在此处输入图片说明

Service:服务:

AccountService.java账户服务.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;
}
...
......

Table models:表型号:

Account.java账号.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;
}

Transaction.java事务.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;
}

TransactionPK.java事务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 primary and foreign key info: H2 DB 主外键信息:

在此处输入图片说明

Sample DB data on SpringBoot app startup (data.sql): 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');

在此处输入图片说明

After some investigation, I have made the following changes and the application run as expected finally.经过一番调查,我进行了以下更改,应用程序最终按预期运行。

Account.java账号.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;
}

Transaction.java事务.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;
}

TransactionPK.java事务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;
}

I have created an "account" field in TransactionPK replacing the "account_num" field (the account object already has the "account_num" info anyway), and annotated it with @ManyToOne.我在 TransactionPK 中创建了一个“account”字段来替换“account_num”字段(无论如何,帐户对象已经具有“account_num”信息),并用@ManyToOne 对其进行了注释。 This is because the releationship is "An account can have many transactions (ie list of transactions), but a transaction only belongs to one account".这是因为关系是“一个账户可以有多个交易(即交易列表),但一个交易只属于一个账户”。 The releationship is at the object level but not field level.关系是在对象级别而不是字段级别。

For the "List transactions" in "Account" and "Account account" in "TransactionPK", they are for indicating the foreign key relationship only, they don't have to be existing in the JSON files.对于“Account”中的“List transactions”和“TransactionPK”中的“Account account”,它们仅用于指示外键关系,它们不必存在于JSON文件中。 And if we just leave it like that, it will give an infinite recursion error when serializing to JSON (since each has an element of the other, it can never finish generating the JSON).如果我们就这样保留它,在序列化为 JSON 时会出现无限递归错误(因为每个都有另一个元素,它永远无法完成生成 JSON)。

To solve the issue, we may mark these fields with @JsonIgnore, which will skip serializing both fields.为了解决这个问题,我们可以用@JsonIgnore 标记这些字段,这将跳过序列化这两个字段。 Or if we need to show one but not the other (eg show "account" in Transaction JSON, but not showing "transactions" in Account JSON), then we can annotate the one which we want to keep with the @JsonManagedReference, and mark the one that we don't want to show in JSON with @JsonBackReference.或者,如果我们需要显示一个而不是另一个(例如,在交易 JSON 中显示“帐户”,但在帐户 JSON 中不显示“交易”),那么我们可以用@JsonManagedReference 注释我们想要保留的那个,并标记我们不想用@JsonBackReference 在JSON 中显示的那个。

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

Another change I made is not using @Data.我所做的另一个更改是不使用@Data。 Avoid using @Data from lombok when you are working with entities if they will be used with ORM like JPA.The @Data's implementation of equals() and hashCode() may mess with the object comparison.当您处理实体时,避免使用 lombok 中的 @Data,如果它们将与 JPA 等 ORM 一起使用。@Data 的 equals() 和 hashCode() 实现可能会干扰对象比较。

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

暂无
暂无

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

相关问题 findall休眠或条件和示例 - findall hibernate or Criteria and Example 我的jpaRepository.findAll()中的Pageable参数返回SqlSyntaxErrorException - Pageable parameter in my jpaRepository.findAll() returns an SqlSyntaxErrorException Spring JpaRepository findAll(example) 方法返回一个空列表 - Spring JpaRepository findAll(example) method is returning an empty list 我在我的应用程序中使用 JpaRepository。在实现类中,我调用了 findAll(Example<T> ) 但它没有给我适当的回应 - I am using JpaRepository in my application.And in Implementation class im calling the findAll(Example<T>) but its not giving me proper response 为什么JpaRepository.findAll()方法从Oracle数据库中获取错误的数据? - Why is the JpaRepository.findAll() method fetching the wrong data from my Oracle database? 如何使用谓词通过JpaRepository.findAll获得对象的不同列表? - How do I get a distinct list of objects with JpaRepository.findAll using a Predicate? 使用JpaRepository的findAll()导致异常-SpringBoot - findAll() using JpaRepository cause an exception - SpringBoot Hibernate示例忽略大小写而不调用Example.ignoreCase() - Hibernate Example ignores case without calling Example.ignoreCase() 从 JpaRepository 检索 object 后,延迟加载在简单的 Hibernate/Spring 启动示例中不起作用 - Lazy loading doesn't work in simple Hibernate/Spring boot example, after retrieving object from JpaRepository Hibernate findAll(example)抛出“ java.lang.IllegalArgumentException:目标对象不能为空” - Hibernate findAll(example) throws “java.lang.IllegalArgumentException: Target object must not be null”
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM