You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring Boot JpaRepository无法执行save:交易实体未保存无报错

问题描述

我在构建一个Spring Boot简易应用时遇到实体保存问题:银行账户能正常存入数据库,但交易实体无法保存。
诡异的是没有任何报错信息,调用transactionRepository.save()后方法直接退出,应用仍正常运行,控制台无提示,调试也没发现异常。

以下是makeTransaction()方法,执行到transactionRepository.save(...)前一切正常:

public void makeTransaction(Iban sourceIban, Iban destinationIban, double amount) 
        throws Exception {
    BankAccountEntity sourceAccount = getBankAccountByIban(sourceIban);

    BankAccountEntity destinationAccount = getBankAccountByIban(destinationIban);

    Date currentDate = new Date();
    TransactionEntity sourceTransaction = new TransactionEntity(sourceAccount, sourceIban, destinationIban, currentDate, amount);
    TransactionEntity destinationTransaction = new TransactionEntity(destinationAccount, sourceIban, destinationIban, currentDate, amount);

    if (sourceAccount.getAmountDeductible() < amount || amount < 0) { // TODO amount checker
        throw new Exception(ILLEGAL_TRANSACTION_AMOUNT_TEXT); // TODO change exception type
    }

    sourceAccount.reduceBalance(amount);
    sourceAccount.addTransaction(sourceTransaction);
    destinationAccount.increaseBalance(amount);
    destinationAccount.addTransaction(destinationTransaction);
    bankAccountRepository.save(sourceAccount);
    bankAccountRepository.save(destinationAccount);
    transactionRepository.save(sourceTransaction);
    transactionRepository.saveAndFlush(destinationTransaction);
}

类中仓库初始化代码:

private final BankAccountRepository bankAccountRepository;
private final TransactionRepository transactionRepository;

public BankAccountService(@Autowired BankAccountRepository bankAccountRepository,
                          @Autowired TransactionRepository transactionRepository) {
    this.bankAccountRepository = bankAccountRepository;
    this.transactionRepository = transactionRepository;
}

BankAccountRepository定义:

public interface BankAccountRepository extends JpaRepository<BankAccountEntity, UUID> {
    Optional<BankAccountEntity> findByIban(String iban);
}

TransactionRepository定义:

public interface TransactionRepository extends JpaRepository<TransactionEntity, UUID> {}

TransactionEntity实体类:

@Entity
@Table(name = "transactions")
public class TransactionEntity {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "transaction_id", nullable = false)
    @Type(type = "uuid-char")
    private Long transactionId;

    private String sourceIban;

    private String destinationIban;

    private long dateExecuted;

    private double amount;

    @ManyToOne
    @JoinColumn(name = "bank_account_id")
    private BankAccountEntity bankAccount;

    public TransactionEntity() {
    }

    public TransactionEntity(BankAccountEntity bankAccount, Iban sourceIban, Iban destinationIban, Date dateExecuted, double amount) {
        this.bankAccount = bankAccount;
        this.sourceIban = sourceIban.toString();
        this.destinationIban = destinationIban.toString();
        this.amount = amount;
        this.dateExecuted = dateExecuted.getTime();
    }

    public BankAccountEntity getBankAccount() {
        return bankAccount;
    }

    public void setBankAccount(BankAccountEntity bankAccount) {
        this.bankAccount = bankAccount;
    }

    public Long getTransactionId() {
        return transactionId;
    }

    public void setTransactionId(Long transactionId) {
        this.transactionId = transactionId;
    }
}

问题分析与解决方法

1. 主键类型不匹配(核心问题)

TransactionRepository继承JpaRepository<TransactionEntity, UUID>,指定主键类型为UUID,但TransactionEntity的transactionId却是Long类型,还错误添加了@Type(type="uuid-char")注解,这会导致JPA无法正确生成和映射主键,最终静默失败。

修复步骤:
修改TransactionEntity的主键类型为UUID:

@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name = "transaction_id", nullable = false)
@Type(type = "uuid-char") // 保持该注解适配数据库CHAR(36)类型的UUID
private UUID transactionId;

// 同时更新对应的getter和setter
public UUID getTransactionId() {
    return transactionId;
}

public void setTransactionId(UUID transactionId) {
    this.transactionId = transactionId;
}

2. 缺失事务管理注解

makeTransaction()方法涉及多个数据库操作(更新账户、保存交易),没有添加@Transactional注解会导致操作不在同一个事务上下文内,可能出现部分执行、静默失败的情况。

修复步骤:
在makeTransaction()方法上添加@Transactional注解:

import org.springframework.transaction.annotation.Transactional;

@Transactional
public void makeTransaction(Iban sourceIban, Iban destinationIban, double amount) 
        throws Exception {
    // 原有代码不变
}

3. 关联关系的级联优化(可选)

如果BankAccountEntity与TransactionEntity是双向关联,可通过配置级联操作简化代码,无需单独调用transactionRepository.save():

在BankAccountEntity的交易集合字段上添加级联属性:

@OneToMany(mappedBy = "bankAccount", cascade = CascadeType.ALL, orphanRemoval = true)
private List<TransactionEntity> transactions = new ArrayList<>();

// addTransaction方法确保双向关联正确
public void addTransaction(TransactionEntity transaction) {
    transactions.add(transaction);
    transaction.setBankAccount(this); // 关键:设置反向关联
}

这样在调用bankAccountRepository.save(sourceAccount)时,会自动级联保存关联的交易实体,无需再单独调用交易仓库的保存方法。

内容的提问来源于stack exchange,提问作者fipps omat

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.03 23:55:28