Skip to content

Java客户端 系统设计

He, Jiehui edited this page Sep 14, 2016 · 8 revisions

简介

本文简要介绍Dal Client的设计。包括设计思路,具体说明和核心类介绍

Dal的设计需要做到以下几点

  • 可理解
  • 可扩展
  • 可维护
  • 可测试

设计思路

分层

总体而言,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来完成工作。

High level design

DalRequestExecutor设计

DalRequestExecutor是DAL的系统核心,抽象了所有数据库操作通用的部分。屏蔽了不同之处,可以看作是一个微内核。

根据请求的特性分为不同类型的DalRequest。DalRequestExecutor会依据传入DalRequest完成通用的判断shard,同步/异步执行,数据分割合并的工作。DalRequest的设计类似template模式。DalRequestExecutor和DalRequest的关系类似状态机模式。

Executor design

处理流程 Executor sequence

主要类说明

DalTableDao

DAO帮助类。配合code gen生成的标准和构建DAO代码。主要针对单表操作。支持数据库和表级别的shard。

Package: com.ctrip.platform.dal.dao

针对单表操作预先提供大多数通用操作。包括

  • 按照主键查询。包括数值形式的主键,对象形式的主键
  • 按照样例查询
  • 一般查询。包括返回多行,查第一个,查开头几个,查指定范围
  • 插入记录。包括同时插入多条;插入时返回自增主键;将多个POJO的插入语句拼成一个大insert语句,一次完成插入;批处理插入
  • 删除记录
  • 更新记录。包括按照条件删除和按照语句更新

DalQueryDao

DAO帮助类。配合code gen生成的标准和构建DAO代码。主要针对自定义查询和更新操作。支持数据库级别的shard。

Package: com.ctrip.platform.dal.dao

针对查询进行优化的DAO

  • 返回多行
  • 遍历结果集
  • 查对象形式的结果。包括按指定映射查和按指定类型查
  • 查第一个
  • 查开头几个
  • 查指定范围

DalClient

所有数据库操作的最终入口。规定了查询,更新,批处理和存储过程接口。具有直连和非直连两种实现。

Package: com.ctrip.platform.dal.dao

支持常用的数据库操作。包括

  • 查询
  • 更新。增删改一类
  • 批处理。包括多条SQL语句和单SQL语句,多参数列表
  • Command模式。保证对事务的处理。包括单个command:所有在同一个事务的逻辑代码;command列表:对逻辑的划分,列表中的command共享同一个事务
  • 存储过程。包括单个语句和批量处理

辅助类说明

dal client通过一些辅助类完成工作,其中某些对结果集处理类的设计借鉴了JDBC Template的类似设计。

DalClientFactory

负责初始化Dal的使用环境。提供取得DalClient的工厂方法。其缺省实现可以满足绝大多数需求。无需开发人员操心路径等繁琐配置

StatementParameters

用于传递构建JDBC Statement时需要设置的参数。

DalHints

用于传递运行时额外的一些跟Connection, Statement相关的属性,和其他一些辅助性的配置信息,需要用户在调用DAO的API前指定。

DalResultSetExtractor

对JDBC ResultSet执行提取解析的统一接口,需要返回处理结果。

public interface DalResultSetExtractor<T> {
    T extract(ResultSet rs) throws SQLException;
}

DalRowMapper

映射一条ResultSet到POJO instance的接口。

public interface DalRowMapper<T> {
    T map(ResultSet rs, int rowNum) throws SQLException;
}

DalRowCallback

对ResultSet每条都执行提取的统一接口,无需要返回处理结果。

public interface DalRowCallback {
    void process(ResultSet rs) throws SQLException;
}

DalCommand

对事务处理的封装。用户可以实现DalCommand将复杂的事务逻辑保护在内。

public interface DalCommand {
    boolean execute(DalClient client) throws SQLException;
}

DalParser

对逻辑数据库,表的元数据,和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);
}

DalShardingHelper

所有关于sharding的判断和分组。所有方法按照DB shard和table shard归类

DalTransactionManager

抽象了事务操作,对ConnectionAction进行操作。

public <T> T doInTransaction(ConnectionAction<T> action, DalHints hints)

DalConnectionManager

抽象了非事务操作,对ConnectionAction进行操作。

public <T> T doInConnection(ConnectionAction<T> action, DalHints hints)

ConnectionAction

ConnectionAction封装了所有需要的参数和逻辑,实现全部在DalDirectClient里面

public abstract T execute() throws Exception;
Clone this wiki locally