Spring数据访问模块设计
前面我们已经完成了IoC和web模块的设计,聪明的码友立马就知道了,该到数据访问模块了,要不就这俩玩个6啊,查库势在必行,至此,它来了。
一、核心设计理念
1、痛点在哪
-
应用离不开数据(数据库、NoSQL、文件等)。但直接写 JDBC/Nosql Client 代码太原始了(码友要优雅):
- 样板代码多: 开连接、关连接、异常处理… 写吐了,还容易漏。
- 资源管理难: 连接池怎么管理?线程安全怎么保证?全靠开发者手动,容易出错。
- SQL/命令与Java代码耦合: SQL 硬编码在 Java 里,改个 SQL 得重新编译,不方便维护和优化。
- 结果集映射繁琐: 把数据库查出来的一行行数据 (
ResultSet
/Document
/Row
) 手动转成 Java 对象 (User
,Order
),又累又容易出错。 - 事务管理复杂: 手动控制事务的开始、提交、回滚,尤其是在跨多个操作时,代码会变得非常混乱且容易出错。
- 缺乏抽象: 不同数据库(MySQL, PostgreSQL)或数据源(DB, Redis)的访问方式有差异,业务代码最好不关心底层细节。
-
已有模块的支撑: 你已经有了强大的 IoC 容器!它能管理对象生命周期和依赖关系。数据访问对象 (
DAO/
Repository`) 本身就是对象,非常适合由 IoC 容器托管。Web 模块处理请求,最终往往需要访问数据,两者需要无缝集成
2、解决方案(设计目标)
-
消灭样板代码: 连接管理、资源释放、异常转换这些通用的、繁琐的事情,模块帮你搞定。
-
职责分离:
- 业务逻辑 (
Service
) 关注做什么 (比如:下单)。 - 数据访问逻辑 (
DAO
/Repository
) 关注怎么做 (比如:保存订单数据到 DB)。 - SQL/命令 最好与 Java 代码分离(虽然不一定强制 XML,但提供灵活配置)。
- 业务逻辑 (
-
面向接口编程: 业务代码 (
Service
) 只依赖UserRepository
接口,不关心具体实现是 JDBC、MyBatis 还是 JPA。模块负责在运行时提供具体实现(动态代理是秘密武器!)。 -
模板方法模式: 定义一个操作(执行 SQL/命令)的骨架,将某些步骤(如获取连接、设置参数、处理结果)延迟到子类或回调中。核心执行流程固化,可变部分开放扩展。
-
异常体系统一化: 把五花八门的底层数据访问异常(
SQLException
,RedisException
)转换成模块定义的一套清晰的、非检查型(RuntimeException
)异常。业务层无需处理大量底层异常细节。 -
无缝集成 IoC: 数据访问对象 (
DAO
/Repository
) 本身就是 Bean,由 IoC 容器创建、注入依赖、管理生命周期。 -
非侵入性: 尽量减少对业务代码的污染。通过注解、接口、少量配置来定义数据访问行为。
二、核心组件与职责划分
组件 | 核心职责 | 数据流中的角色 |
---|---|---|
DataSource | 连接的工厂 (含连接池)。 提供物理数据库连接。 | 起点。Executor 执行操作时向其索取 Connection 。 |
PlatformTransactionManager | 抽象事务管理 (开始、提交、回滚、状态)。 实现事务ACID。 | 事务协调者。为 @Transactional 方法管理连接 (绑定到线程) 和事务边界。协调 Executor 使用正确连接。 |
Repository (接口) | 定义数据操作契约 (如 findById ,** save )。** 业务层入口。 | 触发点。业务层 (Service ) 调用其方法 → 触发动态代理 → 委托给 Executor 。 |
Mapper / @SQL | 承载 SQL/命令 和 映射规则。 定义要执行的语句及参数/结果如何与Java交互。 | 执行蓝图。Executor 根据调用的 Repository 方法找到对应的 Mapper ,作为执行的“指令手册”。 |
Executor (核心) | 执行引擎!应用模板方法模式。 | 核心枢纽 & 苦力: 1. 拿连接 (DataSource / 事务绑定) 2. 根据 Mapper 创建语句 3. 绑定参数 (调用 TypeHandler ) 4. 执行 (查询/更新) 5. 处理结果 (映射,调用 TypeHandler /RowMapper ) 6. 清理资源 (关语句/结果集,还连接) 7. 返回结果/行数。 处理所有异常转换! |
TypeHandler | Java类型 <-> 数据库类型 转换器。 (如 Date <->TIMESTAMP , Enum <->String ) | 转换小助手。在 Executor 绑定参数和映射结果时被调用。 |
SqlSession / Template | 提供编程式API,管理一次“数据访问会话”。内部使用 Executor 。 | 替代或辅助 Repository 接口,提供更集中控制。 |
三、关键设计模式应用
1、模板方法模式:JdbcTemplate
执行流程
核心思想:固定数据库操作流程(获取连接→执行 SQL→处理结果→释放资源),可变部分(SQL 执行、结果映射)通过回调接口扩展。
Spring 源码参考:org.springframework.jdbc.core.JdbcTemplate
public class SimpleJdbcTemplate {private DataSource dataSource;// 模板方法:定义执行骨架public <T> T execute(ConnectionCallback<T> action) {Connection conn = DataSourceUtils.getConnection(dataSource);try {return action.doInConnection(conn); // 回调可变逻辑} catch (SQLException ex) {throw translateException("Connection callback failed", ex);} finally {DataSourceUtils.releaseConnection(conn, dataSource);}}// 查询模板public <T> T query(String sql, ResultSetExtractor<T> rse) {return execute(conn -> {try (PreparedStatement ps = conn.prepareStatement(sql)) {try (ResultSet rs = ps.executeQuery()) {return rse.extractData(rs); // 结果集映射回调}}});}
}// 回调接口(策略模式结合点)
@FunctionalInterface
public interface ResultSetExtractor<T> {T extractData(ResultSet rs) throws SQLException;
}
2、策略模式:事务管理
核心思想:PlatformTransactionManager
抽象事务操作(begin/commit/rollback),不同数据源通过具体策略实现(如 DataSourceTransactionManager
)。
Spring 源码参考:org.springframework.transaction.PlatformTransactionManager
// 事务管理器接口(策略抽象)
public interface PlatformTransactionManager {TransactionStatus getTransaction(TransactionDefinition definition);void commit(TransactionStatus status);void rollback(TransactionStatus status);
}// JDBC 事务策略实现
public class DataSourceTransactionManager implements PlatformTransactionManager {private DataSource dataSource;@Overridepublic TransactionStatus getTransaction(TransactionDefinition definition) {Connection conn = DataSourceUtils.getConnection(dataSource);conn.setAutoCommit(false); // 开启事务return new DefaultTransactionStatus(conn, true);}@Overridepublic void commit(TransactionStatus status) {Connection conn = status.getConnection();conn.commit(); // 提交策略}
}
3、动态代理:声明式事务(AOP 代理)
AOP我们还没有设计,此处就把AOP当成一个“黑盒”就好,
Spring AOP 是用于通过运行时动态代理技术实现模块化横切关注点(如日志、事务管理)的核心模块,它通过在方法执行前后插入增强逻辑(Advice)来实现功能。
核心思想:通过代理对象在方法调用前后注入事务逻辑(begin/commit)。Spring 优先用 JDK 动态代理(基于接口),缺接口时用 CGLIB。
Spring 源码参考:org.springframework.aop.framework.DefaultAopProxyFactory
// 事务代理创建工厂
public class TransactionProxyFactory {public Object createProxy(Object target, PlatformTransactionManager txManager) {return Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),(proxy, method, args) -> {TransactionStatus status = txManager.getTransaction(null);try {Object result = method.invoke(target, args); // 执行业务方法txManager.commit(status); // 提交事务return result;} catch (Exception ex) {txManager.rollback(status); // 回滚策略throw ex;}});}
}// 使用示例
UserService proxy = (UserService) new TransactionProxyFactory().createProxy(userService, transactionManager);
proxy.saveUser(); // 代理方法调用
4、工厂模式:Repository
实例创建
核心思想:通过 BeanFactory
动态创建 Repository
代理对象,解耦业务层与数据访问层。
Spring 源码参考:org.springframework.beans.factory.FactoryBean
public class RepositoryFactoryBean<T> implements FactoryBean<T> {private Class<T> repositoryInterface;@Overridepublic T getObject() {// 动态生成 Repository 代理(结合模板方法+策略)return (T) Proxy.newProxyInstance(repositoryInterface.getClassLoader(),new Class[]{repositoryInterface},(proxy, method, args) -> {JdbcTemplate template = new JdbcTemplate(dataSource);String sql = parseSqlFromAnnotation(method); // 解析 @Select 等注解return template.query(sql, new BeanPropertyRowMapper<>(method.getReturnType()));});}
}// 配置声明
@Bean
public RepositoryFactoryBean<UserRepository> userRepository() {return new RepositoryFactoryBean<>(UserRepository.class);
}
5、结果集映射:RowMapper
策略
Spring 源码实现:BeanPropertyRowMapper
通过反射将 ResultSet 列名映射到 JavaBean 属性
public class UserRowMapper implements RowMapper<User> {@Overridepublic User mapRow(ResultSet rs, int rowNum) throws SQLException {User user = new User();user.setId(rs.getLong("id")); // 列名→属性user.setName(rs.getString("name"));return user;}
}// 在模板方法中使用
List<User> users = jdbcTemplate.query("SELECT * FROM users", new UserRowMapper());
四、高级特性及扩展机制设计
1. 声明式事务管理
-
实现: 依赖 IoC 的 AOP 能力(即使你还没做 AOP 模块,事务管理器也可以看作一种特殊代理)。在
Repository
或Service
方法上添加@Transactional
注解。 -
流程:
- 代理拦截被
@Transactional
标记的方法调用。 - 获取
PlatformTransactionManager
。 - 开启事务(获取连接,设置
autoCommit=false
,绑定连接到当前线程)。 - 执行业务方法(其中包含的数据库操作会使用当前线程绑定的连接)。
- 业务方法成功执行完 -> 提交事务。
- 业务方法抛出异常 -> 回滚事务。
- 最终清理线程绑定资源。
- 代理拦截被
-
好处: 事务控制与业务代码完全分离,配置化。
2. 多数据源支持
-
实现: 在 IoC 容器中配置多个
DataSource
Bean (如primaryDataSource
,secondaryDataSource
)。需要实现:AbstractRoutingDataSource
:** 一个动态DataSource
实现,根据某种“键”(Lookup Key)路由到真正的目标DataSource
。- 确定“键”的策略: 通常基于注解(
@DataSource("slave")
)或线程上下文变量。Repository
方法执行前设置“键”,AbstractRoutingDataSource
据此选择DataSource
。
-
挑战: 需要与事务管理器配合,确保同一事务内的操作使用同一个数据源。
3. 注解支持
@Repository
:** 标记一个接口是数据访问仓库,由 IoC 管理并需要生成代理。@Select
/@Insert
/@Update
/@Delete
:** 直接在Repository
接口方法上声明 SQL。@Param
:** 为方法参数命名,用于在 SQL 中引用 (#{userId}
)。@Transactional
:** 声明事务属性 (传播行为、隔离级别、超时、只读等)。
4. 结果映射扩展
- 自定义
RowMapper
/ResultHandler
:** 允许用户编写代码精细控制如何将一行数据映射到一个对象。 - 嵌套结果映射: 处理复杂的“一对一”、“一对多”关联关系(需要递归映射)。
- 自动驼峰命名映射:
user_name
列自动映射到userName
属性。
5. 插件机制 (Interceptor)
- 实现: 在
Executor
执行流程的关键点(执行前、执行后、处理参数、处理结果)插入拦截逻辑。类似责任链模式。 - 用途: SQL 日志记录、性能监控、分页逻辑自动添加、权限过滤、数据加解密等。极大增强灵活性。
6. 简单缓存集成
- 实现: 在
Repository
代理层或Executor
层,对方法调用结果进行缓存(例如基于方法名和参数生成缓存 Key)。下次相同查询直接返回缓存结果。 - 注意: 需要处理缓存一致性(更新操作需清除相关缓存)。
五、核心流程解析
以下是 Spring Framework 数据访问模块的核心数据流程图及详细说明:
六、设计总结
1. 效率飙升 (开发体验)
- 消灭样板: 连接、语句、资源释放、基础异常处理全自动化。
- 专注核心: 开发者只需写 SQL/命令 和定义 对象映射 (
Mapper
/ 注解)。 - 接口清爽:
Repository
接口明确职责,业务代码 (Service
) 依赖清晰。
2. 健壮可靠 (运行质量)
- 资源无忧: 严格的连接池管理、资源释放,杜绝泄漏。
- 异常清晰: 统一转换底层异常为可读的
RuntimeException
,业务层少踩坑。 - 事务优雅:
@Transactional
声明式事务,复杂提交回滚一行注解搞定,与业务解耦。
3. 灵活扩展 (设计优势)
- 接口抽象:
Repository
接口隔离技术细节,轻松切换实现 (JDBC, JPA等)。 - 插件友好: 拦截器 (
Interceptor
) 机制方便扩展 (SQL日志、分页、监控)。 - IoC 融合: 作为一等公民 Bean,依赖注入无缝衔接,生命周期受控。
一句话精髓: “通过动态代理声明意图 (Repository
),模板方法固化流程 (Executor
),让开发者只关注数据本身 (Mapper
),框架搞定一切繁琐。”