Electron中使用typorm,better-sqlite3实现本地数据sqlite存储

5,401 阅读1分钟

前言

本文主讲在 electron 中使用 typeOrm 和 better-sqlite3 实现本地数据存储以及读取,以及开发过程中遇到问题的解决方案

阅读须知:

  1. 代码使用 TypeScript,vue-setup
  2. 脚手架 electron-vite
  3. 开发环境:window,node:v16.15.1,pnpm:v7.9.0

本文知识点

  • 用 typeOrm 创建连接时,在 Electron 中需要调整的参数
  • 解决 better-sqlite3 使用内置 node 版本和项目使用的 Electron 内置 node 版本不一致

主进程编写

安装

pnpm i typeorm
pnpm i better-sqlite3

实现连接数据库方法

// src\main\service\database.ts
import path from "path";
import { DataSource } from "typeorm";
import { app } from "electron";
import { is } from "@electron-toolkit/utils";
import { BetterSqlite3ConnectionOptions } from "typeorm/driver/better-sqlite3/BetterSqlite3ConnectionOptions";
import { MessageModel } from "./model";

export class DataBase {
  dataSource: DataSource;

  //初始化数据库文件
  constructor(database: string) {
    //使用该方法获取缓存目录从而实现软件升级或卸载数据保留
    // 例:windows下文件存储位置
    // C:\Users\WHWAN\AppData\Roaming\pc-client\data\message.db
    let basePath = path.join(
      app.getPath("appData"),
      app.getName(),
      `./data/${database}.db`
    );
    let options: BetterSqlite3ConnectionOptions = {
      type: "better-sqlite3",
      entities: [MessageModel],
      database: basePath,
      synchronize: true,
    };
    this.dataSource = new DataSource(options);
  }
}

使用出现如下报错

Error: The module '...node_modules\better-sqlite3\build\Release\better_sqlite3.node'
was compiled against a different Node.js version using
NODE_MODULE_VERSION $XYZ. This version of Node.js requires
NODE_MODULE_VERSION $ABC. Please try re-compiling or re-installing
the module (for instance, using `npm rebuild` or `npm install`).

安装

pnpm i -D electron-rebuild

package.json中添加命令并执行,即可构建对应版本better-sqlite3

"rebuild": "electron-rebuild -f -w better-sqlite3"

使用示例

创建表结构

import { Entity, PrimaryColumn, Column } from "typeorm";

@Entity()
export class MessageModel {
  @PrimaryColumn({ type: "int" })
  id: number | undefined;

  @Column({ type: "int8", nullable: false })
  roomId: number | undefined;

  @Column({ type: "text", nullable: false })
  content: string | undefined;

  @Column({ type: "text", nullable: false })
  type: string | undefined;
}

实现 CRUD

主进程实现

// src\main\service\messageService.ts
import { ipcMain } from "electron";
import { DataSource } from "typeorm";
import { WindowsMain } from "../windows";
import { DataBase } from "./database";

//创建数据查询Modal
export interface MsgListDTO extends ListDTO {
  roomId: number;
}
//列表查询基类
export interface ListDTO {
  pageNum: number;
  pageSize: number;
  sort: number;
}

//实现MessageService
export class MessageService {
  static instance: MessageService;
  dataSource: DataSource;

  //使用单例模式
  static getInstance() {
    if (!this.instance) {
      this.instance = new MessageService();
    }
    return this.instance;
  }

  constructor() {
    //创建数据库
    this.dataSource = new DataBase("message").dataSource;
  }

  //初始化主角进程监听事件
  init() {
    //新增数据监听
    ipcMain.on(
      "create-message",
      async (_event, data: { winViewId: number; val: MessageModel }) => {
        const info = new MessageModel();
        info.roomId = data.val.roomId;
        info.content = data.val.content;
        info.type = data.val.type;
        const res = await this.create(info);
        const win = WindowsMain.getInstance().getWin(data.winViewId);
        win && win.webContents.send("update-messages", res);
      }
    );

    //获取数据列表监听
    ipcMain.on(
      "get-message",
      async (_event, data: { winViewId: number; params: MsgListDTO }) => {
        const res = await this.getList(data.params);
        const win = WindowsMain.getInstance().getWin(data.winViewId);
        win && win.webContents.send("get-messages", res);
      }
    );
  }

  //实现新增方法
  async create(message: MessageModel) {
    await this.dataSource.initialize();
    const res = await this.dataSource.manager.save(message);
    await this.dataSource.destroy();
    return res;
  }

  //实现分页查询
  async getList(options: MsgListDTO) {
    await this.dataSource.initialize();
    const skip = options.pageSize * options.pageNum - options.pageSize;
    const sort = options.sort === 2 ? "ASC" : "DESC";
    const listAndCount = await this.dataSource
      .createQueryBuilder(MessageModel, "message")
      .where(`message.roomId = ${options.roomId}`)
      .orderBy("message.id", sort)
      .skip(skip)
      .take(options.pageSize)
      .getManyAndCount();
    await this.dataSource.destroy();
    return { list: listAndCount[0], count: listAndCount[1] };
  }
}

渲染进程新增及查询

//创建消息体
export interface MessageSqlModel {
  id?: number;
  roomId: number;
  content: string;
  type: string;
}
const message = ref<MessageSqlModel>({
  roomId: 1,
  content: "你好",
  type: "text",
});
//发送消息
const onSend = () => {
  window.electron.ipcRenderer.send("create-message", {
    winViewId: window.winViewId,
    val: { ...message.value },
  });
};
//获取更新消息
window.electron.ipcRenderer.on("update-messages", (_event, data) => {
  console.log("update-messages", data);
});

//分页查询
window.electron.ipcRenderer.on("get-messages", (_event, data) => {
  console.log("get-messages", data);
});
window.electron.ipcRenderer.send("get-message", {
  winViewId: window.winViewId,
  params: {
    roomId: 1,
    sort: "ASC",
    pageNum: 1,
    pageSize: 5,
  },
});

后记

本文为学习中记录一下遇到的问题 欢迎交流

参考文档