最近使用sqlite进行了聊天记录存取,故来写一篇记录一下·及相关坑的避免!!!
挺早了解过sqlite 这个东西,这从尝试算是真正接触了。
故事开头是这么回事,项目需求是使用腾讯IM做一个咨询服务,就涉及到了消息的一个持久化处理,腾讯那边值存储7天,然后提供接口让我们的后台可以去拉去消息记录,但是拉取回来的消息记录字段差距太大了,压根没办法直接使用,后来做了一个和后端联动,利用后台拉去的记录进行历史记录拉取,后来效果任然是差强人意。于是决定使用这个东西来存一下聊天记录。
由于暂时需求上只是利用她来存储聊记录,所以并未对相关api做过多的封装,如果后期需求上去了再去对他进行封装吧。
废话不多说进入主题:
第一步:在App模块配置中勾选sqlite,然后制作自定义基座
第二步(非必要):考虑到项目的整体整洁对相关基础接口进行Promise封装,主要是为了方便后期的一个链式调用回调的逻辑处理
我这边是再utils目录下建立一个sqlite的全局mixin js文件,到时候混入到全局,方便使用,省去import的麻烦。
下面是伪代码:(我这边存在不同用户使用同一手机登陆的情况,所以动态匹配了数据库存储路径,达到账号切换信息不丢失,当然有更好的解决方案我暂时还没想好,之前页考虑过使用sql给每个字段添加用户ID进行区分,但是这个腾讯那边的字段和关键之似乎是不支持这么干的所以最终觉得,以多库存储的形式来解决。)
export default {
data(){
return {
options:{
name:'Appname',
path:`_doc/appNAme${uni.getStorageSync('userInfo').phoneNumber}.db`,
success:(e)=>{
this.openDataBaseSuccess(e)
},
fail:(e)=>{
this.openDataBaseFail(e)
}
},
shutDataBase:{
name:'tcwangApp',
success:(e)=>{
this.closeDataBaseSuccess(e)
},
fail:(e)=>{
this.closeDataBaseFail(e)
}
}
}
},
methods:{
//打开数据库,不存在则直接创建数据库不建议传参,定义参数 以满足
//若需要暗data里的例子传参即可
openDataBase(options = this.options){
return new Promise ((res,rej)=>{
void plus.sqlite.openDatabase({
name:'tcwangApp',
path:`_doc/tcwangApp${uni.getStorageSync('userInfo').phoneNumber}.db`,
success:(e)=>{
this.openDataBaseSuccess(e)
res(e)
},
fail:(e)=>{
this.openDataBaseFail(e)
rej(e)
}
});
})
},
openDataBaseSuccess(e){
console.log('If you see me,dataBase open success',e);
},
openDataBaseFail(e){
console.error('If you see me,dataBase open fail',e);
},
//检查sqlite打开情况
checkDataBase(option){
let options = {}
if(option){
options = option
}else{
options = {
name:'tcwangApp',
path:'_doc/tcwangApp.db',
}
}
console.log(plus.sqlite.isOpenDatabase(option));
},
closeDatabase(options = this.shutDataBase){
void plus.sqlite.closeDatabase(options);
},
//关闭database
closeDataBaseSuccess(e){
console.log('If you see me,dataBase close success',e);
},
closeDataBaseFail(e){
console.error('If you see me,dataBase close fail',e);
},
//sqlite事务执行
transaction(options='begin'){
return new Promise((res,rej)=>{
void plus.sqlite.transaction({
name: 'tcwangApp',
operation:options ,
success: function(e){
res(e)
},
fail: function(e){
rej(e)
console.error('transaction failed: '+JSON.stringify(e));
}
});
})
},
//增删改数据
changeData(sql){
return new Promise((res,rej)=>{
void plus.sqlite.executeSql({
name: 'tcwangApp',
sql: sql,
success: function(e){
res(e)
},
fail: function(e){
rej(e)
console.error('executeSql failed: '+JSON.stringify(e));
}
});
})
},
//查询
selectDataBase(sql){
return new Promise((res,rej)=>{
void plus.sqlite.selectSql({
name: 'tcwangApp',
sql: sql,
success: (e) => {
res(e)
console.log('select dataBase success');
},
fail: (e) => {
rej(e)
console.error('select fail,balabala');
}
});
})
},
onFail(error){
console.error('hasErr',error);
}
}
}
接下来就是数据库的链接和表的建立和增删查改了,我这边暂时用不上复杂的事务啊怎么样,所以各位看官老爷届时自行更改,我这边提供一个思路。
changedatabase(){
this.openDataBase()
let sql = 'create table if not exists im_record (id INTEGER PRIMARY KEY AUTOINCREMENT,conversationID varchar(50) UNIQUE,time DATETIME,im_detail TEXT)'
this.changeData(sql).then(res=>{})
let _this = this
console.log(this.conversations,'this.conversations');
this.conversations.forEach(item=>{
test()
async function test(){
try{
let insertData = `insert into im_record (conversationID,time,im_detail) values ("${item.conversationID}", "${item.lastMessage.lastTime}","${AESUtil.encrypt(JSON.stringify(item)).replaceAll('/','-')}")`
await _this.changeData(insertData).then(res=>{console.log('action success!');_this.saveDetailMsg(item.conversationID,item.unreadCount)}).catch(()=>{
var a =null
return a.length
})
}catch (err){
let insertData =`UPDATE im_record SET time="${item.lastMessage.lastTime}", im_detail="${AESUtil.encrypt(JSON.stringify(item)).replaceAll('/','-')}" WHERE conversationID="${item.conversationID}"`
_this.changeData(insertData).then(res=>{console.log('更新数据成功action success!');_this.saveDetailMsg(item.conversationID,item.unreadCount)})
console.log(1234)
}
}
})
let selectData = `select * from im_record order by time DESC`
this.selectDataBase(selectData).then(res=>{
let arr = []
res.forEach(item=>{
arr.push(JSON.parse(AESUtil.decrypt(item.im_detail.replaceAll('-','/'))) )
})
this.conversations1 = arr
if(arr.length <1){
adminSendMsg().then(res => {
this.$store.dispatch('getConversation');
});
}
console.log(res,'查询成功');
})
},
注意之所以使用async 、awite主要是因为try catch只能捕获同步代码中的错误,这样就使得promise异步抛出的错误无法捕捉影响代码实现逻辑。我这边是先打开数据库,如果已经打开了抛出错误,直接建表
let sql = 'create table if not exists im_record (id INTEGER PRIMARY KEY AUTOINCREMENT,conversationID varchar(50) UNIQUE,time DATETIME,im_detail TEXT)'
由于sqlite在uniapp中没有办法使用可视化工具,所以就比较考验sql功底了,可以赵后端小伙伴帮忙。
然后开始插入数据,为了保证数据的唯一性,为每一条记录添加UNIQUE防止重复插入,如果已经存在则通过try catch 执行数据更新,让数据保持最新状态
let insertData =`UPDATE im_record SET time="${item.lastMessage.lastTime}", im_detail="${AESUtil.encrypt(JSON.stringify(item)).replaceAll('/','-')}" WHERE conversationID="${item.conversationID}"`
最后进行排序查询的结果就可以直接使用了,
接下来就是每一条消息的存储了,方法是一样的
建表
let sql = 'create table if not exists msg_dtail1 (id INTEGER PRIMARY KEY AUTOINCREMENT,conversationID TEXT,time DATETIME,msg_dtail TEXT)'
存储
let sql = `insert into msg_dtail1 (time,msg_dtail,conversationID) values ("${item.time}","${AESUtil.encrypt(JSON.stringify(item)).replaceAll('/','-')}","${item.conversationID}")`
我这边是根据未读消息进行存储的,所以不存在重复,所以不需要更新,删除时消息列和聊天记录一并删除即可
let sql = `DELETE FROM im_record2 WHERE conversationID="${e.conversationID}"`;
具体逻辑是一致的
在sql语句编写是要注意:笔者差点被坑疯掉,也是没认真看官方的文档的后果吧,(主要他说这玩意和mysql语句大体保持一致,小声比比)
就是写语句是插入和更新数据要用双引号抱起来,单引号是不行的,否则他就报错给你看说什么token:\\\\
笔者这边对数据的操作方法进行了封装,可见上方的sqlite.js文件中,写的比较乱,需要有一定基础查看,提供一种解决思路吧。
欢迎指正、留言