{{ layerInfo.title }}
-{{ layerInfo.description }}
-{{ activeInfo.desc }}
+-
-
- - {{ mistake }} - +
- {{ m }}
🗄️ Repository 层:数据的"仓库管理员"
-- Repository 封装数据访问逻辑,让上层无需关心数据库细节 -
+{{ view === 'bad' ? badCode : goodCode }}
- @Service
-public class OrderService {
-
- @Autowired
- private JdbcTemplate jdbcTemplate; // 直接依赖底层 JDBC
-
- public List<Order> getUserOrders(Long userId) {
- // ❌ 问题1:SQL 硬编码在 Service 里
- // ❌ 问题2:更换数据库需要改业务代码
- // ❌ 问题3:无法单元测试,必须连真实数据库
- String sql = "SELECT * FROM orders WHERE user_id = ? AND deleted = 0";
-
- return jdbcTemplate.query(sql, (rs, rowNum) -> {
- Order order = new Order();
- order.setId(rs.getLong("id"));
- order.setUserId(rs.getLong("user_id"));
- order.setTotalAmount(rs.getBigDecimal("total_amount"));
- // ... 更多字段映射
- return order;
- }, userId);
- }
-
- public void updateOrderStatus(Long orderId, OrderStatus status) {
- // ❌ 问题4:到处重复 SQL 片段
- String sql = "UPDATE orders SET status = ?, updated_at = NOW() WHERE id = ?";
- jdbcTemplate.update(sql, status.name(), orderId);
- }
-}
-
-
-
- 😰 这种做法的问题
--
-
- 数据库耦合:业务代码里到处都是 SQL,换数据库等于重写 -
- 难以测试:Service 必须连真实数据库才能跑,单元测试变成集成测试 -
- 代码重复:同样的查询条件(如 deleted=0)在每个方法里重复写 -
- 安全隐患:手写 SQL 容易漏掉防注入处理 -
// ========== 1. 实体定义(Domain) ==========
-@Entity
-@Table(name = "orders")
-public class Order {
- @Id
- @GeneratedValue(strategy = GenerationType.IDENTITY)
- private Long id;
-
- @Column(name = "user_id", nullable = false)
- private Long userId;
-
- @Column(name = "total_amount")
- private BigDecimal totalAmount;
-
- @Enumerated(EnumType.STRING)
- private OrderStatus status;
-
- @Column(name = "deleted")
- private Boolean deleted = false;
-
- // getters and setters...
-}
-
-// ========== 2. Repository 接口定义 ==========
-@Repository
-public interface OrderRepository extends JpaRepository<Order, Long> {
-
- // ✅ Spring Data 自动生成查询 - 简单查询
- List<Order> findByUserIdAndDeletedFalse(Long userId);
-
- // ✅ 自定义 JPQL 查询 - 复杂统计
- @Query("""
- SELECT new com.example.OrderStatistics(
- o.userId,
- COUNT(o),
- SUM(o.totalAmount),
- MAX(o.createdAt)
- )
- FROM Order o
- WHERE o.createdAt BETWEEN :startDate AND :endDate
- AND o.deleted = false
- GROUP BY o.userId
- HAVING COUNT(o) >= :minOrderCount
- ORDER BY SUM(o.totalAmount) DESC
- """)
- List<OrderStatistics> findUserOrderStatistics(
- @Param("startDate") LocalDateTime startDate,
- @Param("endDate") LocalDateTime endDate,
- @Param("minOrderCount") Long minOrderCount,
- Pageable pageable
- );
-
- // ✅ 批量更新 - 修改状态
- @Modifying
- @Query("UPDATE Order o SET o.status = :newStatus, " +
- "o.updatedAt = CURRENT_TIMESTAMP WHERE o.id IN :ids")
- int batchUpdateStatus(
- @Param("ids") List<Long> orderIds,
- @Param("newStatus") OrderStatus newStatus
- );
-}
-
-// ========== 3. Service 层(纯业务逻辑) ==========
-@Service
-public class OrderService {
-
- @Autowired
- private OrderRepository orderRepository; // ✅ 依赖接口,不依赖具体实现
-
- @Autowired
- private UserRepository userRepository;
-
- // ✅ 业务方法清晰简洁,不关心数据怎么存
- public List<OrderDTO> getUserOrders(Long userId) {
- // 可以在这里加业务校验
- User user = userRepository.findById(userId)
- .orElseThrow(() -> new UserNotFoundException(userId));
-
- // 直接调用 Repository,SQL 藏在后面
- List<Order> orders = orderRepository.findByUserIdAndDeletedFalse(userId);
-
- // 转换为 DTO 返回
- return orders.stream()
- .map(OrderDTO::from)
- .collect(Collectors.toList());
- }
-}
-
-
-
- 🎉 这样做的好处
--
-
- 关注点分离:Service 专注于业务,Repository 专注于数据,各管一摊 -
- 可测试性高:Service 只依赖 Repository 接口,单元测试可以用 Mock 对象替代真实数据库 -
- 代码复用:通用的查询方法(如 findByUserId)定义一次,到处复用 -
- 切换成本低:从 MySQL 换成 MongoDB,只需改 Repository 实现,Service 完全不动 -
-
+
- {{ item }} +
📊 不同 Repository 实现方式对比
- --
-
- 方法名自动推导查询 -
- 分页排序内置支持 -
- 事务管理集成 -
-
-
- 复杂查询性能一般 -
- 学习曲线较陡 -
-
-
- SQL 完全可控 -
- 复杂查询性能优 -
- 动态 SQL 强大 -
-
-
- 需要手写 SQL -
- 样板代码较多 -
-
-
- 简单轻量 -
- 无延迟加载 -
- 启动快速 -
-
-
- 无复杂映射 -
- 功能较简单 -
| 实现方式 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| {{ r.name }} {{ r.tag }} |
+ {{ r.pros }} | +{{ r.cons }} | +{{ r.scene }} | +
⚙️ Service 层:业务逻辑的"指挥家"
-- Service 层编排业务逻辑,协调多个 Repository,管理事务边界 -
+{{ step.code }}
- {{ step.code }}
+ 🎯 Service 层设计原则
-{{ principle.example }}
- {{ p.example }}