31

数据库适配层

简介

数据库适配层是Known项目中的核心组件,负责为系统提供多种数据库支持。它通过统一的接口抽象不同数据库的差异,使上层业务代码无需关心底层数据库的具体实现。

项目结构

数据库适配层主要位于Shared/Data/目录下,包含以下关键文件:

  1. DbProvider.SqlServer.cs - SQL Server数据库适配器
  2. DbProvider.MySql.cs - MySQL数据库适配器
  3. DbProvider.SQLite.cs - SQLite数据库适配器
  4. DbProvider.Oracle.cs - Oracle数据库适配器
classDiagram
    class DbProvider{
        <<abstract>>
        +Database db
        +FormatName(string name): string
        +FormatBoolean(bool value): object
        +GetBooleanSql(string field, bool isTrue): string
        +GetTableSql(string dbName): string
        +GetTableScript(string tableName, DbModelInfo info): string
        +GetTopSql(int size, string text): string
    }
    DbProvider <|-- SqlServerProvider
    DbProvider <|-- MySqlProvider
    DbProvider <|-- SQLiteProvider
    DbProvider <|-- OracleProvider

核心组件

数据库适配层的核心是DbProvider抽象基类,它定义了所有数据库适配器必须实现的接口。各具体数据库适配器继承并实现这些接口。

1. 数据库适配器基类(DbProvider)

  • 提供数据库操作的统一抽象
  • 定义公共接口和默认实现
  • 管理数据库连接

2. 具体数据库适配器

每种数据库都有对应的适配器实现:

SQL Server适配器

  • 表名使用[表名]格式
  • 布尔值转换为1/0
  • 分页使用TOP语法

MySQL适配器

  • 表名使用表名格式
  • 分页使用LIMIT语法
  • 支持表注释作为表名

SQLite适配器

  • 表名使用[表名]格式
  • 分页使用LIMIT OFFSET语法
  • 自动主键处理

Oracle适配器

  • 参数使用:参数名格式
  • 日期格式化处理
  • 使用ALTER TABLE添加主键

架构概述

数据库适配层采用抽象工厂模式,通过统一的DbProvider接口屏蔽不同数据库的差异。上层代码只需与DbProvider交互,无需关心具体数据库类型。

flowchart TD
    A[业务代码] --> B[DbProvider]
    B --> C[SqlServerProvider]
    B --> D[MySqlProvider]
    B --> E[SQLiteProvider]
    B --> F[OracleProvider]

详细组件分析

1. 表结构生成

所有适配器都实现了GetTableScript方法,用于生成创建表的SQL语句。

示例(SQL Server):

internal static string GetTableScript(string tableName, List<FieldInfo> fields, List<string> keys, int maxLength = 0)
{
    var sb = new StringBuilder();
    sb.AppendLine("CREATE TABLE [{0}] (", tableName);
    foreach (var item in fields)
    {
        var required = item.Required ? "NOT NULL" : "NULL";
        var column = $"[{item.Id}]";
        column = GetColumnName(column, maxLength + 2);
        var type = GetSqlServerDbType(item);
        sb.AppendLine($"    {column} {type} {required},");
    }
    if (keys != null && keys.Count > 0)
    {
        var key = string.Join(", ", keys.Select(k => $"[{k}] ASC"));
        sb.AppendLine($"    CONSTRAINT [PK_{tableName}] PRIMARY KEY ({key})");
    }
    sb.AppendLine(");");
    return sb.ToString();
}

2. 数据类型映射

每种适配器都实现了类型映射方法,如GetSqlServerDbType:

private static string GetSqlServerDbType(FieldInfo item)
{
    string type;
    if (item.Type == FieldType.Date || item.Type == FieldType.DateTime)
        type = "[datetime]";
    else if (item.Type == FieldType.CheckBox || item.Type == FieldType.Switch)
        type = "[int]";
    else if (item.Type == FieldType.Number)
        type = string.IsNullOrWhiteSpace(item.Length) ? "[int]" : $"[decimal]({item.Length})";
    else
    {
        if (string.IsNullOrWhiteSpace(item.Length))
            type = "[ntext]";
        else if (item.Id.StartsWith("Is") || item.Id.EndsWith("Id") || item.Id.EndsWith("No") || item.Id == "CompNo")
            type = $"[varchar]({item.Length})";
        else
            type = $"[nvarchar]({item.Length})";
    }

    if (type.Length < 16)
        type += new string(' ', 16 - type.Length);

    return type;
}

依赖关系分析

数据库适配层主要依赖:

  1. Database类 - 提供基础数据库连接功能
  2. FieldInfo类 - 字段元数据信息
  3. DbModelInfo类 - 表模型信息
classDiagram
    DbProvider --> Database
    DbProvider --> FieldInfo
    DbProvider --> DbModelInfo

性能考量

  1. SQL生成优化:使用StringBuilder构建SQL语句,避免字符串拼接性能问题
  2. 类型映射缓存:可以考虑缓存类型映射结果,减少重复计算
  3. 分页处理:各数据库分页语法不同,适配器需要针对特定数据库优化分页查询

常见问题解决

1. 如何添加新的数据库支持?

  1. 创建新的适配器类继承DbProvider
  2. 实现所有抽象方法
  3. 注册到数据库工厂中

示例骨架:

class NewDbProvider : DbProvider
{
    // 实现必要的方法
    internal override string GetTableSql(string dbName) { ... }
    internal override string GetTableScript(...) { ... }
    // 其他方法实现
}

2. 连接字符串处理

MySQL适配器提供了连接字符串解析示例:

private string GetSchemaName(string dbName)
{
    var items = Database.ConnectionString.Split(';');
    foreach (var item in items)
    {
        var names = item?.Split('=');
        if (names != null && names.Length > 1 && 
            (names[0] == "Initial Catalog" || names[0] == "Database"))
        {
            dbName = names[1];
            break;
        }
    }
    return dbName;
}

3. 布尔值处理差异

不同数据库对布尔值的处理不同,适配器需要统一处理:

  • SQL Server/Oracle: 转换为1/0
  • MySQL/SQLite: 使用特定语法

结论

数据库适配层通过统一的接口抽象,使Known项目能够灵活支持多种数据库。各适配器封装了数据库特定的语法和特性,上层业务代码可以无缝切换不同数据库。这种设计提高了系统的可扩展性和可维护性,是典型的多态和抽象工厂模式的应用。