JDBC vs. Spring JDBC vs. Spring Data JDBC vs. Spring Data R2DBC

JDBC vs. Spring JDBC vs. Spring Data JDBC vs. Spring Data R2DBC

下面是对 JDBC、Spring JDBC、Spring Data JDBC、Spring Data R2DBC 四者的清晰区分与联系,聚焦于它们的定位、抽象层级、编程模型以及彼此关系


✅ 1. JDBC(Java Database Connectivity)

  • 定位:Java 标准库(java.sql)提供的底层数据库访问 API
  • 编程模型阻塞式、同步
  • 特点
    • 直接操作 ConnectionPreparedStatementResultSet
    • 需手动管理资源、异常处理、事务。
    • 样板代码多,但控制力最强。
  • 与 Spring 无关,是 Java 平台基础能力。

📌 本质:所有 Java SQL 数据访问技术的“地基”。

import java.sql.*;

public String findUserNameById(Long id) throws SQLException {
    String url = "jdbc:postgresql://localhost:5432/mydb";
    String user = "user";
    String password = "pass";

    try (Connection conn = DriverManager.getConnection(url, user, password);
         PreparedStatement stmt = conn.prepareStatement("SELECT name FROM users WHERE id = ?")) {
        stmt.setLong(1, id);
        try (ResultSet rs = stmt.executeQuery()) {
            if (rs.next()) {
                return rs.getString("name");
            }
        }
    }
    return null;
}

✅ 2. Spring JDBC(核心是 JdbcTemplate

  • 定位Spring 对 JDBC 的轻量级封装,消除样板代码。
  • 编程模型阻塞式、同步(底层仍用 JDBC)。
  • 特点
    • 封装了连接获取、异常转换(转为 Spring 的 DataAccessException)、资源自动释放。
    • 通过 RowMapper 简化结果集到对象的映射。
    • 不提供 ORM,SQL 仍需手写。
  • 属于 Spring Frameworkspring-jdbc 模块)。

📌 与 JDBC 关系封装 JDBC,但不改变其阻塞本质

import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;

@Repository
public class UserDao {
    private final JdbcTemplate jdbcTemplate;

    public UserDao(JdbcTemplate jdbcTemplate) {
        this.jdbcTemplate = jdbcTemplate;
    }

    public String findUserNameById(Long id) {
        return jdbcTemplate.queryForObject(
            "SELECT name FROM users WHERE id = ?",
            (rs, rowNum) -> rs.getString("name"),
            id
        );
    }
}

✅ 3. Spring Data JDBC

  • 定位Spring Data 项目的一部分,提供基于聚合根的简单 ORM
  • 编程模型阻塞式、同步(底层使用 Spring JDBC)。
  • 特点
    • 提供 CrudRepositoryJdbcAggregateTemplate 等高层抽象。
    • 支持根据方法名自动生成简单 SQL(如 findByEmail)。
    • 不支持懒加载、缓存、复杂关联(刻意保持简单,与 JPA 对比)。
    • 遵循 DDD(领域驱动设计)中的“聚合”概念。
  • 依赖 Spring JDBC,是其之上的一层。

📌 与 Spring JDBC 关系基于 Spring JDBC 构建,提供 Repository 模式

import org.springframework.data.annotation.Id;
import org.springframework.data.jdbc.repository.query.Query;
import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;

// 实体类
public class User {
    @Id
    private Long id;
    private String name;
    // getters/setters
}

// Repository
@Repository
public interface UserRepository extends CrudRepository<User, Long> {
    @Query("SELECT name FROM users WHERE id = :id")
    String findNameById(Long id);
}

使用

@Service
public class UserService {
    private final UserRepository userRepository;

    public UserService(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    public String getUserName(Long id) {
        return userRepository.findNameById(id);
    }
}

✅ 4. Spring Data R2DBC

  • 定位Spring Data 项目对 R2DBC 的响应式封装
  • 编程模型非阻塞、响应式(Reactive),基于 Project Reactor(Mono/Flux)。
  • 特点
    • 提供 R2dbcRepositoryDatabaseClient(旧版)等响应式 Repository。
    • 自动生成响应式 CRUD 方法。
    • 底层依赖 R2DBC 驱动(如 r2dbc-postgresql),不是基于 JDBC
    • 与 Spring WebFlux 完美集成。
  • 与 JDBC 完全无关(R2DBC 是 JDBC 的响应式替代方案)。

📌 关键点Spring Data R2DBC ≠ Spring Data JDBC 的响应式版,而是基于全新底层(R2DBC) 构建的独立体系。

import org.springframework.data.annotation.Id;
import org.springframework.data.r2dbc.repository.Query;
import org.springframework.data.repository.reactive.ReactiveCrudRepository;
import org.springframework.stereotype.Repository;
import reactor.core.publisher.Mono;

// 实体类(与 Spring Data JDBC 类似)
public class User {
    @Id
    private Long id;
    private String name;
    // getters/setters
}

// 响应式 Repository
@Repository
public interface UserRepository extends ReactiveCrudRepository<User, Long> {
    @Query("SELECT name FROM users WHERE id = :id")
    Mono<String> findNameById(Long id);
}

使用

@Service
public class UserService {
    private final UserRepository userRepository;

    public UserService(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    public Mono<String> getUserName(Long id) {
        return userRepository.findNameById(id);
    }
}

🔄 四者的关系图

+-------------------+        +-----------------------+
|      JDBC         |        |        R2DBC          |
| (阻塞,Java 标准) |        | (非阻塞,响应式规范)   |
+-------------------+        +-----------------------+
          |                            |
          v                            v
+-------------------+        +-----------------------+
|   Spring JDBC     |        |   Spring Data R2DBC   |
| (封装 JDBC)       |        | (基于 R2DBC 的响应式  |
+-------------------+        |  Spring Data 抽象)    |
          |                  +-----------------------+
          v
+-------------------+
| Spring Data JDBC  |
| (基于 Spring JDBC |
|  的简单 ORM)      |
+-------------------+

📊 对比总结表

技术 编程模型 底层依赖 是否 Spring 是否自动 Repository 是否响应式
JDBC 阻塞/同步 无(标准)
Spring JDBC 阻塞/同步 JDBC ❌(需手写 DAO)
Spring Data JDBC 阻塞/同步 Spring JDBC ✅(CrudRepository
Spring Data R2DBC 非阻塞/响应式 R2DBC ✅(R2dbcRepository

💡 如何选择?

  • 传统 Spring Boot + Tomcat + 阻塞 I/O
    • 简单查询 → Spring JDBC
    • 想用 Repository + 简单持久化 → Spring Data JDBC
  • Spring WebFlux + Netty + 响应式栈
    • 需要数据库访问 → Spring Data R2DBC
  • 极简控制或兼容老系统原生 JDBC

希望这次的梳理更准确、清晰!如有具体使用场景(比如你正在用 WebFlux 还是 MVC),我可以进一步给出推荐。

posted @ 2023-10-17 02:15  悠哉大斌  阅读(68)  评论(0)    收藏  举报