目录

背景

使用代码

兴趣点


背景

我经常使用C#, 当我与database通信时,我喜欢使用基于ORM的结构。

现在我开始使用构建应用程序react-native并希望sqlite用作存储。

但我讨厌使用SQL查询,因为它看起来不如ORM好。

当我想使用那里的ORM库时,它非常大并且有太多我不需要的代码,所以我构建了自己的ORM。

使用代码

首先,我们需要的是能够知道表的构造器。

所以我们会知道database表的样子了。这就是为什么我们的第一步是构建TableStructor及其组件的原因。

export enum ColumnType {Number,String,Decimal,Boolean
}export class constraint{columnName: string;contraintTableName:string;contraintColumnName:string;constructor(columnName: string, contraintTableName:string, constrainColumnName: string){this.columnName = columnName;this.contraintTableName = contraintTableName;this.contraintColumnName = constrainColumnName;}
}export class columnStructor {columnType: ColumnType;nullable?: boolean;columnName: string;isPrimary?: boolean;autoIncrement?: boolean;constructor(columnType: ColumnType, columnName: string, isPrimary?: boolean, autoIncrement?: boolean, nullable?: boolean) {this.columnType = columnType;this.nullable = nullable;this.columnName = columnName;this.isPrimary = isPrimary;this.autoIncrement = autoIncrement;}
}export default class tablaStructor {tableName: string;columns: columnStructor[];constraints?:constraint[];constructor(tableName: string, columns:columnStructor[], constraint?:constraint[]){this.tableName = tableName;this.columns = columns;this.constraints = constraint;}
}

我们的下一步是构建一个其他类可以extend的基类。让我们称之为BaseModule。

// All your tables names should be added here.
export type TableNames = "Users" | "Items" | "System"
export default class BaseModule {public id: number;public tableName: TableNames;constructor(tableName: TableNames, id?: number) {this.id = id ?? 0;this.tableName = tableName;}
}   

现在让我们创建我们的Users模块。它应该非常简单。

import BaseModule from './baseModule';
import TableStructor, { ColumnType } from './structor';
export default class user extends BaseModule {public userName: string;public passowrd: string;public name: string;public age?: number;constructor(userName: string, passowrd: string,name: string,age?: number, id?: number) {super('Users', id);this.userName = userName;this.passowrd = passowrd;this.name = name;this.age = age;}// here, you should build your table structure.static GetTableStructor() {return new TableStructor("Users",[{ columnName: "id", columnType: ColumnType.Number, nullable: false, isPrimary: true, autoIncrement: true },{ columnName: "userName", columnType: ColumnType.String },{ columnName: "passowrd", columnType: ColumnType.String },{ columnName: "name", columnType: ColumnType.String },{ columnName: "age", columnType: ColumnType.Number, nullable: true },],// if you need to add a constraint, then here is how you could do it as an example//[//{ contraintTableName: "Person", contraintColumnName: "id", columnName: "person_Id" }//])}
}

我们的下一步是构建我们的存储库,我们将expo-sqlite用作我们的database。

我们需要这个存储库做的是以下内容:

  1. 设置数据库。
  2. 查找模块是否已更改并将这些更改应用于database, 例如,在从模块添加和删除新属性时。
  3. 保存一个项目并返回最后添加的项目。
  4. Where方法来搜索您的返回可用项目查询的database 。
  5. 删除项。
 // note: that single, toType are global extension I use, to make things simplerimport * as SQLite from 'expo-sqlite';export default class Repository {static dbIni: Boolean;databaseName: string;database?: SQLite.WebSQLDatabase;constructor() {this.databaseName = 'mydb.db';}createConnection = (force?: boolean) => {if (!this.database || force)this.database = SQLite.openDatabase(this.databaseName);return this.database;};// this is so we know which column the Table is in database containerallowedKeys = (tableName: string) => {return new Promise((resolve, reject) => {this.createConnection().transaction((x) =>x.executeSql(`PRAGMA table_info(${tableName})`,undefined,(trans, data) => {var keys = [] as string[];for (var i = 0; i < data.rows.length; i++) {if (data.rows.item(i).name != 'id')keys.push(data.rows.item(i).name);}resolve(keys);},),(error) => {reject(error);},);}) as Promise<string[]>;};private find = (query: string, args?: any[], tableName?: TableNames) => {var tables = [Users.GetTableStructor()]return new Promise((resolve, reject) => {this.createConnection().transaction(async (x) => {console.log('Executing Find..');x.executeSql(query,args,async (trans, data) => {var booleanColumns =tables.find(x => x.tableName == tableName)?.columns.filter(x => x.columnType == ColumnType.Boolean);console.log('query executed:' + query);const translateKeys = (item: any) => {if (!item || !booleanColumns || booleanColumns.length <= 0)return item;booleanColumns.forEach(column => {if (item[column.columnName] != undefined &&item[column.columnName] != null) {if (item[column.columnName] === 0 ||item[column.columnName] === "0" || item[column.columnName] === false)item[column.columnName] = false;else item[column.columnName] = true;}})return item;}var items = [] as BaseModule[];for (var i = 0; i < data.rows.length; i++) {var item = data.rows.item(i);items.push(translateKeys(item));}resolve(items);},(_ts, error) => {console.log('Could not execute query:' + query);console.log(error);reject(error);return false;},);},(error) => {console.log('Could not execute query:' + query);console.log(error);reject(error);},);}) as Promise<basemodule[]>;};async where<t>(tableName: TableNames, query?: any | T) {var q = `SELECT * FROM ${tableName} ${query ? 'WHERE ' : ''}`;var values = [] as any[];if (query && Object.keys(query).length > 0) {Object.keys(query).forEach((x, i) => {var start = x.startsWith('$') ?x.substring(0, x.indexOf('-')).replace('-', '') : undefined;if (!start) {q += x + '=? ' + (i < Object.keys(query).length - 1 ? 'AND ' : '');values.push(query[x]);} else {if (start == '$in') {var v = query[x] as [];q += x.replace("$in-", "") + ' IN (';v.forEach((item, index) => {q += '?' + (index < v.length - 1 ? ', ' : '');values.push(item);});}q += ') ' + (i < Object.keys(query).length - 1 ? 'AND ' : '');}});}return ((await this.find(q, values, tableName)).map((x) => {x.tableName = tableName;return x;}).toType<t>() ?? []);}// get the last inserted or updated item.async selectLastRecord<t>(item: BaseModule) {console.log('Executing SelectLastRecord... ');if (!item.tableName) {console.log('Table name cant be empty for:');console.log(item);return;}return (await this.find(!item.id || item.id <= 0 ? `SELECT * FROM ${item.tableName} _ORDER BY id DESC LIMIT 1;` : `SELECT * FROM ${item.tableName} WHERE id=?;`,item.id && item.id > 0 ? [item.id] : undefined, item.tableName)).toType<t>().map((x: any) => { x.tableName = item.tableName; return x; }).single<t>();}delete = async (item: BaseModule, tableName?: TableNames) => {tableName = item.tableName ?? tableName;var q = `DELETE FROM ${tableName} WHERE id=?`;await this.execute(q, [item.id]);};// this method will update and insert depending on Id and parameter insertOnlypublic save<t>(item?: BaseModule, insertOnly?: Boolean, tableName?: TableNames) {if (!item) return undefined;if (!item.tableName || item.tableName.length <= 3)item.tableName = tableName ?? "ApplicationSettings";return new Promise(async (resolve, reject) => {try {console.log('Executing Save...');var items = await this.where<basemodule>(item.tableName, { id: item.id });var keys = (await this.allowedKeys(item.tableName)).filter((x) =>Object.keys(item).includes(x));let query = '';let args = [] as any[];if (items.length > 0) {if (insertOnly) return;query = `UPDATE ${item.tableName} SET `;keys.forEach((k, i) => {query += ` ${k}=? ` + (i < keys.length - 1 ? ',' : '');});query += ' WHERE id=?';} else {query = `INSERT INTO ${item.tableName} (`;keys.forEach((k, i) => {query += k + (i < keys.length - 1 ? ',' : '');});query += ') values(';keys.forEach((k, i) => {query += '?' + (i < keys.length - 1 ? ',' : '');});query += ')';}keys.forEach((k: string, i) => {args.push((item as any)[k] ?? null);});if (items.length > 0) args.push(item.id);await this.execute(query, args);resolve(((await this.selectLastRecord<t>(item)) ?? item) as T);} catch (error) {console.log(error);reject(error);}}) as Promise<t>;}// this is a simple execute SQL query.private timeout?: any;private execute = async (query: string, args?: any[]) => {return new Promise((resolve, reject) => {this.createConnection().transaction((tx) => {clearTimeout(this.timeout)this.timeout = setTimeout(() => {console.log("timed out")reject("Query Timeout");}, 2000);console.log('Execute Query:' + query);tx.executeSql(query,args,(tx, results) => {console.log('Statment has been executed....' + query);clearTimeout(this.timeout)resolve(true);},(_ts, error) => {console.log('Could not execute query');console.log(args);console.log(error);reject(error);clearTimeout(this.timeout)return false;},);},(error) => {console.log('db executing statement, has been terminated');console.log(args);console.log(error);reject(error);clearTimeout(this.timeout)throw 'db executing statement, has been terminated';},);});};// validate of the gevin module differs from the database tableprivate validate = async (item: TablaStructor) => {var appSettingsKeys = await this.allowedKeys(item.tableName);return appSettingsKeys.filter(x => x != "id").length != item.columns.filter(x => x.columnName != "id").length || item.columns.filter(x => x.columnName != "id" &&!appSettingsKeys.find(a => a == x.columnName)).length > 0;}private cloneItem<t>(item: any, appended: any, ignoreKeys?: string[]) {var newItem = {} as any;if (appended === undefined)return item;Object.keys(item).forEach((x) => {if (Object.keys(appended).find((f) => f == x) &&appended[x] !== undefined && (!ignoreKeys || !ignoreKeys.includes(x)))newItem[x] = appended[x];else newItem[x] = item[x];});return (newItem as T);}setUpDataBase = async (forceCheck?: boolean) => {if (!Repository.dbIni || forceCheck) {const dbType = (columnType: ColumnType) => {if (columnType == ColumnType.Boolean || columnType == ColumnType.Number)return "INTEGER";if (columnType == ColumnType.Decimal)return "REAL";return "TEXT";}console.log(`dbIni= ${Repository.dbIni}`);console.log(`forceCheck= ${forceCheck}`);console.log("initialize database table setup");this.createConnection(true); // make sure to close all transaction.var tables =[User.GetTableStructor()] // all your table in the right ordersawait tables.asyncForeach(async (table) => {var query = `CREATE TABLE if not exists ${table.tableName} (`;table.columns.forEach((col, index) => {query += `${col.columnName} ${dbType(col.columnType)} ${!col.nullable ?"NOT NULL" : ""} ${col.isPrimary ? "UNIQUE" : ""},\n`});table.columns.filter(x => x.isPrimary === true).forEach((col, index) => {query += `PRIMARY KEY(${col.columnName} ${col.autoIncrement === true ?"AUTOINCREMENT" : ""})` + (index < table.columns.filter(x => x.isPrimary === true).length - 1 ? ",\n" : "\n");});if (table.constraints && table.constraints.length > 0) {query += ",";table.constraints.forEach((col, index) => {query += `CONSTRAINT "fk_${col.columnName}" FOREIGN KEY(${col.columnName})REFERENCES ${col.contraintTableName}(${col.contraintColumnName})` +(index < (table.constraints?.length ?? 0) - 1 ? ",\n" : "\n");});}query += ");";await this.execute(query);})}}// this is where you will find all your giving module changes// and apply it to the databasenewDataBaseStructure = async () => {var items = [] as {tableName:TableNames, items:BaseModule[]}[];if (await this.validate(User.GetTableStructor())) {console.info("Structor changes has been found in User.");var users = await this.where<user>("Users");if (users.length) {items.push({ tableName: "Users", items: users.map(x => this.cloneItem(new User(x.userName, x.password, x.name, x.age), x, ["id", "tableName"])) });}await this.execute(`DROP TABLE if exists Users`);}// Insert the old data to the new table and apply your module changeif (items.length > 0) {await this.setUpDataBase(true);this.createConnection(true); // make sure to close all transaction.await items.reverse().asyncForeach(async x => {console.info(`Ìnserting items into ${x.tableName}`);await x.items.asyncForeach(async item => {var savedItem = await this.save(item, undefined, x.tableName);})});this.createConnection(true); // make sure to close all transaction.return true;}}
}

嗯,就是这样!

现在我们应该能够使查询变得非常简单。

见下文:

var rep= new Repository();
// When your app starts, run this
await rep.setUpDataBase();
await rep.newDataBaseStructure();// thereafter, run your command.
var users = await rep.where<User>("Users", {age: 20});
// Orvar users = await rep.where<User>("Users",{"$in-age": [20,30, 25], userName: "testUser"});users[0].age = 35;
var changedUser = await rep.save<User>(users[0]);

兴趣点

这是构建ORM并完全控制数据库和查询的设置方式的一种非常简单的方法。

https://www.codeproject.com/Tips/5312038/Building-a-Simple-Typescript-ORM-around-sqlite

围绕sqlite构建一个简单的Typescript ORM相关推荐

  1. 基于PyTorch,如何构建一个简单的神经网络

    本文为 PyTorch 官方教程中:如何构建神经网络.基于 PyTorch 专门构建神经网络的子模块 torch.nn 构建一个简单的神经网络. 完整教程运行 codelab→ https://ope ...

  2. python推荐系统-利用python构建一个简单的推荐系统

    摘要: 快利用python构建一个属于你自己的推荐系统吧,手把手教学,够简单够酷炫. 本文将利用python构建一个简单的推荐系统,在此之前读者需要对pandas和numpy等数据分析包有所了解. 什 ...

  3. github密码格式_如何使用GitHub构建一个简单的网页 (不用框架版本)

    1.申请GitHub账号 进入GitHub官网,点击右上角的Sign up进行注册, 注册很简单,只要填写好用户名,邮箱,密码就行(已注册的用户名,邮箱不能再进行注册) 下面有一个你是人类的验证(照着 ...

  4. 【编译原理】构建一个简单的解释器(Let’s Build A Simple Interpreter. Part 9.)(笔记)语法分析(未完,先搁置了!)

    [编译原理]让我们来构建一个简单的解释器(Let's Build A Simple Interpreter. Part 9.) 文章目录 spi.py spi_lexer 我记得当我在大学(很久以前) ...

  5. 【编译原理】构建一个简单的解释器(Let’s Build A Simple Interpreter. Part 8.)(笔记)一元运算符正负(+,-)

    [编译原理]让我们来构建一个简单的解释器(Let's Build A Simple Interpreter. Part 8.) 文章目录 C语言代码(作者没提供完整的python代码,关键的改动提供了 ...

  6. 【编译原理】构建一个简单的解释器(Let’s Build A Simple Interpreter. Part 7.)(笔记)解释器 interpreter 解析器 parser 抽象语法树AST

    [编译原理]让我们来构建一个简单的解释器(Let's Build A Simple Interpreter. Part 7.) 文章目录 python代码 插--后序遍历 C语言代码(有错误) C语言 ...

  7. 【编译原理】让我们来构建一个简单的解释器(Let’s Build A Simple Interpreter. Part 6.)(python/c/c++版)(笔记)

    [编译原理]让我们来构建一个简单的解释器(Let's Build A Simple Interpreter. Part 6.) 文章目录 python代码 C语言代码 总结 今天是这一天:) &quo ...

  8. 【编译原理】让我们来构建一个简单的解释器(Let’s Build A Simple Interpreter. Part 5.)(python/c/c++版)(笔记)Lexer词法分析程序

    [编译原理]让我们来构建一个简单的解释器(Let's Build A Simple Interpreter. Part 5.) 文章目录 python代码 C语言代码 总结 你如何处理像理解如何创建解 ...

  9. 【编译原理】让我们来构建一个简单的解释器(Let’s Build A Simple Interpreter. Part 4.)(python/c/c++版)(笔记)

    [编译原理]让我们来构建一个简单的解释器(Let's Build A Simple Interpreter. Part 4.) 文章目录 python代码 C语言代码 总结 在上一篇文章中,您学习了如 ...

最新文章

  1. JavaScript开发规范 0.01版
  2. grasshop 犀牛5.0下载_新安|原创剑尊下载新版本 2020最新安卓版剑尊下载地址整理...
  3. ssh免密登录方法不生效?Authentication refused: bad ownership or modes for directory
  4. stm32f10x单片机进阶--spi使用
  5. Educational Codeforces Round 14 - F (codeforces 691F)
  6. JS-节点增删改-document-HTML DOM-事件
  7. 信用卡-可恶的招商银行,可恶的循环利息
  8. 汇丰银行是哪个国家的
  9. 最近公共祖先_LeetCode 236. 二叉树的最近公共祖先
  10. 《数字图像处理与机器视觉——Visual C++与Matlab实现》——0.2 数字图像处理与识别...
  11. 页面调用微信扫一扫功能
  12. vs2005启动不了,手把手教你修复它
  13. windows远程控制服务器
  14. sql语句之delete
  15. 苹果8wifi找不到服务器,iPhone8连不上wifi怎么办?苹果iPhone8无法连接网络的解决方法...
  16. 根据人民币大写规则写的java工具类
  17. Python实验四:Python程序设计之文件
  18. SqlServer2005中数据库角色成员身份
  19. rails rjs select method help
  20. Reading22. Understanding Balance Sheets

热门文章

  1. android恢复联系人,如何从Android手机恢复联系人[最佳方式]
  2. linux 重启服务器_linux入门-----6
  3. 启动hive报错_远程服务器模式Hive的搭建过程详解
  4. api laravel 统一返回方法_Laravel-自定义API返回的JSON格式
  5. java se development kit可以卸载吗_首款纯电版MINI COOPER详细评测,或将国产,值得等吗?...
  6. 国风这么火,少不了古风建筑PNG格式
  7. 古典绘画水墨文化艺术插图手绘合集,再也不愁没有设计灵感!
  8. 万能广告促销海报,找不到灵感也不怕
  9. 早秋精品电商男装页面\海报设计PSD模板
  10. UI素材干货模板|插画动效工作区域多个动画对象和6个动画场景