核心是用泛型接口解耦操作行为、数据实体与数据源归属,定义IDao、IDataSourceAware及业务接口,并为不同数据源提供专用泛型基类,结合运行时类型绑定与统一异常事务处理。

核心是用泛型接口把“操作行为”“数据实体”和“数据源归属”三者解耦,而不是让一个DAO硬撑所有数据库。
定义分层泛型接口,明确职责边界
接口不写SQL,只声明能力与语义:
-
IDataSourceAware<T>:仅负责标记上下文,比如提供
useTenant(String tenantId)或withDataSource(String name)方法,不执行任何数据库操作 -
IDao<T>:纯行为契约,含
save(T)、findById(ID)、deleteById(ID)等通用方法;建议约束T extends BaseEntity,确保有getId()和乐观锁字段 -
IUserDao extends IDao<User> & IDataSourceAware<User>:业务层接口,继承通用能力,再叠加语义方法如
findByEmailAndTenant(String email, String tenantId)
按数据源类型提供泛型基类,避免强求“一套通吃”
不同数据库的 SQL 能力、事务模型、连接管理方式差异大,应为每类数据源建专用抽象基类:
Java Maven 项目审查工作流的通用输入处理。当 Java Maven 项目以 ZIP压缩包或 GitLab 仓库 URL 形式提供且需要...时使用。
- JDBC 系(MySQL/PostgreSQL):写
AbstractJdbcDao<T>,内部持DataSourceRouter,每次执行前调用lookupDataSource(name)获取连接 - MyBatis-Plus:Mapper 接口继承
BaseMapper<T>,配合@MapperScan指向动态SqlSessionTemplate,模板由租户或业务域决定 - JPA:定义
JpaDao<T, ID> extends JpaRepository<T, ID>,搭配AbstractRoutingDataSource,重写determineCurrentLookupKey(),根据线程变量返回数据源名
运行时联动泛型类型与数据源路由
泛型本身不参与运行时决策,但可通过反射+上下文完成绑定:
立即学习“Java免费学习笔记(深入)”;
- 在
AbstractJdbcDao<T>中,通过ParameterizedType获取子类传入的T,检查其注解如@TableSchema("user_db")或@TenantIsolated,自动匹配数据源名 - 用
ThreadLocal存当前租户ID,DataSourceRouter拼接T.class.getSimpleName() + "_" + tenantId作为 key,查预加载的Map<String, DataSource> - 避免每次反射:启动时扫描所有 DAO 实现类,缓存
Class<?> → DataSourceKey映射表,后续直接查表
统一异常与事务语义,屏蔽底层差异
上层业务不应感知 MySQL 的 SQLException 或 JPA 的 PersistenceException:
- 所有 DAO 基类统一抛出自定义异常,如
DataAccessException,包装原始异常并附带上下文(数据源名、实体类型、操作动作) - 事务控制交由切面或模板方法统一管理,例如
@Transactional注解作用在IDao方法上时,自动绑定当前数据源事务管理器 - 对批量操作、分页查询等高频场景,封装成泛型模板方法,保证各数据源实现一致的行为契约

















