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

JPA多表关联插入实现:新增发票时同步插入商品及关联信息

新增发票时同步插入商品及关联信息的实现方案

问题背景

现有三张数据库表:

  • 表A(product,商品表):存储商品基础信息
  • 表B(invoice,发票表):存储发票基础信息
  • 表C(invoice_info,发票信息表):作为关联表,通过invoice_id和product_id关联发票与商品

对应的中文注释实体类如下:

Product(商品实体)

@Entity
@Table(name = "product")
public class Product {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id")
    private Long id; // 商品ID
    @Column(name = "family_id")
    private long familyId; // 商品分类ID
    @Column(name = "product_name")
    private String productName; // 商品名称
    @Column(name = "product_category")
    private String productCategory; // 商品类别
    @Column(name = "product_quantity")
    private int productQuantity; // 商品数量
    // getter和setter方法
}

Invoice(发票实体)

@Entity
@Table(name = "invoice")
public class Invoice {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "invoice_id")
    private Long id; // 发票ID
    @Column(name = "provider_id")
    private Long providerId; // 供应商ID
    @Column(name = "total")
    private int invoiceTotal; // 发票总金额
    @Column(name = "date")
    private Date invoiceDate; // 发票日期
    // getter和setter方法
}

InvoiceInfo(发票关联信息实体)

@Entity
@Table(name = "invoice_info")
public class InvoiceInfo {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "item_id")
    private long id; // 关联记录ID
    @Column(name = "product_id")
    private long productId; // 关联商品ID
    @Column(name = "invoice_id")
    private long invoiceId; // 关联发票ID

    // getter和setter方法
}

实现方案

要实现新增发票的同时插入商品并同步创建关联记录,核心是保证三个操作的原子性(要么全成功,要么全回滚),同时利用JPA生成的自增ID关联三张表。

一、基础实现方式(手动维护关联表)

1. 定义Repository接口

为三个实体创建基础的Repository接口,继承JpaRepository以实现CRUD操作:

// ProductRepository
public interface ProductRepository extends JpaRepository<Product, Long> {
}

// InvoiceRepository
public interface InvoiceRepository extends JpaRepository<Invoice, Long> {
}

// InvoiceInfoRepository
public interface InvoiceInfoRepository extends JpaRepository<InvoiceInfo, Long> {
}

2. 业务逻辑与事务控制

在Service层方法上添加@Transactional注解,确保三个操作在同一个事务中执行:

import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
public class InvoiceService {

    private final ProductRepository productRepository;
    private final InvoiceRepository invoiceRepository;
    private final InvoiceInfoRepository invoiceInfoRepository;

    // 构造函数注入Repository
    public InvoiceService(ProductRepository productRepository,
                          InvoiceRepository invoiceRepository,
                          InvoiceInfoRepository invoiceInfoRepository) {
        this.productRepository = productRepository;
        this.invoiceRepository = invoiceRepository;
        this.invoiceInfoRepository = invoiceInfoRepository;
    }

    @Transactional(rollbackFor = Exception.class)
    public void createInvoiceWithProduct(Product product, Invoice invoice) {
        // 1. 保存商品,获取自动生成的商品ID
        Product savedProduct = productRepository.save(product);
        // 2. 保存发票,获取自动生成的发票ID
        Invoice savedInvoice = invoiceRepository.save(invoice);
        // 3. 创建关联记录并保存
        InvoiceInfo invoiceInfo = new InvoiceInfo();
        invoiceInfo.setProductId(savedProduct.getId());
        invoiceInfo.setInvoiceId(savedInvoice.getId());
        invoiceInfoRepository.save(invoiceInfo);
    }
}

二、优化实现方式(利用JPA关联映射)

如果希望减少手动维护关联表的代码,可以修改实体类,通过@ManyToMany注解让JPA自动维护关联关系:

1. 修改Invoice实体

@Entity
@Table(name = "invoice")
public class Invoice {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "invoice_id")
    private Long id; // 发票ID
    @Column(name = "provider_id")
    private Long providerId; // 供应商ID
    @Column(name = "total")
    private int invoiceTotal; // 发票总金额
    @Column(name = "date")
    private Date invoiceDate; // 发票日期

    // 关联商品,指定中间表为invoice_info
    @ManyToMany
    @JoinTable(
        name = "invoice_info",
        joinColumns = @JoinColumn(name = "invoice_id"),
        inverseJoinColumns = @JoinColumn(name = "product_id")
    )
    private List<Product> products;

    // getter和setter方法
}

2. 简化后的业务逻辑

此时无需手动操作InvoiceInfo,直接保存发票即可自动完成商品保存和关联记录插入:

@Transactional(rollbackFor = Exception.class)
public void createInvoiceWithProduct(Invoice invoice, List<Product> products) {
    invoice.setProducts(products);
    invoiceRepository.save(invoice);
    // JPA会自动保存所有新增的Product,同时在invoice_info中插入关联记录
}

注意:这种方式下,建议将invoice_info的主键改为invoice_id和product_id的联合主键,更符合关联表的设计规范。


内容的提问来源于stack exchange,提问作者Gaya Touak

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.09 18:15:51