想用好 DDD 必须先过 Spring Data 这关( 二 )

 
Spring Data 通过提供简单的、通用的数据访问接口(如Repository)和自动生成实现代码 , 使得开发人员不必编写重复的数据访问代码 。这样 , 开发人员可以专注于业务逻辑 , 而无需关注数据存储和访问的细节 。
总的来说 , Spring Data的主要解决的问题是:简化数据访问层的开发 , 提高代码复用性 , 降低开发复杂度 。
Spring Data 对多种数据存储提供了支持 , 本文以 Spring Data Jpa 为例 , 快速实现应用程序与关系数据库的交互 。
4.1. 引入 Spring Data JPA

Spring Data jpa 是 Spring Data 家族的重要成员 , 主要解决 JAVA 应用程序使用 Jpa 完成对数据库的访问问题 。它提供了一种简单而灵活的方法来访问和管理数据 , 并且可以消除重复代码和提高开发效率 。
 
首先 , 需要在pom中 引入 spring-data-jpa-starter , 具体如下:
org.springframework.boot spring-boot-starter-data-jpa
其次 , 引入 mysql 驱动 , 具体如下:
com.mySQL mysql-connector-j runtime
Spring Data Jpa 默认实现是 Hibernate , 而 Hibernate 是目前最流行且功能最强大的 JPA 实现 , 它提供了强大的映射、查询和事务管理能力 。
4.2. 完成配置 
在 Application.yml 增加 DB 和 Jpa 相关配置 , 具体如下:
spring: application: name: Spring-Data-for-DDD-demo datasource: # 数据库配置信息 driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://127.0.0.1:3306/books username: root password: root jpa: # 打印 sql show-sql: true
在启动类上启用 Spring Data Jpa 。
@SpringBootApplication // 开启 Spring Data Jpa ,  basePackages 是 Repository 接口存放的包路径 @EnableJpaRepositories(basePackages = "com.geekhalo.springdata4ddd.order.repository") public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } } 4.3. 使用 Repository
一切就绪 , 接下来就可以为模型创建专属 Repository , 具体如下:
public interface OrderCommandRepository extends JpaRepository { }
至此 , Order 的专属 Repository 就开发完成 。
不知道你是否存在疑问:
 
  1. 说好的统一的易用方法在哪里?
  2. 为什么没有看到实现代码?
 
一般情况下 , JpaRepository 接口中的方法就能满足大部分需求 , 典型方法包括:
方法
含义
save、saveAll
保存或更新 , 如果数据库没有则执行 insert 操作 , 数据库有则执行 update 操作
findById
根据主键查询实体
findAllById
根据主键批量获取实体
count
查询数量
delete、deleteById
删除数据
findAll
分页或排序
5. 实战--订单
为了体现 Spring Data Jpa的强大功能 , 以最常见的订单为例 , 业务模型如下图所示:
想用好 DDD 必须先过 Spring Data 这关

文章插图
 
 
  1. 一笔下单对应一个订单(Order)
  2. 一个订单可以有一个收获地址(OrderAddress)
  3. 一个订单可以关联多个订单项(OrderItem)
 
对应到领域模型如下:
想用好 DDD 必须先过 Spring Data 这关

文章插图
 
核心代码如下:
@Data @Entity @Table(name = "tb_order") @Setter(AccessLevel.private) public class Order { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @Column(name = "user_id") private Long userId; @Column(name = "status") @Enumerated(EnumType.STRING) private OrderStatus status; @Column(name = "price") private int price; // 收货地址 @.NEToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY) @JoinColumn(name = "user_address_id") private OrderAddress address; // 订单项 @OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL) @JoinColumn(name = "order_id") private List items = new ArrayList<>(); } 5.1. 生单
先简单看下生单的核心代码 , 具体如下:
@Transactional(readOnly = false) public Order createOrder(CreateOrderCommand command){ // 创建内存对象 Order order = Order.create(command); // 保存到数据库 this.repository.save(order); return order; } // Order 实体上的 create 方法 public static Order create(CreateOrderCommand command) { // 创建内存对象 Order order = new Order(); order.setUserId(command.getUserId()); String userAddress = command.getUserAddress(); if (!StringUtils.hasText(userAddress)){ // 设置收获地址 OrderAddress orderAddress = new OrderAddress(); orderAddress.setDetail(userAddress); order.setAddress(orderAddress); } // 添加订单项 List productForBuys = command.getProducts(); productForBuys.stream() .map(productForBuy -> OrderItem.create(productForBuy)) .forEach(orderItem -> order.addOrderItem(orderItem)); order.init(); return order; }


推荐阅读