js环境_electron环境,使用typeorm+sqlite数据库

235 阅读5分钟

大家有任何想法,都可以联系博主沟通。

本系列为实战文章,最终实现的桌面工具软件,获取方式:百度网盘地址:pan.baidu.com/s/1yrl0jYpt…


前言

最近桌面软件需要管理的数据增多,将数据保存在缓存或者本地json文件的方式,已经不能满足频繁的数据存取操作,所以需要寻找本地数据持久化的方案。


一、引读

阅读本文的同学,可能对数据库了解不多,所以这里先简单阐述下数据库,帮助大家更好理解数据库。

首先要理解数据库的本质,是一个文件,文件里存储数据,每次需要操作数据,就操作文件里的数据,完成增删改查,从而实现持久化存储的能力。(都存本地文件了,当然持久)

从存储功能维度来看,数据库的数据文件和我们手动创建txt、json、js来保存数据,并没有什么本质区别。

那我们为什么要选择数据库呢?

因为数据库还提供了软件功能,软件部分负责操作数据库数据文件,从而实现快速、高并发、安全、一致、可拓展、可版本管理地操作数据。数据库提供了如此多强大的功能,肯定不能简单通过鼠标点点点就全部实现,所以它提供了一系列的api,来管理数据。

虽然数据库提供了一系列强大的api,能够协助我们完成所有的数据库运维工作,但是愿意记那么多api去一点点敲命令,除了特殊环境下不得不这样做的情况,可能也只有装x犯能忍受。所以我们还需要一个便捷的可视化操作工具,例如navicat。

了解了数据库的基本情况,我们就可以很容易选择最适合我们的数据库。我的数据库要集成到electron中,运行到每一个PC机而不是服务器,关系与非关系型都可以,功能不需要太复杂,综合考虑,最合适的就是精简轻量的数据库——sqlite。

二、哪些人可以参考本文

如果读者使用的是midwayjs、nestjs等成熟后端框架,那么可能有更便捷的插件方式,将typeorm+sqlite数据库应用于项目,这种情况,一定不要被本文误导,你需要的是去官网寻找方案。

如果读者的开发语言是ts,会有更强大的装饰器语法,更方便的初始化sqlite,更简单地使用orm,不需要完全参考本文,不过本文部分内容可以为你提供思路。

如果读者的开发语言是js,那么完全可以通过本文,完成typeorm+sqlite初始化的所有工作。

三、使用步骤

  1. 下载sqlite。
npm install sqlite3 --save
  1. 下载typeorm
npm install typeorm --save

orm可选有很多,选一个自己熟悉的即可。

  1. 实例化orm。创建一个文件sqliteInit.js,用来统一管理数据库初始化的配置。
const typeorm = require('typeorm')
const path = require('path')
const glob = require('glob');

const database = path.join(process.env.Electron, 'data', 'sqlite', 'database.sqlite')
// 动态获取实体路径
const entities = glob.sync(process.env.Electron+'data/sqlite/entry/*.js');

const dataSource = new typeorm.DataSource({
    type: 'sqlite',
    database,
    entities,
    // synchronize: true //自动创建表,否则新增entry时数据库中没有对应的表,设置无效,必须手动创建表
})

module.exports=dataSource

代码解释:

  • const database = path.join(process.env.Electron, 'data', 'sqlite', 'database.sqlite'):获取数据库数据文件地址。第一次运行时,发现没有数据文件,初始化时会自动创建这个数据文件。创建的文件大小为0,当插入数据后,初始大小为50K左右。 在这里插入图片描述

  • const entities = glob.sync(process.env.Electron+'data/sqlite/entry/*.js'):我所有的实体对象,都保存在entry文件夹中,所以此处使用glob工具,遍历所有实体对象,如果使用require一个个引入对象,也是可以的。 在这里插入图片描述

  1. 上面只是实例化了orm,如果不调用初始化方法,会报错:“EntityMetadataNotFoundError: No metadata for "xxxx" was found”

在main.js中初始化orm:

const AppDataSource=require('../data/sqlite/sqliteInit')
app.whenReady().then(async () => {
...省略代码
//初始化orm
await AppDataSource.initialize()
})
  1. 创建对应的实体对象:
const {  EntitySchema } = require('typeorm');

// 使用 EntitySchema 来定义实体
module.exports = new EntitySchema({
    name: "Service",
    tableName: "serviceManage",
    columns: {
        id: {
            primary: true,
            type: "int",
            generated: true,
        },
        jarId: {
            //对应的jar包id
            type: "varchar",
            nullable:true

        },
        name:{
            //服务名
            type: "varchar",
        },
        runStatus:{
            //运行状态
            type: "boolean",
            nullable:true
        }
    },
})

上面代码中,主要是了解创建实体的方法是new EntitySchema,网上这部分资料比较少,这是官网推荐方式。

其中的属性,可以自行查阅。

四、使用typeOrm操作数据库

4.1保存和修改数据

const AppDataSource = require('../sqliteInit.js')
const {nanoid}=require('nanoid')
class ServiceManage {
   async addService(option={}) {
        const {serviceName} = option
        const id = nanoid();
       const serviceRepository= AppDataSource.getRepository('Service')
        const rs =await serviceRepository.save({
            id,
            name:serviceName
        })
        console.log(rs)
        return 'rs'
    }
}
module.exports= ServiceManage

代码解释:

  1. AppDataSource是实例化后的对象,后续所有的api都是通过AppDataSource 对象来操作。

  2. nanoid是一个生成随机id的库,类似与uuid,比uuid生成的长度更短,库大小更轻量。

  3. AppDataSource.getRepository('Service')可以得到对应实体的实例化对象serviceRepository,serviceRepository对象提供了各种操作api

  4. serviceRepository.save是保存数据的方法,返回的是promise对象。

如果保存数据时,发现id已经存在,则会更新修改数据

4.2删除数据

写这篇笔记时,还没有完善功能,所以只跑通一个保存方法,其他方法,参考以往的代码,先简单实现,应该大差不差。

复杂的sql语句,最好借助queryBuilder方式:

 async deleteService(id){
        const photoRepository = AppDataSource.getRepository('Service')
        const rs = await photoRepository.createQueryBuilder('Service')
            .delete()
            .where(`Service.id = ${id}`)
            .execute()
        return rs
    }

4.3查询数据

查询语法也是盲写,还未验证,为大家提供个思路:

 async findServiceList(option) {
        const {id, skip, limit} = option
        const photoRepository = AppDataSource.getRepository('Service')
        let queryBuilder = photoRepository.createQueryBuilder('Service')
        const total = await queryBuilder.getCount()
        if (id) {
            queryBuilder = queryBuilder
                .where(`'Service'.id = ${id}`)
        }
        if (skip) {
            queryBuilder
                .skip(skip)
                .take(limit)

        } else if (skip === 0) {
            queryBuilder
                .take(limit)
        }
        const rs = await queryBuilder.getMany()
        return {
            success: true,
            total,
            data: rs
        }
    }

skip:跳过多少条数据,limit:查询几条数据,这两个参数是为了实现分页功能。

五、总结

遇到网络资源缺乏的场景,一定要首先去查阅官网,如果官网查阅有难度,再考虑借鉴原创博主的方案,比如一直坚持原创的【中二少年学编程】同学,哈哈哈。