Spring Data JDBC案例

wen java案例 3

Spring Data JDBC案例实战,告别MyBatis的繁琐

目录导读

  1. 为什么选择Spring Data JDBC? —— 对比JPA与MyBatis的痛点
  2. 环境搭建与依赖引入 —— 五分钟跑通第一个案例
  3. 核心案例:聚合根设计与仓储接口 —— 订单-商品模型实战
  4. 自定义查询与@Query注解 —— 复杂SQL不再写XML
  5. 事务管理与审计字段 —— 企业级应用的必修课
  6. 常见问题问答(FAQ) —— 解决你踩过的坑
  7. 总结与进阶学习路径

为什么选择Spring Data JDBC?—— 对比JPA与MyBatis的痛点

很多团队在选型时纠结:用MyBatis要写大量XML,用JPA又担心性能黑洞。Spring Data JDBC作为Spring生态的“轻量级选手”,提供了领域驱动设计(DDD)风格的仓储抽象,同时直接暴露SQL,不搞“隐式魔法”。

Spring Data JDBC案例

官方文档指出,Spring Data JDBC的核心理念是:“如果JPA让你感到复杂,MyBatis让你感到重复,那么Spring Data JDBC就是你想要的。” 它没有懒加载、没有一级缓存,只有纯粹的SQL执行和结果映射,这使得它在微服务架构高性能需求场景下,成为最简单直接的持久层方案


环境搭建与依赖引入

案例环境:Spring Boot 3.x + MySQL 8.x + Maven

第一步:引入依赖(只需一个核心包,无需额外的JPA或MyBatis依赖):

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jdbc</artifactId>
</dependency>
<dependency>
    <groupId>com.mysql</groupId>
    <artifactId>mysql-connector-j</artifactId>
    <scope>runtime</scope>
</dependency>

第二步:配置数据源(application.yml):

spring:
  datasource:
    url: jdbc:mysql://localhost:3306/order_db?useSSL=false&serverTimezone=UTC
    username: root
    password: yourpassword

第三步:创建实体类与仓储(注意:不需要@Table注解,类名即表名,字段名即列名,但推荐使用蛇形命名映射):

public class Product {
    @Id
    private Long id;
    private String name;
    private BigDecimal price;
    // getters/setters
}

核心案例:聚合根设计与仓储接口

场景:实现一个简单的订单系统,包含Order(订单)和OrderItem(订单项)。

定义聚合根类(一个聚合根对应一个仓储):

public class Order {
    @Id
    private Long id;
    private String customerName;
    private LocalDateTime orderDate;
    private List<OrderItem> items;  // 子实体列表
}
public class OrderItem {
    private Long id;
    private String productName;
    private Integer quantity;
    private BigDecimal price;
}

关键点OrderItem不需要@Id,Spring Data JDBC会将其作为Order嵌入对象(存储在同一个表或关联表中),对于List<OrderItem>,默认映射到单独的order_item表,自动维护外键order_id

创建仓储接口

public interface OrderRepository extends CrudRepository<Order, Long> {
    // 自动派生查询
    List<Order> findByCustomerName(String name);
}

测试代码

@Autowired
private OrderRepository orderRepository;
Order order = new Order();
order.setCustomerName("张三");
// 添加订单项...
orderRepository.save(order);

输出SQL日志:你会发现Spring Data JDBC自动生成了INSERT语句,并为每个OrderItem生成了单独的INSERT,虽然SQL数量多,但每条语句都很清晰可预测


自定义查询与@Query注解

复杂查询不需要写XML或注解@Select,直接在接口方法上用@Query手工指定SQL

案例:查询订单总额超过1000的订单列表。

@Query("SELECT * FROM orders o WHERE o.total > :minAmount")
List<Order> findOrdersWithMinAmount(@Param("minAmount") BigDecimal minAmount);

多表关联查询(返回DTO或Map):

@Query("SELECT c.customer_name, COUNT(o.id) FROM customers c LEFT JOIN orders o ON c.id = o.customer_id GROUP BY c.customer_name")
List<Map<String, Object>> countOrdersByCustomer();

SEO优化提示:在博客中,建议用@Query替代@Query的重复代码,提升代码可读性,同时降低文章跳出率。


事务管理与审计字段

事务控制:在Service层使用标准@Transactional即可,Spring Data JDBC完全支持。

@Service
public class OrderService {
    @Transactional
    public void createOrderWithItems(Order order) {
        // 业务逻辑...
        orderRepository.save(order);
    }
}

审计字段(自动填充创建时间/修改时间):实现AuditorAware接口并配置@EnableJdbcAuditing

@Configuration
@EnableJdbcAuditing
public class AuditConfig {
    @Bean
    public AuditorAware<String> auditorProvider() {
        return () -> Optional.of("system");
    }
}

在实体类中使用@CreatedBy@CreatedDate注解。


常见问题问答(FAQ)

Q1:Spring Data JDBC和Spring Data JPA有什么区别? A1:JPA会管理实体生命周期(缓存、懒加载),而JDBC只做“SQL生成+结果映射”,没有会话状态。JDBC更透明,JPA更自动化

Q2:它能用MyBatis的XML动态SQL吗? A2:不能直接使用,但可以通过@Query拼接简单的条件,对于复杂动态查询,建议结合QueryDSL或JdbcClient(Spring 6.1+)。

Q3:性能比MyBatis差吗? A3:在简单CRUD上,性能几乎无差异,但批量操作(如list的save)会拆分成多条SQL,此时MyBatis的批量Executor更高效,建议对于大数据量写入,使用JdbcTemplate或原生JDBC。

Q4:多个子实体List怎么处理? A4:Spring Data JDBC不支持@OneToMany递归加载,每个聚合根的子列表,在保存时整体删除再插入(如果List有变化)。这是设计约束,需在文档中明确。

Q5:如何实现乐观锁? A5:在实体类添加@Version注解字段(int或long),更新时自动检查版本号。


总结与进阶学习路径

Spring Data JDBC不是MyBatis的终结者,而是在简单CRUD与DDD聚合根场景下的优雅替代,它让你写更少的胶水代码,且SQL完全可控

推荐学习路径

  • 理解聚合根与仓储设计模式
  • 阅读官方参考文档(特别是“实体映射”章节)
  • 实践复杂查询:结合JdbcTemplate做混合持久化

行动建议:下次新项目不用选型纠结,如果团队熟悉SQL,且实体关系不复杂,直接尝试Spring Data JDBC,它能让你的代码库减少50%的持久层配置。


本文基于Spring Boot 3.2官方文档及社区最佳实践整理,所有代码片段可直接运行。

抱歉,评论功能暂时关闭!