【electron】-electron的应用版本升级更新客户端库表的方法(拙见)

提要

  1. 之前开坑实现了electron中使用better-sqlite3,只完成了使用以及基础配置
  2. 后面有个新的场景,就是后续客户端版本升级,当更新内容涉及表结构变化时,需要同时更新客户端的库表,然后还不能影响到客户的历史数据,之前想到的是用knex但是一直没操作成功(一直报错来着)。
  3. 后面想了几种场景,发现用knex有点复杂了,其实从数据库层面来看,可以根据版本变化直接执行更新的sql语句,来进行更新。

具体操作

创建数据库时同时创建数据库版本,根据手动控制版本变化选择是否执行字段或者库表更新sql。下面是实际操作流程。
在这里插入图片描述

  1. 先创建一个migrations的文件夹,该文件夹下存放更新用的sql。
  2. 约定该文件夹下sql的名字为1.sql …2.sql 文件名即为当前数据库的版本号。(后面我又弄了一个json来做管理,感觉比多文件要省事儿)目录结构如下。
    在这里插入图片描述
  3. 初始化一张版本控制表,记录当前应用数据库版本信息。可以放到schema.sql里随版本初始化一起生成,或者对现有应用改造的话就在代码里db连上之后生成。
    CREATE TABLE IF NOT EXISTS migrations (
      id INTEGER PRIMARY KEY AUTOINCREMENT,
      version INTEGER NOT NULL,
      description TEXT,
      applied_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
      UNIQUE(version)
    );
    
  4. 所有的历史版本sql一定不能删除或者修改!!因为后面更新要按版本顺序更新库表。修改会导致不同版本的客户端出现问题! 所以涉及数据库的变化一定在开发环境测试好再上线!!
  5. 代码就不写了,大致思路就是获取目录下的文件名和数据库里最新的version对比,如果文件名比库里的version大就执行一下sql文件,既可完成sql的更新。注意一点要把更新的sql文件打到包里 package.json里配置 data/migrations/*.sql
    在这里插入图片描述

改进

前面的文件依赖创建迁移的sql文件,如果更新的频繁会有很多个sql文件,目前想到改进的方案是创建一个json文件,然后通过读取json文件来控制版本升级,这样就一个json文件就行了。以下是核心代码,思路还是把json的内容读出来,按照约定好的格式进行解析,执行sql更新。

{
  "version-2": {
    "description": "测试更新2",
    "sqls": ["CREATE TABLE IF NOT EXISTS detail (id INTEGER PRIMARY KEY AUTOINCREMENT,name TEXT NOT NULL,description TEXT,price REAL NOT NULL );"]
  },
  "version-1": {
    "description": "测试更新1",
    "sqls": ["ALTER TABLE users ADD COLUMN phone TEXT;", "ALTER TABLE products ADD COLUMN phone TEXT;"]
  }
}

 // 初始化迁移表并获取当前版本
  private async initializeMigrations(): Promise<void> {
    if (!existsSync(this.migrationDir)) {
      mkdirSync(this.migrationDir, { recursive: true });
      logger.info(`迁移目录已创建: ${this.migrationDir}`);
    }
    this.checkAndApplyMigrations();
  }
  // 检查并应用迁移
  private checkAndApplyMigrations(): void {
    const currentVersion = this.getCurrentVersionFromDB();
    const migrations = this.getMigrationFilesFromJson();
    logger.info(`test: ${JSON.stringify(migrations)}`);
    logger.info(`当前数据库版本: ${currentVersion}`);

    // 筛选出版本号高于当前版本的迁移文件
    const pendingMigrations = migrations.filter((m: any) => {
      return m.version > currentVersion;
    });
    logger.info(`pendingMigrations${JSON.stringify(pendingMigrations)}`);

    if (pendingMigrations.length === 0) {
      logger.info('数据库已是最新版本,无需迁移');
      return;
    }

    logger.info(`发现 ${pendingMigrations.length} 个待应用迁移`);

    // 按版本号升序执行迁移
    const sortedMigrations = [...pendingMigrations].sort((a, b) => a.version - b.version);
    // 使用事务执行所有迁移
    const transaction = this.db.transaction(() => {
      sortedMigrations.forEach((migration) => {
        try {
          if (migration.sqls && migration.sqls.length > 0) {
            migration.sqls.forEach((sql) => {
              this.db.exec(sql);
            });
            this.recordMigration(this.db, migration.version, `迁移记录成功 - 版本: ${migration.version},说明: ${migration.description}`);
          }
        } catch (error) {
          logger.error(`迁移失败 ${migration.version}.sql:`, error);
          throw error; // 回滚事务
        }
      });
    });
    transaction();
    logger.info('所有迁移已完成');
  }
  private getMigrationFilesFromJson() {
    const migrationPath = resolve(this.migrationDir, 'update.json');
    if (!existsSync(migrationPath)) {
      logger.info('未找到迁移文件: update.json');
      return [];
    }
    const migrationContent = readFileSync(migrationPath, 'utf8');
    const migrationData = JSON.parse(migrationContent);
    // 提取所有版本键并按数字排序
    const versionKeys = Object.keys(migrationData)
      .filter((key) => key.startsWith('version-'))
      .sort((a, b) => {
        const numA = parseInt(a.split('-')[1], 10);
        const numB = parseInt(b.split('-')[1], 10);
        return numA - numB;
      });
    return versionKeys.map((key, index) => {
      return { ...migrationData[key], version: index + 1 };
    });
  }

结论

  1. 目前只尝试了一些简单的更新,类似增加字段,增加库表等操作。更复杂的操作,类似修改字段类型,修改表结构等操作还没具体尝试。要多注意处理好执行错误还有兜底。开发环境多进行验证。
  2. 大家有更好,更完善的做法欢迎批评指正
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值