Procházet zdrojové kódy

添加数据库工具类和脚本,无需写sql,只需调用api即可完成增删改查

chenritian před 1 měsícem
rodič
revize
1c9b6c41c7

+ 5 - 0
commons/basic/Index.ets

@@ -1,3 +1,8 @@
+export { RelationalStoreUtils } from './src/main/ets/rdb/utils/RelationalStoreUtis'
+export { Test } from './src/main/ets/rdb/tables/Test'
+
+
+
 export { registerFontUtil } from './src/main/ets/utils/RegisterFontUtil'
 export { RouterPage } from './src/main/ets/ts'
 export { YTToast } from './src/main/ets/utils/YTToast'

+ 3 - 0
commons/basic/src/main/ets/rdb/sqls/RDBSql.ts

@@ -0,0 +1,3 @@
+export const rDbSql = {
+  CREATE_TEST_TABLE: 'CREATE TABLE IF NOT EXISTS Test (id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL, age INTEGER NOT NULL)'
+}

+ 6 - 0
commons/basic/src/main/ets/rdb/tables/Test.ets

@@ -0,0 +1,6 @@
+//名称就是表名
+export interface Test {
+  id: number;
+  name: string;
+  age: number;
+}

+ 221 - 0
commons/basic/src/main/ets/rdb/utils/RelationalStoreUtis.ets

@@ -0,0 +1,221 @@
+import { relationalStore } from '@kit.ArkData';
+import { Context } from '@ohos.abilityAccessCtrl';
+import { BusinessError } from '@kit.BasicServicesKit';
+import { rDbSql } from '../sqls/RDBSql';
+
+
+export interface CloudLoadItem {
+  uid: string,
+  date: string,
+  jsonDataPath: string,
+  lastRequestTime: number
+}
+
+
+export class RelationalStoreUtils {
+  private static cloudLoadStore: relationalStore.RdbStore | undefined = undefined;
+
+  //初始化数据库
+  static initReplayCloudStore(context: Context, success?: () => void) {
+    const REPLAY_CLOUD_STORE_CONFIG: relationalStore.StoreConfig = {
+      name: 'YT_HM.db', // 数据库文件名
+      securityLevel: relationalStore.SecurityLevel.S3, // 数据库安全级别
+      encrypt: false, // 可选参数,指定数据库是否加密,默认不加密
+      customDir: 'customDir/subCustomDir', // 可选参数,数据库自定义路径。数据库将在如下的目录结构中被创建:context.databaseDir + '/rdb/' + customDir,其中context.databaseDir是应用沙箱对应的路径,'/rdb/'表示创建的是关系型数据库,customDir表示自定义的路径。当此参数不填时,默认在本应用沙箱目录下创建RdbStore实例。
+      isReadOnly: false // 可选参数,指定数据库是否以只读方式打开。该参数默认为false,表示数据库可读可写。该参数为true时,只允许从数据库读取数据,不允许对数据库进行写操作,否则会返回错误码801。
+    };
+
+    // 假设当前数据库版本为3,表结构:EMPLOYEE (NAME, AGE, SALARY, CODES, IDENTITY)
+
+    relationalStore.getRdbStore(context, REPLAY_CLOUD_STORE_CONFIG, (err, store) => {
+      if (err) {
+        console.error(`Failed to get RdbStore. Code:${err.code}, message:${err.message}`);
+        return;
+      }
+      // 当数据库创建时,数据库默认版本为0
+      if (store.version === 0) {
+
+        try {
+          Object.keys(rDbSql).forEach((key) => {
+            store.executeSync(rDbSql[key])
+          })
+        } catch (error) {
+          console.error('Failed to create table:', error);
+        }
+        // 设置数据库的版本,入参为大于0的整数
+        store.version = 3;
+      }
+      success?.()
+      RelationalStoreUtils.cloudLoadStore = store;
+    })
+  }
+
+
+  static getStore() {
+    return RelationalStoreUtils.cloudLoadStore;
+  }
+
+
+  /**
+   * 插入数据
+   * @param tableName
+   * @param param
+   * @param success
+   */
+  static insert(tableName: string, param: ESObject, success?: () => void) {
+    const insertParam: relationalStore.ValuesBucket = {};
+    if (param) {
+      Object.keys(param).forEach((key) => {
+        insertParam[key] = param[key];
+      })
+    }
+    if (RelationalStoreUtils.cloudLoadStore !== undefined) {
+      (RelationalStoreUtils.cloudLoadStore as relationalStore.RdbStore).insert(tableName, insertParam,
+        (err: BusinessError, rowId: number) => {
+          if (err) {
+            console.error(`Failed to insert data. Code:${err.code}, message:${err.message}`);
+            return;
+          }
+          success?.();
+          console.info(`Succeeded in inserting data. rowId:${rowId}`);
+        })
+    }
+  }
+
+
+  /**
+   * 根据id更新数据
+   * @param tableName
+   * @param param
+   * @param success
+   */
+  static updateItemById(tableName: string, param: ESObject, success?: () => void) {
+    const updateParam: relationalStore.ValuesBucket = {};
+    Object.keys(param).forEach((key) => {
+      updateParam[key] = param[key];
+    })
+
+    let predicates = new relationalStore.RdbPredicates(tableName);
+    predicates = predicates.equalTo("id", updateParam.id)
+
+    if (RelationalStoreUtils.cloudLoadStore !== undefined) {
+      (RelationalStoreUtils.cloudLoadStore as relationalStore.RdbStore).update(updateParam, predicates,
+        (err: BusinessError, rows: number) => {
+          if (err) {
+            console.error(`Updated failed, code is ${err.code},message is ${err.message}`);
+            return;
+          }
+          success?.();
+          console.info(`Updated row count: ${rows}`);
+        })
+    }
+  }
+
+  /**
+   * 查询列表
+   * @param tableName
+   * @param queryParam
+   * @returns
+   */
+  static getList<T = ESObject>(tableName: string, queryParam?: ESObject): T[] {
+    const list: T[] = []
+    let predicates = new relationalStore.RdbPredicates(tableName);
+    let keys: string[] = []
+    if (queryParam) {
+      keys = Object.keys(queryParam);
+    }
+    if (RelationalStoreUtils.cloudLoadStore != undefined) {
+      // 正确拼接查询条件
+      // if (keys.length > 0) {
+      //   predicates = predicates.equalTo(keys[0], queryParam[keys[0]]);
+      //   for (let i = 1; i < keys.length; i++) {
+      //     predicates = predicates.and().equalTo(keys[i], queryParam[keys[i]]);
+      //   }
+      // }
+      //朝毅高级写法
+      if (keys.length > 0) {
+        predicates = keys.slice(1).reduce(
+          (pred, key) => pred.and().equalTo(key, queryParam[key]),
+          predicates.equalTo(keys[0], queryParam[keys[0]])
+        );
+      }
+      const resultSet = (RelationalStoreUtils.cloudLoadStore as relationalStore.RdbStore).querySync(predicates)
+
+      // 获取列名信息
+      const columnNames = resultSet.columnNames
+
+      while (resultSet.goToNextRow()) {
+        const item: ESObject = {}
+        columnNames.forEach((columnName) => {
+          // 根据列类型获取值
+          const columnIndex = resultSet.getColumnIndex(columnName);
+          // 可以根据需要处理不同数据类型
+          item[columnName] = resultSet.getValue(columnIndex);
+        })
+        list.push(item as T);
+      }
+
+      resultSet.close();
+    }
+    return list;
+  }
+
+
+  /**
+   * 查询单个数据
+   * @param tableName
+   * @param queryParam
+   * @returns
+   */
+  static getItemById<T = ESObject>(tableName: string, queryParam: ESObject) {
+    const keys = Object.keys(queryParam);
+    const item: ESObject = {}
+    let predicates = new relationalStore.RdbPredicates(tableName);
+
+    if (RelationalStoreUtils.cloudLoadStore != undefined) {
+      //高级写法
+      if (keys.length > 0) {
+        predicates = keys.slice(1).reduce(
+          (pred, key) => pred.and().equalTo(key, queryParam[key]),
+          predicates.equalTo(keys[0], queryParam[keys[0]])
+        );
+      }
+      const resultSet = (RelationalStoreUtils.cloudLoadStore as relationalStore.RdbStore).querySync(predicates)
+      // 获取列名信息
+      const columnNames = resultSet.columnNames
+      // resultSet是一个数据集合的游标,默认指向第-1个记录,有效的数据从0开始。
+      while (resultSet.goToNextRow()) {
+        columnNames.forEach((columnName) => {
+          // 根据列类型获取值
+          const columnIndex = resultSet.getColumnIndex(columnName);
+          // 可以根据需要处理不同数据类型
+          item[columnName] = resultSet.getValue(columnIndex);
+        })
+        break
+      }
+      // 释放数据集的内存,若不释放可能会引起fd泄露与内存泄露
+      resultSet.close();
+    }
+    return item as T;
+  }
+
+  /**
+   * 根据id删除数据
+   * @param tableName
+   * @param queryParam
+   * @param success
+   */
+  static deleteItemById(tableName: string, id: number, success?: () => void) {
+
+    let predicates = new relationalStore.RdbPredicates(tableName);
+    predicates = predicates.equalTo("id", id)
+
+    if (RelationalStoreUtils.cloudLoadStore !== undefined) {
+      const flag = (RelationalStoreUtils.cloudLoadStore as relationalStore.RdbStore).deleteSync(predicates)
+      if (flag != 0) {
+        return true
+      }
+    }
+    return false
+  }
+}

+ 45 - 0
features/feature/src/main/ets/view/ThirdView.ets

@@ -1,6 +1,51 @@
+import { RelationalStoreUtils, Test } from "basic";
+
 @Component
 export struct ThirdView {
+  @State list: Test[] = []
   build() {
+    Column() {
+      Row() {
+        Button('测试新增')
+          .onClick(() => {
+            RelationalStoreUtils.insert('Test', { name: 'test1', age: 18 }, () => {
+              console.log('insert success');
+              let list = RelationalStoreUtils.getList<Test>('Test')
+              this.list = list
+              console.log('list:', JSON.stringify(list))
+            })
+          })
+
+        Button('测试修改')
+          .onClick(() => {
+            RelationalStoreUtils.updateItemById('Test', { id: 1, name: '测试1修改', age: 20 }, () => {
+              console.log('insert success');
+              let list = RelationalStoreUtils.getList<Test>('Test')
+              this.list = list
+              console.log('list:', JSON.stringify(list))
+            })
+          })
+        Button('测试删除')
+          .onClick(() => {
+            RelationalStoreUtils.deleteItemById('Test', 1)
+            console.log('insert success');
+            let list = RelationalStoreUtils.getList<Test>('Test')
+            this.list = list
+            console.log('list:', JSON.stringify(list))
+          })
+      }
+      .width('100%')
+      .justifyContent(FlexAlign.SpaceAround)
+      .margin({ bottom: 30 })
+
+      Text(JSON.stringify(this.list))
+        .font({ size: 20 })
+        .margin({ bottom: 20 })
 
+    }
+    .justifyContent(FlexAlign.Center)
+    .alignItems(HorizontalAlign.Center)
+    .height('100%')
+    .width('100%')
   }
 }

+ 231 - 1
hvigorfile.ts

@@ -199,9 +199,239 @@ function customPlugin(): HvigorPlugin {
     }
 }
 
+function rDBPlugin(): HvigorPlugin {
+    return {
+        pluginId: 'customPlugin',
+        apply(node: HvigorNode) {
+            try {
+                // 根据node对象结构获取项目路径
+                const projectPath = node.nodeDir.filePath;
+                console.log('目标路径:', projectPath);
+
+                // 获取所有子模块
+                const modules = node.allSubNodes.filter(subNode => subNode.node.classKind === 'module');
+                console.log('模块数量:', modules.length);
+
+                // 打印每个模块的路径
+                const modulePaths = [];
+                modules.forEach((module, index) => {
+                    try {
+                        // 从module.node._nodePath获取模块路径
+                        const modulePath = module.node._nodePath;
+                        if (modulePath) {
+                            console.log(`模块 ${index + 1} 路径:`, modulePath);
+                            modulePaths.push(modulePath);
+                            if (modulePath.includes('basic')) {
+                                processTables(modulePath)
+                            }
+                        } else {
+                            console.log(`模块 ${index + 1} 路径: 无法获取路径`);
+                        }
+                    } catch (error) {
+                        console.error(`获取模块 ${index + 1} 路径时出错:`, error);
+                    }
+                });
+
+                // 遍历并处理所有模块
+                // modulePaths.forEach((modulePath, index) => {
+                //     console.log(`处理模块 ${index + 1}:`, modulePath);
+                //     processTables(); // 处理tables目录的逻辑
+                // });
+                // processTables(modulePaths[])
+
+            } catch (error) {
+                console.error('Error in custom plugin:', error);
+            }
+        }
+    }
+
+    function processTables(currentDir:string) {
+        try {
+            // 构建tables目录路径
+            const tablesPath = path.join(currentDir, 'src', 'main', 'ets', 'rdb', 'tables');
+            console.log('tables路径:', tablesPath);
+
+            // 检查tables目录是否存在
+            if (fs.existsSync(tablesPath)) {
+                // 读取目录内容
+                const files = fs.readdirSync(tablesPath);
+                console.log(`Found ${files.length} files/directories in tables folder:`);
+
+                const tableNames: string[] = [];
+                const sqlStatements: string[] = [];
+
+                // 替换原有的字段处理逻辑
+                files.forEach((file, index) => {
+                    const fullPath = path.join(tablesPath, file);
+                    const stat = fs.statSync(fullPath);
+                    const type = stat.isDirectory() ? '[DIR]' : '[FILE]';
+                    console.log(`${index + 1}. ${type} ${file}`);
+
+                    // 如果是文件,读取文件内容
+                    if (!stat.isDirectory() && file.endsWith('.ets')) {
+                        try {
+                            const content = fs.readFileSync(fullPath, 'utf8');
+
+                            // 使用正则表达式匹配interface名称
+                            const interfaceRegex = /export\s+interface\s+(\w+)/g;
+                            let match;
+
+                            while ((match = interfaceRegex.exec(content)) !== null) {
+                                const interfaceName = match[1];
+                                tableNames.push(interfaceName);
+
+                                // 解析接口属性并生成字段
+                                const fieldDefinitions = parseInterfaceFields(content, interfaceName);
+
+                                // 生成CREATE TABLE SQL语句(包含字段)
+                                const sqlStatement =
+                                    `static readonly CREATE_${interfaceName.toUpperCase()}_TABLE = 'CREATE TABLE IF NOT EXISTS ${interfaceName} (${fieldDefinitions})';`;
+                                sqlStatements.push(sqlStatement);
+
+                                console.log(`Found interface: ${interfaceName}`);
+                            }
+                        } catch (readError) {
+                            console.error(`Error reading file ${file}:`, readError);
+                        }
+                    }
+                });
+
+                // 添加解析接口字段的辅助函数
+                function parseInterfaceFields(content: string, interfaceName: string): string {
+                    // 查找接口定义区域
+                    const interfaceStart = content.indexOf(`export interface ${interfaceName}`);
+                    if (interfaceStart === -1) {
+                        return 'ID INTEGER PRIMARY KEY AUTOINCREMENT';
+                    }
+
+                    const openBrace = content.indexOf('{', interfaceStart);
+                    const closeBrace = content.indexOf('}', openBrace);
+
+                    if (openBrace === -1 || closeBrace === -1) {
+                        return 'ID INTEGER PRIMARY KEY AUTOINCREMENT';
+                    }
+
+                    const interfaceBody = content.substring(openBrace + 1, closeBrace);
+                    const lines = interfaceBody.split('\n');
+
+                    const fields: string[] = [];
+                    let hasPrimaryKey = false;
+
+                    lines.forEach(line => {
+                        const fieldMatch = line.trim().match(/^(\w+)\??\s*:\s*(.+?);?$/);
+                        if (fieldMatch) {
+                            const fieldName = fieldMatch[1];
+                            const fieldType = fieldMatch[2].trim();
+
+                            // 根据TypeScript类型映射到SQLite类型
+                            let sqliteType = 'TEXT';
+                            if (fieldType.includes('number') || fieldType.includes('Number')) {
+                                sqliteType = 'INTEGER';
+                            } else if (fieldType.includes('boolean') || fieldType.includes('Boolean')) {
+                                sqliteType = 'INTEGER';
+                            } else if (fieldType.includes('Date')) {
+                                sqliteType = 'TEXT';
+                            }
+
+                            // 检查是否为主键字段(约定以id命名的字段作为主键)
+                            if (fieldName.toLowerCase() === 'id') {
+                                fields.push(`${fieldName} INTEGER PRIMARY KEY AUTOINCREMENT`);
+                                hasPrimaryKey = true;
+                            } else {
+                                // 处理可选字段
+                                const nullable = line.trim().includes('?') ? '' : ' NOT NULL';
+                                fields.push(`${fieldName} ${sqliteType}${nullable}`);
+                            }
+                        }
+                    });
+
+                    // 如果没有定义主键,则添加默认主键
+                    if (!hasPrimaryKey) {
+                        fields.unshift('ID INTEGER PRIMARY KEY AUTOINCREMENT');
+                    }
+
+                    return fields.join(', ');
+                }
+
+
+                // 更新RelationalStoreUtils.ets文件中的tables数组
+                const relationalStorePath =
+                    path.join(currentDir, 'src', 'main', 'ets', 'rdb', 'utils', 'RelationalStoreUtis.ets');
+                if (fs.existsSync(relationalStorePath)) {
+                    let content = fs.readFileSync(relationalStorePath, 'utf8');
+
+                    // 更新tables数组
+                    if (tableNames.length > 0) {
+                        const tablesArray = `private static tables: string[] = [${tableNames.map(name => `'${name}'`).join(', ')}]`;
+                        const updatedContent = content.replace(/private static tables: string\[\] = \[[^\]]*\]/, tablesArray);
+
+                        fs.writeFileSync(relationalStorePath, updatedContent, 'utf8');
+                        console.log('RelationalStoreUtis.ets文件已更新');
+                    }
+                }
+
+                // 替换原有的更新RDBSql.ets文件的代码块
+
+                // 更新RDBSql.ts文件
+                const rdbSqlPath = path.join(currentDir, 'src', 'main', 'ets', 'rdb', 'sqls', 'RDBSql.ts');
+                if (fs.existsSync(rdbSqlPath)) {
+                    let content = fs.readFileSync(rdbSqlPath, 'utf8');
+
+                    // 构建SQL语句对象属性
+                    if (sqlStatements.length > 0) {
+                        // 提取属性名和值
+                        const sqlProperties: string[] = [];
+                        sqlStatements.forEach(statement => {
+                            // 从 static readonly CREATE_USER_TABLE = 'CREATE TABLE...' 提取属性
+                            const propertyName = statement.split('=')[0].trim().replace('static readonly ', '');
+                            const propertyValue = statement.split('=')[1].trim().slice(1, -2); // 去掉引号和分号
+                            sqlProperties.push(`  ${propertyName}: '${propertyValue}'`);
+                        });
+
+                        // 构建新的对象内容
+                        const newContent = `export const rDbSql = {\n${sqlProperties.join(',\n')}\n}`;
+
+                        fs.writeFileSync(rdbSqlPath, newContent, 'utf8');
+                        console.log('RDBSql.ts文件已更新为对象形式');
+                    }
+                } else {
+                    // 如果文件不存在,创建新文件
+                    if (sqlStatements.length > 0) {
+                        const sqlProperties: string[] = [];
+                        sqlStatements.forEach(statement => {
+                            const propertyName = statement.split('=')[0].trim().replace('static readonly ', '');
+                            const propertyValue = statement.split('=')[1].trim().slice(1, -2);
+                            sqlProperties.push(`  ${propertyName}: '${propertyValue}'`);
+                        });
+
+                        const newContent = `export const rDbSql = {\n${sqlProperties.join(',\n')}\n}`;
+
+                        // 确保目录存在
+                        const dirPath = path.dirname(rdbSqlPath);
+                        if (!fs.existsSync(dirPath)) {
+                            fs.mkdirSync(dirPath, { recursive: true });
+                        }
+
+                        fs.writeFileSync(rdbSqlPath, newContent, 'utf8');
+                        console.log('RDBSql.ts文件已创建并初始化为对象形式');
+                    }
+                }
+
+
+                console.log('----------------------------------------');
+            } else {
+                console.log('Tables directory not found');
+            }
+        } catch (error) {
+            console.error(`处理tables目录时出错:`, error);
+        }
+    }
+}
+
 export default {
     system: appTasks,  /* Built-in plugin of Hvigor. It cannot be modified. */
     plugins: [
-        customPlugin()  // 应用自定义Plugin
+        customPlugin(),  // 应用自定义Plugin
+        rDBPlugin()
     ]         /* Custom plugin to extend the functionality of Hvigor. */
 }

+ 2 - 1
products/entry/src/main/ets/entryability/EntryAbility.ets

@@ -2,7 +2,7 @@ import { AbilityConstant, bundleManager, ConfigurationConstant, UIAbility, Want
 import { hilog } from '@kit.PerformanceAnalysisKit';
 import { window } from '@kit.ArkUI';
 import {
-  AppStorageKeyCollect, IBestInit, YTAvoid, YTBreakPoint, YTLog
+  AppStorageKeyCollect, IBestInit, RelationalStoreUtils, YTAvoid, YTBreakPoint, YTLog
 } from 'basic';
 
 const DOMAIN = 0x0000;
@@ -66,6 +66,7 @@ export default class EntryAbility extends UIAbility {
         AppStorage.setOrCreate(AppStorageKeyCollect.SCREEN_WIDTH, px2vp(size.width))
         AppStorage.setOrCreate(AppStorageKeyCollect.SCREEN_HEIGHT, px2vp(size.height))
       })
+      RelationalStoreUtils.initReplayCloudStore(this.context)
     });
 
   }