发布时间:2026/7/15 8:14:35
DDD学习第4课仓储与持久化隔离(使用数据库) 第4课仓储模式与持久化隔离本节目标理解仓储Repository的作用使用Spring Data JPA将Order聚合保存到数据库保持领域层“纯净”不依赖框架展示Order聚合的数据库持久化与查询仓储Repository的职责在 DDD 中仓储是领域层与持久化层的分界线。角色职责领域层只关心业务对象Order、Menu 等仓储接口定义持久化操作save、findById基础设施层实现通过 JPA / MyBatis / Redis 实现实际的数据访问重点仓储接口放在domain.repository包里属于领域层。实现类放在infrastructure.repository包里属于基础设施层。修改pom.xml添加 Spring Data JPA 和 H2 数据库依赖内存数据库方便演示dependencies!-- Web 基础依赖 --dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-web/artifactId/dependency!-- 数据持久化Spring Data JPA --dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-data-jpa/artifactId/dependency!-- 内存数据库 H2 --dependencygroupIdcom.h2database/groupIdartifactIdh2/artifactIdscoperuntime/scope/dependency/dependencies持久化实体设计我们不能直接把领域模型Order当成 JPA 实体那会污染领域层。所以我们新增一个持久化对象Entity来对应数据库结构。OrderEntity.javapackagecom.example.ordersystem.infrastructure.persistence.entity;importjakarta.persistence.*;importjava.util.ArrayList;importjava.util.List;/** * JPA 实体对象仅用于持久化 * 与领域模型 Order 分离 */EntityTable(nameorders)publicclassOrderEntity{IdprivateStringid;privateStringstatus;privatedoubletotalAmount;// 一对多关联到 OrderItemEntityOneToMany(mappedByorder,cascadeCascadeType.ALL,orphanRemovaltrue)privateListOrderItemEntityitemsnewArrayList();publicOrderEntity(){}publicOrderEntity(Stringid,Stringstatus,doubletotalAmount){this.idid;this.statusstatus;this.totalAmounttotalAmount;}publicStringgetId(){returnid;}publicStringgetStatus(){returnstatus;}publicdoublegetTotalAmount(){returntotalAmount;}publicListOrderItemEntitygetItems(){returnitems;}publicvoidaddItem(OrderItemEntityitem){item.setOrder(this);items.add(item);}}OrderItemEntity.javapackagecom.example.ordersystem.infrastructure.persistence.entity;importjakarta.persistence.*;/** * JPA 实体对象订单项 */EntityTable(nameorder_items)publicclassOrderItemEntity{IdGeneratedValue(strategyGenerationType.IDENTITY)privateLongid;privateStringname;privateintquantity;privatedoubleprice;ManyToOne(fetchFetchType.LAZY)JoinColumn(nameorder_id)privateOrderEntityorder;publicOrderItemEntity(){}publicOrderItemEntity(Stringname,intquantity,doubleprice){this.namename;this.quantityquantity;this.priceprice;}publicvoidsetOrder(OrderEntityorder){this.orderorder;}// Getter 省略可补充}JPA Repository 接口在基础设施层定义 Spring Data 的仓储接口。packagecom.example.ordersystem.infrastructure.persistence.jpa;importcom.example.ordersystem.infrastructure.persistence.entity.OrderEntity;importorg.springframework.data.jpa.repository.JpaRepository;importorg.springframework.stereotype.Repository;/** * JPA仓储接口 * 直接继承Spring Data JpaRepository */RepositorypublicinterfaceJpaOrderRepositoryextendsJpaRepositoryOrderEntity,String{}仓储实现类DDD Repository 实现我们要把领域模型Order ↔ 持久化实体OrderEntity互相转换。packagecom.example.ordersystem.infrastructure.repository;importcom.example.ordersystem.domain.order.model.*;importcom.example.ordersystem.domain.order.repository.OrderRepository;importcom.example.ordersystem.infrastructure.persistence.entity.OrderEntity;importcom.example.ordersystem.infrastructure.persistence.entity.OrderItemEntity;importcom.example.ordersystem.infrastructure.persistence.jpa.JpaOrderRepository;importorg.springframework.stereotype.Repository;RepositorypublicclassJpaOrderRepositoryImplimplementsOrderRepository{ResourceLazyJpaOrderRepositoryjpaRepository;Overridepublicvoidsave(Orderorder){// 将领域模型转换为 JPA 实体OrderEntityentitynewOrderEntity(order.getId().getValue(),order.getStatus().name(),order.getTotalAmount().getAmount());// 添加 OrderItemorder.getItems().forEach(item-{OrderItemEntityitemEntitynewOrderItemEntity(item.getName(),item.getQuantity(),item.getPrice().getAmount());entity.addItem(itemEntity);});jpaRepository.save(entity);}OverridepublicOrderfindById(OrderIdid){OrderEntityentityjpaRepository.findById(id.getValue()).orElseThrow(()-newRuntimeException(订单不存在: id.getValue()));// 将 JPA 实体转换回领域模型OrderordernewOrder(newOrderId(entity.getId()));entity.getItems().forEach(item-order.addMenuItem(// 使用 MenuItem 模拟此时价格已持久化newcom.example.ordersystem.domain.menu.model.MenuItem(item.getName(),item.getPrice()),item.getQuantity()));// 恢复状态switch(entity.getStatus()){casePAID-order.pay();caseCONFIRMED-order.confirm();caseCOMPLETED-order.complete();caseCANCELLED-order.cancel();}returnorder;}}修改OrderLifecycleServiceimportcom.example.ordersystem.domain.menu.model.Menu;importcom.example.ordersystem.domain.menu.model.MenuItem;importcom.example.ordersystem.domain.order.model.Order;importcom.example.ordersystem.domain.order.model.OrderId;importcom.example.ordersystem.domain.order.repository.OrderRepository;importorg.springframework.stereotype.Service;/** * 应用服务管理订单生命周期 */ServicepublicclassOrderLifecycleService{privatefinalOrderRepositoryrepository;privatefinalMenumenu;publicOrderLifecycleService(OrderRepositoryrepository,Menumenu){this.repositoryrepository;this.menumenu;}/** 创建订单 */publicOrdercreateOrder(){OrderordernewOrder(OrderId.newId());MenuItemfriedRicemenu.findItem(Fried Rice);MenuItemcokemenu.findItem(Coke);order.addMenuItem(friedRice,1);order.addMenuItem(coke,2);repository.save(order);returnorder;}publicOrderpayOrder(StringorderId){Orderorderrepository.findById(newOrderId(orderId));order.pay();repository.save(order);returnorder;}publicOrderconfirmOrder(StringorderId){Orderorderrepository.findById(newOrderId(orderId));order.confirm();repository.save(order);returnorder;}}修改OrderSystemApplicationimportorg.springframework.boot.SpringApplication;importorg.springframework.boot.autoconfigure.SpringBootApplication;importorg.springframework.context.annotation.Bean;importorg.springframework.scheduling.annotation.EnableAsync;importorg.springframework.web.client.RestTemplate;importcom.example.ordersystem.domain.menu.model.Menu;importcom.example.ordersystem.domain.menu.model.MenuId;importorg.springframework.boot.SpringApplication;importorg.springframework.boot.autoconfigure.SpringBootApplication;importorg.springframework.context.annotation.Bean;EnableAsyncSpringBootApplication(scanBasePackages{com.example})publicclassOrderSystemApplication{publicstaticvoidmain(String[]args){SpringApplication.run(OrderSystemApplication.class,args);}/** * 初始化菜单 */BeanpublicMenuinitMenu(){MenumenunewMenu(MenuId.newId());menu.addItem(Fried Rice,12.5);menu.addItem(Coke,3.0);returnmenu;}}删除InMemoryOrderRepository配置数据库在application.yml中添加spring:datasource:url:jdbc:h2:file:d:/data/testdb;DB_CLOSE_DELAY-1;DB_CLOSE_ON_EXITFALSEdriver-class-name:org.h2.Driverusername:sapassword:jpa:hibernate:ddl-auto:updateshow-sql:true测试运行启动应用后访问POST http://localhost:8080/orders/create观察控制台输出的 SQLinsertintoorders...insertintoorder_items...表示订单已经被成功持久化到数据库。你可以再访问POST http://localhost:8080/orders/{id}/pay会发现状态被正确更新数据库中的记录也会变化。第4课总结要点说明仓储接口domain定义存取方法不依赖技术框架仓储实现infrastructure实现持久化细节使用 JPAJPA 实体映射数据库表不影响领域模型领域层依旧纯净不直接使用 JPA 注解或依赖 Spring

相关新闻

2026/7/15 8:09:34

Linux shadow 文件详解:恢复 root 密码的完整指南

一. shadow 文件简介在 Linux 系统中,/etc/shadow 文件用于存储用户密码的加密信息以及密码相关的策略属性。它与 /etc/passwd 文件配合使用,passwd 文件保存用户基本信息(且对所有用户可读),而 shadow 文件中存放的加…

2026/7/15 8:09:34

PAGravf重力场逼近的误差分析与精度评定原理及方法

来源:地球物理大地测量科学计算平台,章传银地球重力场无缝连续地充满于整个地固空间,我们只能用一定分辨率的格网空间平均值来表达重力场量,这是重力场量区别于一般离散点几何大地测量要素的典型特征,重力场逼近的误差…

2026/7/15 9:14:37

GLM-5.2-colibri-int4社区贡献指南:如何参与项目开发与优化

GLM-5.2-colibri-int4社区贡献指南:如何参与项目开发与优化 【免费下载链接】GLM-5.2-colibri-int4 项目地址: https://ai.gitcode.com/hf_mirrors/jlnsrk/GLM-5.2-colibri-int4 GLM-5.2-colibri-int4是基于colibr引擎的INT4量化模型,专为在消费…

2026/7/15 9:14:37

如何在Apple Silicon上部署Ornith-1.0-35B-5bit?超详细图文教程

如何在Apple Silicon上部署Ornith-1.0-35B-5bit?超详细图文教程 【免费下载链接】Ornith-1.0-35B-5bit 项目地址: https://ai.gitcode.com/hf_mirrors/mlx-community/Ornith-1.0-35B-5bit 想要在Apple Silicon设备上运行强大的视觉语言模型吗?Or…

2026/7/15 9:14:37

Model Search:面向工程落地的轻量级神经网络结构搜索框架

1. 项目概述:Model Search 是什么,它解决的到底是什么问题? 你可能已经听说过“AutoML”这个词——它听起来很酷,但实际用起来常常让人困惑:到底是自动调参?还是自动选模型?抑或是连网络结构都给…

2026/7/15 9:09:37

从机械到腾讯:我的两年自学转行与六轮面试闯关实录

1. 从机械齿轮到代码世界:转行前的挣扎与觉醒2019年夏天,我握着武汉理工大学机械工程专业的毕业证书,像大多数同学一样进入了一家知名汽车零部件企业。每天14小时的工作强度,周末单休是常态,到手工资却只有互联网行业应…

2026/7/14 12:47:32

3步解锁音乐自由:ncmdumpGUI终极NCM文件解密转换指南

3步解锁音乐自由:ncmdumpGUI终极NCM文件解密转换指南 【免费下载链接】ncmdumpGUI C#版本网易云音乐ncm文件格式转换,Windows图形界面版本 项目地址: https://gitcode.com/gh_mirrors/nc/ncmdumpGUI 你是否曾在网易云音乐下载了心爱的歌曲&#…

2026/7/14 19:23:19

CANoe 19 SP3 配置 GB/T 27930-2023 A类系统:3步搭建BMS仿真测试环境

CANoe 19 SP3 配置 GB/T 27930-2023 A类系统:3步搭建BMS仿真测试环境随着新能源汽车行业的快速发展,充电通信协议的标准化和测试验证变得尤为重要。GB/T 27930-2023作为中国智能充电协议的最新版本,对充电机与电动汽车之间的通信提出了更严格…

2026/7/14 18:30:06

3步搞定RTL8852BE驱动:从零开始配置Wi-Fi 6网卡

3步搞定RTL8852BE驱动:从零开始配置Wi-Fi 6网卡 【免费下载链接】rtl8852be Realtek Linux WLAN Driver for RTL8852BE 项目地址: https://gitcode.com/gh_mirrors/rt/rtl8852be 还在为Linux系统无法识别RTL8852BE Wi-Fi 6网卡而烦恼吗?&#x1f…

2026/7/15 0:08:29

【LINUX】驱动

【LINUX驱动】【字符设备】【中断】【Platform】【网课 设备树】【GPIO】【PINCTRL】【INPUT】【IIC】【SPI】【网络驱动】【屏幕驱动】【一 设备树】【二 内核模块编译】【三 基本驱动框架】【四 Platform总线设备驱动框架】【五 驱动子系统】【六 综合】

2026/7/15 0:08:29

【1982-2026】全国高精度建筑轮廓|村级精度|SHP矢量

🔍 数据简介 本次分享1982-2026年全国村级精度建筑轮廓矢量数据,覆盖全国各省市区县,到村级别精细,为2026年最新实时采集成果,非网传仅60/77个城市的老旧数据。 数据含带高度/不带高度双版本,单体建筑边界精…

2026/7/15 0:08:29

【1975-2026】全国水系水路数据|河流/水库/运河|SHP矢量

🔍 数据简介 本次分享1975-2026年全国高精度水系水路矢量数据,覆盖全国全域,包含河流、水系、水库、运河、湿地、冰川、沟渠等全类别水文要素。 数据集包含双层矢量图层,字段分类清晰、要素齐全,支持2013-2026逐年完整…

2026/7/14 12:47:31

3个高效策略:快速掌握Axure中文界面配置

3个高效策略:快速掌握Axure中文界面配置 【免费下载链接】axure-cn Chinese language file for Axure RP. Axure RP 简体中文语言包。支持 Axure 11、10、9。不定期更新。 项目地址: https://gitcode.com/gh_mirrors/ax/axure-cn 还在为Axure RP的英文界面感…