尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
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
RELATED

相关推荐

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

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

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

📅 2026/11/8 21:53:59
QQ音乐加密音频解密终极指南:qmcdump让你的音乐文件重获自由

QQ音乐加密音频解密终极指南:qmcdump让你的音乐文件重获自由

QQ音乐加密音频解密终极指南:qmcdump让你的音乐文件重获自由 【免费下载链接】qmcdump 一个简单的QQ音乐解码(qmcflac/qmc0/qmc3 转 flac/mp3),仅为个人学习参考用。 项目地址: https://gitcode.com/gh_mirrors/qm/qmcdump …

📅 2026/11/8 21:54:00
PIC18F46K40与M95M02-DR EEPROM的嵌入式数据存储方案

PIC18F46K40与M95M02-DR EEPROM的嵌入式数据存储方案

1. 项目背景与核心需求在嵌入式系统开发中,数据存储的可靠性往往决定着整个系统的稳定性。M95M02-DR这颗2Mbit的EEPROM芯片与PIC18F46K40微控制器的组合,恰好能满足工业级应用对非易失性存储的严苛要求。我最近在一个环境监测设备项目中采用了这套方案&a…

📅 2026/10/23 16:50:12
MORE NEWS

更多资讯

📰

本溪低压电工证学校怎么选才能拿到电子证书上岗?

本溪低压电工证学校怎么选才能拿到电子证书上岗? 你是不是正为找工作发愁?急需一个能让你顺利上岗的特种作业证?低压电工证正是很多行业的“敲门砖”,尤其是在建筑、制造业、电力等领域,没有低压电工证,连面试资格都可能被直接刷掉。但很多人却不知道,…

📰

沙坪坝电工证考点过期了不知道怎么复审延期?含金量怎么保值?

沙坪坝电工证考点过期了不知道怎么复审延期?含金量怎么保值? 证书过期了不知道怎么复审延期?你不是一个人,这几乎是所有特种作业持证人的共同痛点。特别是像 沙坪坝电工证考点…

📰

你怕考不过白交培训费?看看这组【应急管理部 19 号令特种作业证】的真实经历

你怕考不过白交培训费?看看这组【应急管理部 19 号令特种作业证】的真实经历 你怕考不过白交培训费?别急,看看这组【应急管理部 19…

📰

本溪低压电工证学校多久拿证?别再被中介忽悠了

本溪低压电工证学校多久拿证?别再被中介忽悠了 你是不是也遇到过这样的问题:找了一堆机构,问了一圈人,最后还是不知道去哪报名,怕被中介坑?特别是像低压电工证这种实操性很强的证书,一旦出问题,后果可不是闹着玩的。本溪低压电工证学校多久拿证?这是…

📰

新蔡哪里办电工证怎么报名才能顺利转证上岗

新蔡哪里办电工证怎么报名才能顺利转证上岗 之前考的证在外省能不能转过来?这是很多在外地打工、返乡就业的电工朋友最关心的问题。尤其是在新蔡这样的地方,很多人之前在外地打工拿到了电工证,现在回到本地想继续从事相关工作,却发现证书在本地不被认可,…

📰

大化电工证复审难?内部消息:实操考试心里没底怕挂科,这些技巧帮你稳过

大化电工证复审难?内部消息:实操考试心里没底怕挂科,这些技巧帮你稳过 实操考试心里没底怕挂科,这是很多准备参加大化电工证复审的学员共同的担忧。尤其是像你这样在鹤壁水利工程一线工作的电工,不仅要面对复杂的设备和环境,还得在规定时间内完成一系列…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬