-
Notifications
You must be signed in to change notification settings - Fork 448
Java客户端 系统设计
本文简要介绍Dal Client的设计。包括设计思路,具体说明和核心类介绍
Dal的设计需要做到以下几点
- 可理解
- 可扩展
- 可维护
- 可测试
Ctrip Dal Java Client Design PPT
总体而言,Dal相关代码分为3层,上层依赖下层。上层是生成的代码,中间是dal的入口类和微内核处理器,底层是实际操作数据库的代码。
其中上下两层都相对比较简单,要完成的任务很明确。中层有些复杂,要在其他两层保持简单的情况下对数据库分片,结果合并,同步异步处理。
观察参数与返回值,可以发现某些数据库操作具有类似性。因此将具有类似性的操作抽象为接口,这样可以简化通用的处理流程。这既是模板模式,也可以看作是微内核设计。例如下面的各种DalRequest,Task的接口与实现。
所谓沙箱是由一个管理者处理进入的命令,由管理者完全保证命令所需要的资源的获得与释放,命令只需完成自身的动作即可。这样的设计或使分工十分明确,同时减轻双方的工作量。管理者可以把资源管理和环境管理做细致,不用担心泄露风险。而命令实现方可以大幅简化自己的工作。
例如:DalConnectionMaager和DalTransactionManager都可以对Action进行处理,获得与释放事务和连接的工作全部不用Action做
一个非沙箱设计则相反,命令需要自行获得需要的资源并且保证对资源的释放。而管理者则无法保证资源是否合理使用和释放,因此需要很多意外场景检测和处理。总体而言,这种设计的抽象程度低,资源管理工作被分散到系统所有的参与方,难以使用和管理。
例如:一般的连接串和事务接口
在DAL的实现过程中,为了满足携程的需要和保持系统的通用性,在很多方面提供了接口和缺省实现。方便用户安装自己的实际需求扩展。具体可以参考扩展说明的wiki
这里体现的是上面提到的分层设计思路。用户通过code gen生成的DAO为顶层调用代码;顶层代码调用DalTableDao和DalQueryDao完成通用操作
- DalTableDao主要为标准DAO和构建Sql的DAO服务。主要满足针对单表的操作
- DalQueryDao主要为自定义Sql的DAO服务。可以满足复杂sql或多表联合sql
DalTableDao和DalQueryDao会把所有的数据库操作请求打包为DalRequest并调用DalRequestExecutor来完成工作
DalRequestExecutor会把任务分配给各种DaoTask,而DaoTask会调用dal底层的DalClient来完成工作。
DalRequestExecutor是DAL的系统核心,抽象了所有数据库操作通用的部分。屏蔽了不同之处,可以看作是一个微内核。
根据请求的特性分为不同类型的DalRequest。DalRequestExecutor会依据传入DalRequest完成通用的判断shard,同步/异步执行,数据分割合并的工作。DalRequest的设计类似template模式。DalRequestExecutor和DalRequest的关系类似状态机模式。
处理流程
DAO帮助类。配合code gen生成的标准和构建DAO代码。主要针对单表操作。支持数据库和表级别的shard。
Package: com.ctrip.platform.dal.dao
针对单表操作预先提供大多数通用操作。包括
- 按照主键查询。包括数值形式的主键,对象形式的主键
- 按照样例查询
- 一般查询。包括返回多行,查第一个,查开头几个,查指定范围
- 插入记录。包括同时插入多条;插入时返回自增主键;将多个POJO的插入语句拼成一个大insert语句,一次完成插入;批处理插入
- 删除记录
- 更新记录。包括按照条件删除和按照语句更新
DAO帮助类。配合code gen生成的标准和构建DAO代码。主要针对自定义查询和更新操作。支持数据库级别的shard。
Package: com.ctrip.platform.dal.dao
针对查询进行优化的DAO
- 返回多行
- 遍历结果集
- 查对象形式的结果。包括按指定映射查和按指定类型查
- 查第一个
- 查开头几个
- 查指定范围
所有数据库操作的最终入口。规定了查询,更新,批处理和存储过程接口。具有直连和非直连两种实现。
Package: com.ctrip.platform.dal.dao
支持常用的数据库操作。包括
- 查询
- 更新。增删改一类
- 批处理。包括多条SQL语句和单SQL语句,多参数列表
- Command模式。保证对事务的处理。包括单个command:所有在同一个事务的逻辑代码;command列表:对逻辑的划分,列表中的command共享同一个事务
- 存储过程。包括单个语句和批量处理
dal client通过一些辅助类完成工作,其中某些对结果集处理类的设计借鉴了JDBC Template的类似设计。
负责初始化Dal的使用环境。提供取得DalClient的工厂方法。其缺省实现可以满足绝大多数需求。无需开发人员操心路径等繁琐配置
用于传递构建JDBC Statement时需要设置的参数。
用于传递运行时额外的一些跟Connection, Statement相关的属性,和其他一些辅助性的配置信息,需要用户在调用DAO的API前指定。
对JDBC ResultSet执行提取解析的统一接口,需要返回处理结果。
public interface DalResultSetExtractor<T> {
T extract(ResultSet rs) throws SQLException;
}
映射一条ResultSet到POJO instance的接口。
public interface DalRowMapper<T> {
T map(ResultSet rs, int rowNum) throws SQLException;
}
对ResultSet每条都执行提取的统一接口,无需要返回处理结果。
public interface DalRowCallback {
void process(ResultSet rs) throws SQLException;
}
对事务处理的封装。用户可以实现DalCommand将复杂的事务逻辑保护在内。
public interface DalCommand {
boolean execute(DalClient client) throws SQLException;
}
对逻辑数据库,表的元数据,和ORM的封装。
public interface DalParser<T> extends DalRowMapper<T> {
String getDatabaseName();
String getTableName();
String[] getColumnNames();
String[] getPrimaryKeyNames();
int[] getColumnTypes();
boolean isAutoIncrement();
Number getIdentityValue(T pojo);
Map<String, ?> getPrimaryKeys(T pojo);
Map<String, ?> getFields(T pojo);
}
所有关于sharding的判断和分组。所有方法按照DB shard和table shard归类
抽象了事务操作,对ConnectionAction进行操作。
public <T> T doInTransaction(ConnectionAction<T> action, DalHints hints)
抽象了非事务操作,对ConnectionAction进行操作。
public <T> T doInConnection(ConnectionAction<T> action, DalHints hints)
ConnectionAction封装了所有需要的参数和逻辑,实现全部在DalDirectClient里面
public abstract T execute() throws Exception;