微信小程序簡單數據增刪改查案例
微信小程序雲開發提供了一個 JSON 數據庫,顧名思義,數據庫中的每條記錄都是一個 JSON 格式的對象。一個數據庫可以有多個集合(相當於關係型數據中的表),集合可看做一個 JSON 數組,數組中的每個對象就是一條記錄,記錄的格式是 JSON 對象。
關係型數據庫和 JSON 數據庫的概念對應關係如下表:
關係型 | 文檔型 |
---|---|
數據庫 database | 數據庫 database |
表 table | 集合 collection |
行 row | 記錄 record / doc |
列 column | 字段 field |
先看效果圖,微信小程序搜索陌然 Tool或者微信掃描下面二維碼可以觀看更多教程:
小程序二維碼
運行效果圖
案例源代碼
部分樣式我放在app.wxss文件中就沒有寫出來,樣式可以自己美化,每個人審美標準不一樣。
Page({
/**
* 頁面的初始數據
*/
data: {
openid: "", // 用戶身份ID
opName: "", // 數據庫操作名稱,如‘add’‘qry’等等
opResult: "", // 數據庫操作結果字符串
opResult2: "", // 數據庫操作結果字符串2
resData: null, // 數據庫操作結果數據
resData2: null, // 數據庫操作結果數據2
finished: false // 數據庫操作是否完成的標記
},
// “增”按鈕點擊事件函數
addRecord: function() {
this.setData({
opName: "add",
finished: false
})
},
// “刪”按鈕點擊事件函數
deleteRecord: function() {
this.setData({
opName: "del",
finished: false
})
},
// “改”按鈕點擊事件函數
updateRecord: function() {
this.setData({
opName: "upd",
finished: false
})
},
// “查”按鈕點擊事件函數
queryRecord: function() {
this.setData({
opName: "qry",
finished: false
})
},
// 拼接日期字符串的函數
makeDateString: function(dateObj) {
return dateObj.getFullYear() + '-' + (dateObj.getMonth() + 1) + '-' + dateObj.getDate();
},
// 拼接時間字符串的函數
makeTimeString: function(dateObj) {
return dateObj.getHours() + ':' + dateObj.getMinutes() + ':' + dateObj.getSeconds();
},
// 添加記錄事件函數
doAdd: function(e) {
console.log(e)
var workContent = e.detail.value.workContent
if (workContent != "") { // 如果用戶輸入內容不爲空
const db = wx.cloud.database() // 調用接口返回雲開發數據庫引用保存在常量db中
var myDate = new Date()
db.collection('work_done').add({ // 向集合‘work_done’中添加一條記錄
data: { // 一條記錄的字段數據
date: this.makeDateString(myDate), // 日期字符串
time: this.makeTimeString(myDate), // 時間字符串
content: workContent // 工作內容字符串
},
complete: res => { // 操作完成時的回調函數
this.setData({
finished: true
})
},
success: res => { // 操作成功時的回調函數
// 在返回結果中會包含新創建的記錄的 _id
this.setData({
opResult: "操作完成,新增一條記錄,_id爲:\n ",
resData: res._id
})
wx.showToast({
title: '新增記錄成功',
})
console.log('[數據庫] [新增記錄] 成功,記錄 _id: ', res._id)
},
fail: err => { // 操作失敗時的回調函數
wx.showToast({
icon: 'none',
title: '新增記錄失敗'
})
console.error('[數據庫] [新增記錄] 失敗:', err)
}
})
} else {
wx.showToast({
title: '請輸入事情描述!',
})
}
},
// 刪除記錄事件函數
doDelete: function(e) {
console.log(e)
var that = this
var itemID = e.detail.value.itemID
if (itemID != "") { // 如果用戶輸入的記錄id不爲空
const db = wx.cloud.database() // 調用接口返回雲開發數據庫引用保存在常量db中
db.collection('work_done').doc(itemID).get({ // 從集合‘work_done’中查詢id爲itemID的記錄
success: res => { // 操作成功時的回調函數
console.log(res)
this.setData({
opResult: '查詢記錄成功:\n',
resData: res.data
})
db.collection('work_done').doc(itemID).remove({ // 操作接口從集合‘work_done’中刪除這條記錄
complete: res => { // 操作完成時的回調函數
that.setData({
finished: true
})
},
success: res => { // 操作成功時的回調函數
console.log('[數據庫] [刪除記錄] 成功: ', res)
that.setData({
opResult2: '已成功刪除上面的記錄。'
})
},
fail: err => { // 操作失敗時的回調函數
wx.showToast({
icon: 'none',
title: '刪除記錄失敗'
})
console.error('[數據庫] [刪除記錄] 失敗:', err)
}
})
},
fail: err => { // 操作失敗時的回調函數
wx.showToast({
icon: 'none',
title: '查詢記錄失敗'
})
console.error('[數據庫] [查詢記錄] 失敗:', err)
}
})
} else {
wx.showToast({
title: '請輸入itemID!',
})
}
},
// 更新記錄事件函數
doUpdate: function(e) {
console.log(e)
var that = this
var itemID = e.detail.value.itemID
var workContent = e.detail.value.workContent
if (itemID != "") { // 如果用戶輸入的記錄id不爲空
const db = wx.cloud.database() // 調用接口返回雲開發數據庫引用保存在常量db中
db.collection('work_done').doc(itemID).get({ // 從集合‘work_done’中查詢id爲itemID的記錄
success: res => { // 操作成功時的回調函數
this.setData({
opResult: '查詢記錄成功:\n',
resData: res.data
})
db.collection('work_done').doc(itemID).update({ // 更新集合‘work_done’中的這條記錄
data: {
content: workContent,
},
complete: res => { // 操作完成時的回調函數
that.setData({
finished: true
})
},
success: res => { // 操作成功時的回調函數
console.log('[數據庫] [更新記錄] 成功: ', res)
that.setData({
opResult2: '已成功更新上面的記錄內容爲:\n',
resData2: workContent
})
},
fail: err => { // 操作失敗時的回調函數
wx.showToast({
icon: 'none',
title: '更新記錄失敗'
})
console.error('[數據庫] [更新記錄] 失敗:', err)
}
})
},
fail: err => { // 操作失敗時的回調函數
wx.showToast({
icon: 'none',
title: '查詢記錄失敗'
})
console.error('[數據庫] [查詢記錄] 失敗:', err)
}
})
} else {
wx.showToast({
title: '請輸入itemID!',
})
}
},
// 查詢記錄事件函數
doQuery: function(e) {
console.log(e)
var workDate = e.detail.value.workDate
if (workDate != "") { // 如果用戶輸入的日期字符串不爲空
const db = wx.cloud.database() // 調用接口返回雲開發數據庫引用保存在常量db中
db.collection('work_done').where({ // 從集合‘work_done’中查詢記錄(最多二十條)
date: workDate // 記錄創建日期
}).get({
complete: res => { // 操作完成時的回調函數
this.setData({
finished: true
})
},
success: res => { // 操作成功時的回調函數
this.setData({
opResult: "操作完成,查詢到" + res.data.length + "條記錄:\n ",
resData: res.data
})
console.log('[數據庫] [查詢記錄] 成功: ', res)
},
fail: err => { // 操作失敗時的回調函數
wx.showToast({
icon: 'none',
title: '查詢記錄失敗'
})
console.error('[數據庫] [查詢記錄] 失敗:', err)
}
})
} else {
wx.showToast({
title: '請輸入查詢日期!',
})
}
},
/**
* 生命週期函數--監聽頁面加載
*/
onLoad: function(options) {
this.setData({
openid: wx.getStorageSync('openID') // 讀取本地存儲的openID(“獲取OpenID”案例中同步存儲了用戶的openid到本地)
});
},
/**
* 生命週期函數--監聽頁面初次渲染完成
*/
onReady: function() {
},
/**
* 生命週期函數--監聽頁面顯示
*/
onShow: function() {
},
/**
* 生命週期函數--監聽頁面隱藏
*/
onHide: function() {
},
/**
* 生命週期函數--監聽頁面卸載
*/
onUnload: function() {
},
/**
* 頁面相關事件處理函數--監聽用戶下拉動作
*/
onPullDownRefresh: function() {
},
/**
* 頁面上拉觸底事件的處理函數
*/
onReachBottom: function() {
},
/**
* 用戶點擊右上角分享
*/
onShareAppMessage: function() {
}
})
<!--pages/cloud/Database/index.wxml-->
<view class="box">
<view class='title'>數據庫操作</view>
<text class="preNote">請點擊相應按鈕,實現在數據庫中增加、刪除、更新或查詢記錄的操作</text>
<view class="Hcontainer">
<button class='DBbutton' bindtap="addRecord" style='background-color: {{opName=="add"?"#ae57a4":"blue"}}'>增</button>
<button class='DBbutton' bindtap="deleteRecord" style='background-color: {{opName=="del"?"#ae57a4":"blue"}}'>刪</button>
<button class='DBbutton' bindtap="updateRecord" style='background-color: {{opName=="upd"?"#ae57a4":"blue"}}'>改</button>
<button class='DBbutton' bindtap="queryRecord" style='background-color: {{opName=="qry"?"#ae57a4":"blue"}}'>查</button>
</view>
<view wx:if="{{opName=='add'}}">
<!-- 新增記錄 -->
<view class="record-op" style='{{opName!=""?"border: 1px solid #00007f":""}}'>
<form bindsubmit='doAdd'>
<text class="op-note">添加一件今日已完成的工作:</text>
<textarea name="workContent" class="content-input" maxlength="50" placeholder="事情描述(不超過50個字)" auto-height adjust-position cursor-spacing='20px'></textarea>
<button form-type='submit' type='primary'>確定</button>
</form>
</view>
<view wx:if="{{finished}}" class="op-result">
<text class="headline">操作結果信息:</text>
<text class='text-title'>{{opResult}}</text>
<text class="list" selectable>{{resData}}</text>
</view>
</view>
<view wx:if="{{opName=='del'}}">
<!-- 刪除記錄 -->
<view class="record-op" style='{{opName!=""?"border: 1px solid #00007f":""}}'>
<form bindsubmit='doDelete'>
<text class="op-note">指定刪除item的ID:</text>
<input name="itemID" class="line-input" maxlength="32" placeholder="itemID(32位字符串)" />
<button form-type='submit' type='primary'>確定</button>
</form>
</view>
<view wx:if="{{finished}}" class="op-result">
<text class="headline">操作結果信息:</text>
<text class='text-title'>{{opResult}}</text>
<text class="list" selectable>{{resData._id}}:{{resData.date}} {{resData.time}} {{resData.content}}</text>
<text class='text-title'>{{opResult2}}</text>
</view>
</view>
<view wx:if="{{opName=='upd'}}">
<!-- 更新記錄 -->
<view class="record-op" style='{{opName!=""?"border: 1px solid #00007f":""}}'>
<form bindsubmit='doUpdate'>
<text class="op-note">指定更新item的ID:</text>
<input name="itemID" class="line-input" maxlength="32" placeholder="itemID(32位字符串)" />
<text class="op-note">輸入更新的內容:</text>
<textarea name="workContent" class="content-input" maxlength="50" placeholder="事情描述(不超過50個字)" auto-height adjust-position cursor-spacing='20px'></textarea>
<button form-type='submit' type='primary'>確定</button>
</form>
</view>
<view wx:if="{{finished}}" class="op-result">
<text class="headline">操作結果信息:</text>
<text class='text-title'>{{opResult}}</text>
<text class="list" selectable>{{resData._id}}:{{resData.date}} {{resData.time}} {{resData.content}}</text>
<text class='text-title'>{{opResult2}}</text>
<text class="list" selectable>{{resData2}}</text>
</view>
</view>
<view wx:if="{{opName == 'qry'}}">
<!-- 查詢記錄 -->
<view class="record-op" style='{{opName!=""?"border: 1px solid #00007f":""}}'>
<form bindsubmit='doQuery'>
<text class="op-note">指定查詢日期(年-月-日,不需要無效的0):</text>
<input name="workDate" class="line-input" maxlength="10" placeholder="事件日期(年-月-日)" />
<button form-type='submit' type='primary'>確定</button>
</form>
</view>
<view wx:if="{{finished}}" class="op-result">
<text class="headline">操作結果信息:</text>
<text class='text-title'>{{opResult}}</text>
<block wx:for='{{resData}}' wx:key='{{item._id}}'>
<text class="list" selectable>{{item._id}}:{{item.date}} {{item.time}} {{item.content}}</text>
</block>
</view>
</view>
</view>
/* pages/cloud/Database/index.wxss */
.preNote {
padding: 20px;
font-size: 32rpx;
line-height: 40rpx;
color: #666;
box-sizing: border-box;
}
.Hcontainer {
margin: 20rpx 0rpx;
padding: 0 50rpx;
display: flex;
flex-direction: row;
justify-content: space-around;
}
.DBbutton {
width: 100rpx;
color: white;
background-color: #0066ff;
}
.record-op {
margin: 10rpx;
padding: 20rpx;
box-sizing: border-box;
}
.op-note {
color: #000;
font-size: 14px;
}
.content-input {
width: 90%;
padding: 20rpx;
margin: 20rpx;
min-height: 200rpx;
border: 1px solid #ccc;
box-sizing: border-box;
}
.op-result {
padding: 10rpx;
box-sizing: border-box;
display: flex;
flex-direction: column;
}
.op-result .headline {
margin-top: 50rpx;
font-size: 40rpx;
font-weight: bold;
color: #0066ff;
}
.op-result .text-title {
margin-top: 20rpx;
margin-bottom: 10rpx;
padding-left: 20rpx;
padding-right: 20rpx;
font-size: 14px;
color: red;
}
.op-result .list {
margin-top: 20rpx;
font-size: 28rpx;
color: black;
display: block;
}
.line-input {
min-height: 30px;
line-height: 20px;
width: 100%;
padding: 5px;
margin: 10px 0;
display: inline-block;
border: 1px solid #ccc;
border-radius: 5px;
box-sizing: border-box;
}
雲開發數據庫提供以下幾種數據類型:
- String:字符串
- Number:數字
- Object:對象
- Array:數組
- Bool:布爾值
- Date:時間
- Geo:多種地理位置類型,詳見下
- Null:相當於一個佔位符,表示一個字段存在但是值爲空。
下面對Data的字段做下補充說明。
Date
Date 類型用於表示時間,精確到毫秒,在小程序端可用 JavaScript 內置 Date 對象創建。需要特別注意的是,在小程序端創建的時間是客戶端時間,不是服務端時間,這意味着在小程序端的時間與服務端時間不一定吻合,如果需要使用服務端時間,應該用 API 中提供的 serverDate 對象來創建一個服務端當前時間的標記,當使用了 serverDate 對象的請求抵達服務端處理時,該字段會被轉換成服務端當前的時間,更棒的是,我們在構造 serverDate 對象時還可通過傳入一個有 offset 字段的對象來標記一個與當前服務端時間偏移 offset 毫秒的時間,這樣我們就可以達到比如如下效果:指定一個字段爲服務端時間往後一個小時。
那麼當我們需要使用客戶端時間時,存放 Date 對象和存放毫秒數是否是一樣的效果呢?不是的,我們的數據庫有針對日期類型的優化,建議大家使用時都用 Date 或 serverDate 構造時間對象。
以下是一個示例的集合數據,假設我們有一個 books 集合存放了圖書記錄,其中有兩本書:
[
{
"_id": "Wzh76lk5_O_dt0vO",
"title": "The Catcher in the Rye",
"author": "J. D. Salinger",
"characters": [
"Holden Caulfield",
"Stradlater",
"Mr. Antolini"
],
"publishInfo": {
"year": 2019,
"country": "United States"
}
},
{
"_id": "Wzia0lk5_O_dt0vR",
"_openid": "ohl4L0Rnhq7vmmbT_DaNQa4ePaz0",
"title": "The Lady of the Camellias",
"author": "Alexandre Dumas fils",
"characters": [
"Marguerite Gautier",
"Armand Duval",
"Prudence",
"Count de Varville"
],
"publishInfo": {
"year": 1998,
"country": "France"
}
}
]
在圖書信息中,我們用 title, author 來記錄圖書標題和作者,用 characters 數組來記錄書中的主要人物,用 publishInfo 來記錄圖書的出版信息。在其中我們可以看到,字段既可以是字符串或數字,還可以是對象或數組,就是一個 JSON 對象。
每條記錄都有一個 _id 字段用以唯一標誌一條記錄、一個 _openid 字段用以標誌記錄的創建者,即小程序的用戶。需要特別注意的是,在管理端(控制檯和雲函數)中創建的不會有 _openid 字段,因爲這是屬於管理員創建的記錄。開發者可以自定義 _id,但不可自定義和修改 _openid 。_openid 是在文檔創建時由系統根據小程序用戶默認創建的,開發者可使用其來標識和定位文檔。
數據庫 API 分爲小程序端和服務端兩部分,小程序端 API 擁有嚴格的調用權限控制,開發者可在小程序內直接調用 API 進行非敏感數據的操作。對於有更高安全要求的數據,可在雲函數內通過服務端 API 進行操作。雲函數的環境是與客戶端完全隔離的,在雲函數上可以私密且安全的操作數據庫。
數據庫 API 包含增刪改查的能力,使用 API 操作數據庫只需三步:獲取數據庫引用、構造查詢/更新條件、發出請求。以下是一個在小程序中查詢數據庫的發表於美國的圖書記錄的例子:
// 1. 獲取數據庫引用
const db = wx.cloud.database()
// 2. 構造查詢語句
// collection 方法獲取一個集合的引用
// where 方法傳入一個對象,數據庫返回集合中字段等於指定值的 JSON 文檔。API 也支持高級的查詢條件(比如大於、小於、in 等),具體見文檔查看支持列表
// get 方法會觸發網絡請求,往數據庫取數據
db.collection('books').where({
publishInfo: {
country: 'United States'
}
}).get({
success: function(res) {
// 輸出 [{ "title": "The Catcher in the Rye", ... }]
console.log(res)
}
})