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
相关产品推荐
相关产品推荐

