uniapp 本地数据库多端适配实例(根据运行环境自动选择适配器)
项目有个需求,需要生成app和小程序,app支持离线数据库,如果当前没有网络提醒用户开启离线模式,所以就随便搞了下,具体的思路就是: 一个接口和多个实现类(类似后端的模板设计模式),例如sqlite实现类,indexedDB实现类等等,根据环境动态选用具体的实现类
indexedDB有些方法没有改,sqlite没有测试,此处就是提供一个思路,只是用来学习和研究着玩了
目录

class类
BaseApi
import type LocalDB from '@/class/DbClass'
import { tableConfig } from '@/enums/DbEnums'
import { CallBackFunType } from '@/utils/request'
class BaseApi {public localDB: LocalDB;public table: string;public hasTable: boolean;constructor(table: string, localDB: LocalDB){this.table = tablethis.hasTable = falsethis.localDB = localDBif(this.localDB.Db().isOpen()){this.localDB.Db().hasTable(table).then((res:any) => {this.hasTable = res}).catch((res: any) => {console.log("构造初始化失败, table: " + this.table)})}}public getTable(): string{return this.table;}public getHasTable(): boolean{return this.hasTable;}public setHasTable(hasTable:boolean){this.hasTable = hasTable;}/*** 验证是否存在表信息*/public async verifyTable(init: boolean = false): Promise<CallBackFunType<any>>{if(!this.getHasTable() && init){const res = await this.init();if(!res){return new CallBackFunType({}).fail('初始化数据库失败')}else {return new CallBackFunType({}).success('初始化数据库成功');}}return new CallBackFunType({}).success('存在数据库');}/*** 初始化*/public async init(): Promise<boolean>{// 浏览器不能直接新建,需要适配const res = await this.localDB.Db().createTable(this.table, tableConfig[this.table])return res;}
}export default BaseApi;
DbClass
import type SqlAbapter from '@/plugins/db/SqlAbapter'// 根据平台选择适配器
class LocalDB {private db : SqlAbapter;public name : string;public isSupport: boolean;constructor(db:SqlAbapter , name: string, isSupport: boolean){this.db = db;this.name = namethis.isSupport = isSupport}/*** 批量初始化表*/public initDb(tables: Record<string, Record<string, any>>): void{// 验证当前是否开启了离线缓存,验证当前是否支持离线缓存if(!this.isSupport){console.log("当前不支持离线缓存")return;}this.db.open(tables).then((res: boolean) => {if(res){console.log('数据库打开成功')}else {console.log('数据库打开失败')}})}// 验证是否public Db():SqlAbapter{// 验证当前是否开启了离线缓存,验证当前是否支持离线缓存if(!this.isSupport){console.log("当前不支持离线缓存")}return this.db;}
}export default LocalDB;
方法抽象
抽象模版 SqlAbapter.ts
interface SqlAbapter {/*** 数据库是否开启*/isOpen: () => boolean;/*** 开启数据库*/open: (tables: Record<string, Record<string, any>>) => Promise<boolean>;/*** 关闭数据库*/close: () => Promise<boolean>;/*** 是否存在表*/hasTable: (dbTable: string) => Promise<boolean>;/*** 创建表*/createTable: (dbTable: string, data: string[]) => Promise<boolean>;/*** 删除表*/dropTable: (dbTable: string) => Promise<boolean>;/*** 新增数据: indexedDb 重读id会报错*/insertTableData: (dbTable: string, data:Record<string,any>) => Promise<boolean>;/*** 新增或修改*/insertOrReplaceData: (dbTable:string, data:Record<string,any>, condition:Record<string,any>) => Promise<boolean>;/*** 查询数据*/selectTableData: (dbTable:string, condition: Record<string,any>) => Promise<any>;/*** 删除数据*/deleteTableData: (dbTable:string, condition: Record<string,any>) => Promise<boolean>;/*** 更新数据*/updateTableData: (dbTable:string, data: Record<string,any>, condition: Record<string,any>) => Promise<Boolean>;/*** 分页查询*/pullSQL: (dbTable: string, id:string, current: number, size:number) => Promise<any>;}export default SqlAbapter;
sqlite实现
import type SqlAbapter from '@/plugins/db/SqlAbapter'
import { replace, keyValSql, isEmpty, whereSql, updateSetSql } from '@/plugins/utils';
class SqliteAdapter implements SqlAbapter {public dbName: string;public dbPath: string;constructor(dbName: string, dbPath:string){this.dbName = dbName;this.dbPath = dbPath;}// 判断数据库是否打开isOpen() {// 数据库打开了就返回 true,否则返回 falsevar open = plus.sqlite.isOpenDatabase({name: this.dbName, // 数据库名称path: this.dbPath // 数据库地址})return open;}// 创建数据库 或 有该数据库就打开open(tables: Record<string, Record<string, any>>):Promise<boolean> {return new Promise((resolve, reject) => {// 打开数据库plus.sqlite.openDatabase({name: this.dbName,path: this.dbPath,success(e) {resolve(true); // 成功回调// 初始化表 todo: },fail(e) {reject(false); // 失败回调}})})}hasTable(dbTable: string): Promise<boolean> {let sql = `select * from sqlite_master where type = 'table' and name = '${dbTable}'`return new Promise((resolve, reject) => {// 打开数据库plus.sqlite.executeSql({name: this.dbName,sql: [sql],success(res) {if (res.resultSet.length > 0) {resolve(true); // 成功回调} else {resolve(false); // 失败回调}},fail(e) {reject(false); // 失败回调}})})}// 关闭数据库close(): Promise<boolean> {return new Promise((resolve, reject) => {plus.sqlite.closeDatabase({name: this.dbName,success(e) {resolve(e);},fail(e) {reject(e);}})})}// 数据库建表 sql:'CREATE TABLE IF NOT EXISTS dbTable("id" varchar(50),"name" TEXT) // 创建 CREATE TABLE IF NOT EXISTS 、 dbTable 是表名,不能用数字开头、括号里是表格的表头createTable(dbTable: string, data: string[]): Promise<boolean> {let keys = '';if(!data || data.length <= 0){return new Promise((resolve, reject) => { reject("创建失败,索引不能为空") })}data.forEach((key:string) => {keys = keys + key +",";})keys = replace(keys, ",")// todo: 增加表 属性, varchar等等let sql = `CREATE TABLE IF NOT EXISTS ${dbTable}(${keys})`return new Promise((resolve, reject) => {// executeSql: 执行增删改等操作的SQL语句plus.sqlite.executeSql({name: this.dbName,sql: [sql],success(e) {resolve(true);},fail(e) {reject(false);}})})}// 数据库删表 sql:'DROP TABLE dbTable'dropTable(dbTable: string): Promise<boolean> {return new Promise((resolve, reject) => {plus.sqlite.executeSql({name: this.dbName,sql: [`DROP TABLE ${dbTable}`],success(e) {resolve(true);},fail(e) {reject(false);}})})}// 向表格里添加数据 sql:'INSERT INTO dbTable VALUES('x','x','x')' 对应新增// 或者 sql:'INSERT INTO dbTable ('x','x','x') VALUES('x','x','x')' 具体新增// 插入 INSERT INTO 、 dbTable 是表名、根据表头列名插入列值insertTableData(dbTable: string, data:Record<string,any>): Promise<boolean> {// 判断有没有传参if (dbTable !== undefined && data) {// 判断传的参是否有值if (!isEmpty(data)) {let res = keyValSql(data)// 拼接sql,执行插入var sql = `INSERT INTO ${dbTable} (${res.keySql}) VALUES(${res.valSql})`;// console.log(sql);return new Promise((resolve, reject) => {// 表格添加数据plus.sqlite.executeSql({name: this.dbName,sql: [sql],success(e) {resolve(e);},fail(e) {reject(e);}})})} else {return new Promise((resolve, reject) => { reject("错误添加") })}} else {return new Promise((resolve, reject) => { reject("错误添加") })}}// 根据条件向表格里添加数据 有数据更新、无数据插入// (建表时需要设置主键) 例如 --- "roomid" varchar(50) PRIMARY KEYinsertOrReplaceData(dbTable: string, data: Record<string,any>):Promise<boolean> {// 判断有没有传参if (dbTable !== undefined && data) {if (!isEmpty(data)) {let res = keyValSql(data)let sql = `INSERT OR REPLACE INTO ${dbTable} (${res.keySql}) VALUES(${res.valSql})`;// console.log(sql);return new Promise((resolve, reject) => {// 表格添加数据plus.sqlite.executeSql({name: this.dbName,sql: [sql],success(e) {resolve(e);},fail(e) {reject(e);}})})}else {return new Promise((resolve, reject) => { reject("错误添加") })}} else {return new Promise((resolve, reject) => { reject("错误添加") })}}// 查询获取数据库里的数据 sql:'SELECT * FROM dbTable WHERE lname = 'lvalue''// 查询 SELECT * FROM 、 dbTable 是表名、 WHERE 查找条件 lname,lvalue 是查询条件的列名和列值selectTableData(dbTable:string, condition: Record<string,any>): Promise<any> {if (dbTable !== undefined) {// 第一个是表单名称,后两个参数是列表名,用来检索let where = ''if(!isEmpty(condition)){where = whereSql(condition)}// if (lname !== undefined && cc !== undefined) {// // 两个检索条件// var sql = `SELECT * FROM ${dbTable} WHERE ${lname} = '${lvalue}' AND ${cc} = '${dd}'`;// }let sql = `SELECT * FROM ${dbTable}`;if(where){sql = sql + " where " + where;}return new Promise((resolve, reject) => {// 表格查询数据 执行查询的SQL语句plus.sqlite.selectSql({name: this.dbName,sql: sql,success(e) {resolve(e);},fail(e) {reject(e);}})})} else {return new Promise((resolve, reject) => { reject("错误查询") });}}// 删除表里的数据 sql:'DELETE FROM dbTable WHERE lname = 'lvalue''// 删除 DELETE FROM 、 dbTable 是表名、 WHERE 查找条件 lname,lvalue 是查询条件的列名和列值deleteTableData(dbTable:string, condition: Record<string,any>): Promise<boolean> {if (dbTable !== undefined) {let where = ''if(!isEmpty(condition)){where = whereSql(condition)}var sql = `DELETE FROM ${dbTable}`;if(where){sql = sql + " where " + where}return new Promise((resolve, reject) => {// 删除表数据plus.sqlite.executeSql({name: this.dbName,sql: [sql],success(e) {resolve(e);},fail(e) {reject(e);}})})} else {return new Promise((resolve, reject) => { reject("错误删除") });}}// 修改数据表里的数据 sql:"UPDATE dbTable SET 列名 = '列值',列名 = '列值' WHERE lname = 'lvalue'"// 修改 UPDATE 、 dbTable 是表名, data: 要修改的列名=修改后列值, lname,lvalue 是查询条件的列名和列值updateTableData(dbTable:string, data: Record<string,any>, condition: Record<string,any>): Promise<boolean> {if(!dbTable || isEmpty(data)){return new Promise((resolve, reject) => { reject("修改错误") });}let res = updateSetSql(data)var sql = `UPDATE ${dbTable} SET ` + res;if(!isEmpty(condition)){let where = whereSql(condition)if(where){sql = sql + " where " + where}}// WHERE 前面是要修改的列名、列值,后面是条件的列名、列值return new Promise((resolve, reject) => {// 修改表数据plus.sqlite.executeSql({name: this.dbName,sql: [sql],success(e) {resolve(e);},fail(e) {reject(e);}})})}// 获取指定数据条数 sql:"SELECT * FROM dbTable ORDER BY 'id' DESC LIMIT 15 OFFSET 'num'"// dbTable 表名, ORDER BY 代表排序默认正序, id 是排序的条件 DESC 代表倒序,从最后一条数据开始拿// LIMIT 15 OFFSET '${num}',这句的意思是跳过 num 条拿 15 条数据, num 为跳过多少条数据是动态值// 例 初始num设为0,就从最后的数据开始拿15条,下次不拿刚获取的数据,所以可以让num为15,这样就能一步一步的拿完所有的数据pullSQL(dbTable: string, id:string, current: number, size:number): Promise<any> {if(current <= 0){return new Promise((resolve, reject) => { reject("分页查询错误,页码必须从1开始") });}let num = 0;if(current > 0){num = (current - 1) * size}return new Promise((resolve, reject) => {plus.sqlite.selectSql({name: this.dbName,sql: `SELECT * FROM ${dbTable} ORDER BY '${id}' DESC LIMIT ${size} OFFSET '${num}'`,success(e) {resolve(e);},fail(e) {reject(e);}})})}
}export default SqliteAdapter;
indexedDb 实现
import type SqlAbapter from '@/plugins/db/SqlAbapter'
import { keyValSql, isEmpty, whereSql, updateSetSql } from '@/plugins/utils';class IndexDbAdapter implements SqlAbapter {// 这个做法是因为 不同的浏览器获取indexedDB的方式不一样。// mozIndexedDB:火狐浏览器内核;webkitIndexedDB:webkit内核;msIndexedDB:IE内核。public indexedDB = window.indexedDB || window.mozIndexedDB || window.webkitIndexedDB || window.msIndexedDB;public dbName: string;public dbPath: string;private request: IDBOpenDBRequest | undefined;private db: IDBDatabase | undefined;constructor(dbName: string, dbPath:string){this.dbName = dbName;this.dbPath = dbPath;// this.request = this.indexedDB.open(this.dbName,1)}// 判断数据库是否打开isOpen() {// 数据库打开了就返回 true,否则返回 falseif(this.request == null){return false;}else {return true;}}// 创建数据库 或 有该数据库就打开open(tables: Record<string, Record<string, any>>):Promise<boolean> {return new Promise((resolve, reject) => {this.request = this.indexedDB.open(this.dbName,1)let _this = this;this.request.onerror = function(event) {console.error('Database error:', event.target?.errorCode);reject("数据库打开失败");};this.request.onupgradeneeded = function(event) {console.log('onupgradeneeded ====>', event)resolve(true);_this.db = (event.target as IDBOpenDBRequest).result;// _this.createTable('ttt', ['name', 'val'])if(!isEmpty(tables)){let tabs = Object.keys(tables)tabs.forEach((tab:string) => {_this.hasTable(tab).then((res: boolean) => {if(res){// 当前console.log(`当前数据库: ${tab} 已存在,不进行初始化`)}else {// 初始化表_this.createTable(tab, Object.keys(tables[tab])).then((creRes:any) => {if(creRes){console.log(`当前数据库: ${tab} 初始化完成`)}else {console.log(`当前数据库: ${tab} 初始化失败`)}})}})})}};this.request.onsuccess = function(event) {_this.db = (event.target as IDBOpenDBRequest).result;resolve(true);console.log('Database opened successfully');};})}hasTable(dbTable: string): Promise<boolean> {let _this = this;return new Promise((resolve, reject) => {let res = _this.db?.objectStoreNames.contains(dbTable) as booleanconsole.log('hasTable this.db? =====>', this.db)console.log('hasTable =====>', res)resolve(res);})}// 关闭数据库close(): Promise<boolean> {let _this = this;return new Promise((resolve, reject) => {_this.db?.close();resolve(true);})}// 数据库建表 sql:'CREATE TABLE IF NOT EXISTS dbTable("id" varchar(50),"name" TEXT) // 创建 CREATE TABLE IF NOT EXISTS 、 dbTable 是表名,不能用数字开头、括号里是表格的表头createTable(dbTable: string, data: string[]): Promise<boolean> {if(!data || data.length <= 0){return new Promise((resolve, reject) => { reject("创建失败,索引不能为空") })}let _this = this;return new Promise((resolve, reject) => {// executeSql: 创建表const objectStore = _this.db?.createObjectStore(dbTable,{ keyPath: 'id' });data.forEach((key:any) => {objectStore?.createIndex(key, key, {unique: false})})resolve(true)})}// 数据库删表 sql:'DROP TABLE dbTable'dropTable(dbTable: string): Promise<boolean> {return new Promise((resolve, reject) => {reject(false);})}// 向表格里添加数据 sql:'INSERT INTO dbTable VALUES('x','x','x')' 对应新增// 或者 sql:'INSERT INTO dbTable ('x','x','x') VALUES('x','x','x')' 具体新增// 插入 INSERT INTO 、 dbTable 是表名、根据表头列名插入列值insertTableData(dbTable: string, data:Record<string,any>): Promise<boolean> {// 判断有没有传参if (dbTable !== undefined && data) {// 判断传的参是否有值if (!isEmpty(data)) {let _this = this;return new Promise((resolve, reject) => {// 添加数据到对象存储空间let transaction = _this.db?.transaction([dbTable], 'readwrite');const objectStore = transaction?.objectStore(dbTable);let request = objectStore?.add(data) as IDBRequest;// 写入数据的事件监听request.onsuccess = function (event) {resolve(true)console.log('数据写入成功');};request.onerror = function (event) {reject("数据写入失败: " + event?.target?.error?.message)console.log('数据写入失败: event =====》 ', event);}})} else {return new Promise((resolve, reject) => { reject("错误添加") })}} else {return new Promise((resolve, reject) => { reject("错误添加") })}}// 根据条件向表格里添加数据 有数据更新、无数据插入// (建表时需要设置主键) 例如 --- "roomid" varchar(50) PRIMARY KEYinsertOrReplaceData(dbTable: string, data: Record<string,any>):Promise<boolean> {// 判断有没有传参if (dbTable !== undefined && data) {if (!isEmpty(data)) {return new Promise((resolve, reject) => {// 表格添加数据reject(false);})}else {return new Promise((resolve, reject) => { reject("错误添加") })}} else {return new Promise((resolve, reject) => { reject("错误添加") })}}// 查询获取数据库里的数据 sql:'SELECT * FROM dbTable WHERE lname = 'lvalue''// 查询 SELECT * FROM 、 dbTable 是表名、 WHERE 查找条件 lname,lvalue 是查询条件的列名和列值selectTableData(dbTable:string, condition: Record<string,any>): Promise<any> {if (dbTable !== undefined) {// 第一个是表单名称,后两个参数是列表名,用来检索return new Promise((resolve, reject) => {reject(false);})} else {return new Promise((resolve, reject) => { reject("错误查询") });}}// 删除表里的数据 sql:'DELETE FROM dbTable WHERE lname = 'lvalue''// 删除 DELETE FROM 、 dbTable 是表名、 WHERE 查找条件 lname,lvalue 是查询条件的列名和列值deleteTableData(dbTable:string, condition: Record<string,any>): Promise<boolean> {if (dbTable !== undefined) {return new Promise((resolve, reject) => {reject(false);})} else {return new Promise((resolve, reject) => { reject("错误删除") });}}// 修改数据表里的数据 sql:"UPDATE dbTable SET 列名 = '列值',列名 = '列值' WHERE lname = 'lvalue'"// 修改 UPDATE 、 dbTable 是表名, data: 要修改的列名=修改后列值, lname,lvalue 是查询条件的列名和列值updateTableData(dbTable:string, data: Record<string,any>, condition: Record<string,any>): Promise<boolean> {return new Promise((resolve, reject) => {reject(false);})}// 获取指定数据条数 sql:"SELECT * FROM dbTable ORDER BY 'id' DESC LIMIT 15 OFFSET 'num'"// dbTable 表名, ORDER BY 代表排序默认正序, id 是排序的条件 DESC 代表倒序,从最后一条数据开始拿// LIMIT 15 OFFSET '${num}',这句的意思是跳过 num 条拿 15 条数据, num 为跳过多少条数据是动态值// 例 初始num设为0,就从最后的数据开始拿15条,下次不拿刚获取的数据,所以可以让num为15,这样就能一步一步的拿完所有的数据pullSQL(dbTable: string, id:string, current: number, size:number): Promise<any> {return new Promise((resolve, reject) => {reject(false);})}
}export default IndexDbAdapter;
动态选取
db-plugins
import SqliteAdapter from './db/sqlite/sqliteAdapter';
import IndexDbAdapter from './db/indexedDb/indexedDbAdapter';
import { DbConfig, tableConfig } from '@/enums/DbEnums'
import LocalDB from '@/class/DbClass'let localDB : LocalDB;
// #ifdef APP-PLUS
// App 环境使用 sqlite 适配器localDB = new LocalDB(new SqliteAdapter(DbConfig.Name, DbConfig.Path), 'sqlite', true);
// #endif
// #ifdef MP-WEIXIN
// 小程序环境使用内存适配器,自定义实现// #endif
// #ifdef H5
// 浏览器 环境使用 indexeddb 适配器localDB = new LocalDB(new IndexDbAdapter(DbConfig.Name, DbConfig.Path), 'indexedDb', true);
// #endifconsole.log('当前环境注册的本地数据库为',localDB.name)// 初始化表
localDB.initDb(tableConfig)
export default localDB;
api应用
testService
import {request , CallBackFunType} from '@/utils/request'
import { HttpPath } from '@/enums/constant'
import localDB from '@/plugins/db-plugins'
import type LocalDB from '@/class/DbClass'
import { DbTable, DbConfig } from '@/enums/DbEnums'
import BaseApi from '@/class/baseApiClass'class TestApi extends BaseApi{constructor(table: string, localDB:LocalDB){super(table, localDB)}public save(data: {id: string ,name: string, value: string}) {return request({'url': HttpPath.App + '/dict/type','method': 'post',data}, async (requestConfig: any) => {console.log('离线回调操作: ', requestConfig)const res = await this.verifyTable(true);return new Promise<CallBackFunType<any>>((resolve, reject) => {if(res.code != 200){reject(res)}else{// 执行数据库操作this.localDB.Db().insertTableData(DbTable.Test, data).then((res) => {if(res){resolve(new CallBackFunType({}).success())}else {reject(new CallBackFunType({}).fail())}}).catch((e:any) => {reject(e)})}})})}
}export default new TestApi(DbTable.Test, localDB);
页面使用
<template><view>测试</view>
</template><script lang="ts" setup>import { onLoad } from '@dcloudio/uni-app';import TestApi from '@/api/testService'onLoad(() => {TestApi.save({id:'1231',name: '2321', value: 'dasdas'}).then((res:any) => {console.log(res)})})
</script><style lang="scss">
</style>
执行效果 以浏览器(indexedDb)为例



相关文章:
uniapp 本地数据库多端适配实例(根据运行环境自动选择适配器)
项目有个需求,需要生成app和小程序,app支持离线数据库,如果当前没有网络提醒用户开启离线模式,所以就随便搞了下,具体的思路就是: 一个接口和多个实现类(类似后端的模板设计模式)&am…...
百度觉醒,李彦宏渴望光荣
文 | 大力财经 作者 | 魏力 2025年刚刚开年,被一家名为DeepSeek的初创公司强势改写。在量化交易出身的创始人梁文锋的带领下,这支团队以不到ChatGPT 6%的训练成本,成功推出了性能可与OpenAI媲美的开源大模型。 此成果一经问世,…...
【算法工程】大模型局限性新发现之解决能连github但无法clone项目的问题
最近,linux服务器遇到一个奇怪的问题,能ping通github,但是无法clone git项目,尝试了各种大模型,都提到代理啥的问题,发现没有一个能解决问题。 后来尝试设置 http.sslVerify 为 false,才解决问题…...
SOME/IP-SD -- 协议英文原文讲解3
前言 SOME/IP协议越来越多的用于汽车电子行业中,关于协议详细完全的中文资料却没有,所以我将结合工作经验并对照英文原版协议做一系列的文章。基本分三大块: 1. SOME/IP协议讲解 2. SOME/IP-SD协议讲解 3. python/C举例调试讲解 5.1.2.4…...
软件测试八股文,软件测试常见面试合集【附答案】
PS:加上参考答案有几十万字,答案就没有全部放上来了,高清打印版本超过400多页,评论区留言直接获取 1、你的测试职业发展是什么? 2、你认为测试人员需要具备哪些素质 3、你为什么能够做测试这一行 4、测试的目的是什么? 5、…...
数据结构秘籍(一)线性数据结构
1.数组 数组(Array)是一种很常见的数据结构。它由相同类型的元素(element)组成,并且是使用一块连续的内存来存储。 我们直接可以利用元素的索引(index)计算出该元素对应的存储地址。 数组的特…...
TFChat:腾讯大模型知识引擎(DeepSeek R1)+飞书机器人实现AI智能助手
效果 TFChat项目地址 https://github.com/fish2018/TFChat 腾讯大模型知识引擎用的是DeepSeek R1,项目为sanic和redis实现,利用httpx异步处理流式响应,同时使用buffer来避免频繁调用飞书接口更新卡片的网络耗时。为了进一步减少网络IO消耗&…...
使用消息队列怎样防止消息重复?
大家好,我是君哥。 使用消息队列时,我们经常会遇到一个可能对业务产生影响的问题,消息重复。在订单、扣款、对账等对幂等有要求的场景,消息重复的问题必须解决。 那怎样应对重复消息呢?今天来聊一聊这个话题。 1.三…...
MySQL安装多版本与版本切换
起因 今天在将一个项目部署到本地,想着是先找到一个功能差不多的开源项目,再在这基础之上进行改动,找到的这个项目使用的MySQL版本是MySQL5.7,应该是比较古早的项目了,但是我现在装的是8.4版本的,所以涉及…...
Docker02 - 深入理解Docker
深入理解Docker 文章目录 深入理解Docker一:Docker镜像原理1:镜像加载原理1.1:unionFS1.2:加载原理 2:分层理解 二:容器数据卷详解1:什么是容器数据卷2:使用数据卷3:具名…...
检查SSH安全配置-sshd服务端未认证连接最大并发量配置
介绍 MaxStartups参数指到SSH守护进程的未经身份验证的最大并发连接数。 逻辑依据 为防止系统因大量待处理的身份验证连接尝试而出现拒绝服务的情况,请使用 MaxStartups 的速率限制功能来保护 sshd 登录的可用性,并防止守护进程不堪重负。 检查方法 …...
HarmonyOS Design 介绍
HarmonyOS Design 介绍 文章目录 HarmonyOS Design 介绍一、HarmonyOS Design 是什么?1. 设计系统(Design System)2. UI 框架的支持3. 设计工具和资源4. 开发指南5. 与其他设计系统的对比总结 二、HarmonyOS Design 特点 | 应用场景1. Harmon…...
C++中的多重继承
在 C 中,多重继承是一种允许一个类同时继承多个基类的特性。这意味着派生类可以继承多个基类的属 性和方法。 多重继承增加了语言的灵活性,但同时也引入了额外的复杂性,特别是当多个基类具有相同 的成员时。 基本概念 在多重继承中ÿ…...
Java基础第14天-坦克大战【1】
Java绘图坐标体系 像素 计算机在屏幕上显示的内容都是由屏幕上的每一个像素组成的。如,计算机显示器的分辨率是800x600,表示计算机屏幕上的每一行由800个点组成,共有600行,整个计算机屏幕共有480000个像素。像素是一个密度单位。…...
Java线程池入门04
1. 提交任务的两种方式 executorsubmit 2. executor executor位于Executor接口中 public interface Executor {void executor(Runnable command); }executor提交的是无返回值的任务 下面是一个具体的例子 package LearnThreadPool; import java.util.concurrent.ExecutorSe…...
【论文笔记-ECCV 2024】AnyControl:使用文本到图像生成的多功能控件创建您的艺术作品
AnyControl:使用文本到图像生成的多功能控件创建您的艺术作品 图1 AnyControl的多控制图像合成。该研究的模型支持多个控制信号的自由组合,并生成与每个输入对齐的和谐结果。输入到模型中的输入控制信号以组合图像显示,以实现更好的可视化。 …...
计算机毕业设计 ——jspssm519Springboot 的幼儿园管理系统
作者:程序媛9688 开发技术:SpringBoot、SSM、Vue、MySQL、JSP、ElementUI、Python、小程序等。 🌟文末获取源码数据库🌟 感兴趣的可以先收藏起来,还有大家在毕设选题(免费咨询指导选题)…...
山东大学软件学院人工智能导论实验之知识库推理
目录 实验目的: 实验代码: 实验内容: 实验结果 实验目的: 输入相应的条件,根据知识库推理得出相应的知识。 实验代码: def find_data(input_process_data_list):for epoch, data_process in enumerat…...
【Uniapp-Vue3】点击将内容复制到剪切板
具体使用方法在官网: uni-app官网https://uniapp.dcloud.net.cn/api/system/clipboard.html大致使用方法如下: // value是需要复制的值 function copyValue (value) { uni.setClipboardData({data: value,success: res>{// 复制成功逻辑},fail:err&…...
英伟达 Isaac Sim仿真平台体验【2】
一、产品基础信息 仿真平台:NVIDIA Isaac Sim 4.1.0硬件配置:NVIDIA RTX 4090 2 (24GB显存)核心特性: Omniverse内核的多GPU物理加速原生PyTorch/TensorFlow集成支持基于USD的场景构建体系 二、GPU加速仿真实战 ▶ 多球体跌落测试 操作步…...
KubeSphere 容器平台高可用:环境搭建与可视化操作指南
Linux_k8s篇 欢迎来到Linux的世界,看笔记好好学多敲多打,每个人都是大神! 题目:KubeSphere 容器平台高可用:环境搭建与可视化操作指南 版本号: 1.0,0 作者: 老王要学习 日期: 2025.06.05 适用环境: Ubuntu22 文档说…...
CVPR 2025 MIMO: 支持视觉指代和像素grounding 的医学视觉语言模型
CVPR 2025 | MIMO:支持视觉指代和像素对齐的医学视觉语言模型 论文信息 标题:MIMO: A medical vision language model with visual referring multimodal input and pixel grounding multimodal output作者:Yanyuan Chen, Dexuan Xu, Yu Hu…...
CMake基础:构建流程详解
目录 1.CMake构建过程的基本流程 2.CMake构建的具体步骤 2.1.创建构建目录 2.2.使用 CMake 生成构建文件 2.3.编译和构建 2.4.清理构建文件 2.5.重新配置和构建 3.跨平台构建示例 4.工具链与交叉编译 5.CMake构建后的项目结构解析 5.1.CMake构建后的目录结构 5.2.构…...
pam_env.so模块配置解析
在PAM(Pluggable Authentication Modules)配置中, /etc/pam.d/su 文件相关配置含义如下: 配置解析 auth required pam_env.so1. 字段分解 字段值说明模块类型auth认证类模块,负责验证用户身份&am…...
【大模型RAG】Docker 一键部署 Milvus 完整攻略
本文概要 Milvus 2.5 Stand-alone 版可通过 Docker 在几分钟内完成安装;只需暴露 19530(gRPC)与 9091(HTTP/WebUI)两个端口,即可让本地电脑通过 PyMilvus 或浏览器访问远程 Linux 服务器上的 Milvus。下面…...
sqlserver 根据指定字符 解析拼接字符串
DECLARE LotNo NVARCHAR(50)A,B,C DECLARE xml XML ( SELECT <x> REPLACE(LotNo, ,, </x><x>) </x> ) DECLARE ErrorCode NVARCHAR(50) -- 提取 XML 中的值 SELECT value x.value(., VARCHAR(MAX))…...
基于Docker Compose部署Java微服务项目
一. 创建根项目 根项目(父项目)主要用于依赖管理 一些需要注意的点: 打包方式需要为 pom<modules>里需要注册子模块不要引入maven的打包插件,否则打包时会出问题 <?xml version"1.0" encoding"UTF-8…...
MacOS下Homebrew国内镜像加速指南(2025最新国内镜像加速)
macos brew国内镜像加速方法 brew install 加速formula.jws.json下载慢加速 🍺 最新版brew安装慢到怀疑人生?别怕,教你轻松起飞! 最近Homebrew更新至最新版,每次执行 brew 命令时都会自动从官方地址 https://formulae.…...
【学习笔记】erase 删除顺序迭代器后迭代器失效的解决方案
目录 使用 erase 返回值继续迭代使用索引进行遍历 我们知道类似 vector 的顺序迭代器被删除后,迭代器会失效,因为顺序迭代器在内存中是连续存储的,元素删除后,后续元素会前移。 但一些场景中,我们又需要在执行删除操作…...
tomcat指定使用的jdk版本
说明 有时候需要对tomcat配置指定的jdk版本号,此时,我们可以通过以下方式进行配置 设置方式 找到tomcat的bin目录中的setclasspath.bat。如果是linux系统则是setclasspath.sh set JAVA_HOMEC:\Program Files\Java\jdk8 set JRE_HOMEC:\Program Files…...
