关键在于用泛型接口定义行为契约,再通过抽象基类封装通用逻辑,让具体实现只聚焦实体元信息和数据源适配。IDao<T>声明基础操作,IDataSourceAware<T>标记数据源能力,IUserDao继承二者并扩展语义方法;BaseDao<T>提供统一入口与模板逻辑,子类仅需实现元信息;按技术栈提供JDBC/MyBatis/JPA专用实现;运行时通过反射缓存+ThreadLocal联动泛型类型与数据源,异常统一转换,事务显式表达。

关键在于用泛型接口定义行为契约,再通过抽象基类封装通用逻辑,让具体实现只聚焦实体元信息和数据源适配,不重复写模板代码。
定义分层泛型接口,明确职责边界
不堆砌大而全的接口,而是按能力拆解:
- IDao<T>:纯行为契约,声明 save(T)、findById(ID)、update(T)、deleteById(ID) 等基础方法;T 通常约束为 extends BaseEntity,确保 getId()、getVersion() 等通用方法可用
- IDataSourceAware<T>:仅声明数据源上下文能力,比如 withDataSource(String name) 或 useTenant(String tenantId),不执行 SQL,只做标记
- IUserDao:业务接口,继承 IDao<User> & IDataSourceAware<User>,可扩展 findByEmail(String) 等语义方法,不包含 SQL 细节或连接逻辑
抽象基类复用通用逻辑,子类只需提供元信息
BaseDao<T> 是核心载体,它不绑定 JDBC/MyBatis/JPA,而是面向统一操作入口(如 JdbcTemplate、SqlSession 或 EntityManager):
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
- 声明受保护的数据访问对象,例如 protected JdbcTemplate jdbcTemplate
- 把表名、主键字段、参数映射等交由子类决定:定义抽象方法 getTableName()、getIdColumn()、getInsertArgs(T)、mapRow(ResultSet, int)
- save()、findById() 等方法在基类中完成 SQL 拼装、参数绑定、资源关闭、异常转换,子类无需重复 try-catch 或手动 close
按数据源类型提供专用泛型实现,避免“一套通吃”
不同框架能力差异大,应为每种技术栈建对应抽象基类,复用其原生能力并注入路由逻辑:
立即学习“Java免费学习笔记(深入)”;
- JDBC 系:AbstractJdbcDao<T> 内部持有 DataSourceRouter,执行前调用 lookupDataSource(name) 获取连接
- MyBatis-Plus:Mapper 接口直接继承 BaseMapper<T>,再配合 @MapperScan 指向动态 SqlSessionTemplate
- JPA:定义 JpaDao<T, ID> extends JpaRepository<T, ID>,配合 AbstractRoutingDataSource 重写 determineCurrentLookupKey(),根据 ThreadLocal 中的租户标识返回数据源名
运行时联动泛型类型与数据源,兼顾性能与灵活性
泛型擦除后无法直接获取 T,但可通过反射+缓存机制高效绑定:
- 启动时扫描所有 DAO 实现类,用 ParameterizedType 解析子类传入的 T,读取 @TableSchema("user_db") 或 @TenantIsolated 注解,建立 Class<?> → DataSourceKey 映射表
- 执行时不再反射,直接查缓存表;ThreadLocal 存当前租户 ID,拼出 key 后快速定位数据源
- 所有原生异常(SQLException、MyBatisSystemException)统一转为 DataAccessException,事务由 saveInTransaction(T) 等方法签名显式表达

















