uni-app 云数据库操作

章节介绍

数据库是应用开发中不可或缺的组成部分,用于存储和管理数据。uni-app 提供了基于 uniCloud 的云数据库服务,开发者可以通过云数据库实现数据的存储、查询、更新和删除等操作,无需关心数据库的搭建、配置和维护。本章节将详细介绍如何操作 uni-app 云数据库,包括数据库的概念、创建方法、增删改查操作、索引使用、事务处理和最佳实践,帮助开发者高效地管理和操作云数据库。

核心知识点讲解

1. 云数据库概述

uniCloud 云数据库是一种 NoSQL 数据库服务,基于文档模型设计,具有以下特点:

  • 无需搭建和维护:开发者无需关心数据库的搭建、配置、监控和维护
  • 自动扩缩容:根据数据量和请求量自动调整存储和计算资源
  • 高可靠性:数据多副本存储,确保数据安全可靠
  • 高并发:支持高并发读写操作,应对大规模应用场景
  • 易于使用:提供简洁的 API,与前端开发无缝集成
  • 支持复杂查询:支持多种查询条件、排序、聚合等操作

2. 云数据库的创建与配置

2.1 创建数据库集合

在 uniCloud 中,数据存储在集合(Collection)中,集合类似于关系型数据库中的表。创建集合的步骤如下:

  1. 在 HBuilderX 中打开项目

    • 确保项目已关联 uniCloud 服务空间
  2. 打开云数据库管理页面

    • 点击 "uniCloud" > "云数据库" > "打开云数据库"
  3. 创建集合

    • 点击 "创建集合" 按钮
    • 输入集合名称,如 "users"
    • 点击 "确定" 按钮

2.2 集合结构设计

在创建集合之前,需要设计合理的集合结构。云数据库是文档型数据库,每个文档(Document)是一个 JSON 对象,具有以下特点:

  • 文档结构灵活,无需预定义字段
  • 不同文档可以有不同的字段结构
  • 支持嵌套文档和数组
  • 每个文档有一个唯一的 _id 字段,由系统自动生成

3. 云数据库的基本操作

3.1 初始化数据库

在前端或云函数中使用云数据库之前,需要初始化数据库:

在前端初始化

// 初始化数据库
const db = uniCloud.database();

在云函数中初始化

'use strict';
const db = uniCloud.database();

exports.main = async (event, context) => {
  // 数据库操作
  // ...
};

3.2 插入数据

插入单个文档

// 插入单个文档
const res = await db.collection('users').add({
  name: '张三',
  age: 25,
  email: 'zhangsan@example.com',
  createTime: new Date()
});
console.log('插入成功,文档 ID:', res.id);

批量插入文档

// 批量插入文档
const users = [
  { name: '张三', age: 25, email: 'zhangsan@example.com' },
  { name: '李四', age: 30, email: 'lisi@example.com' },
  { name: '王五', age: 35, email: 'wangwu@example.com' }
];

// 循环插入
for (const user of users) {
  await db.collection('users').add(user);
}
console.log('批量插入成功');

3.3 查询数据

查询所有文档

// 查询所有文档
const res = await db.collection('users').get();
console.log('查询结果:', res.data);

条件查询

// 条件查询
const res = await db.collection('users')
  .where({
    age: { $gt: 25 }, // 年龄大于 25
    name: /^张/ // 名字以 "张" 开头
  })
  .get();
console.log('查询结果:', res.data);

排序查询

// 排序查询
const res = await db.collection('users')
  .where({ age: { $gt: 25 } })
  .orderBy('age', 'desc') // 按年龄降序排序
  .get();
console.log('查询结果:', res.data);

分页查询

// 分页查询
const page = 1;
const pageSize = 10;
const offset = (page - 1) * pageSize;

const res = await db.collection('users')
  .orderBy('createTime', 'desc')
  .skip(offset) // 跳过前面的文档
  .limit(pageSize) // 限制返回数量
  .get();
console.log('查询结果:', res.data);

获取单个文档

// 根据 ID 获取单个文档
const res = await db.collection('users').doc('document-id').get();
console.log('查询结果:', res.data);

3.4 更新数据

更新单个文档

// 更新单个文档
const res = await db.collection('users')
  .doc('document-id')
  .update({
    age: 26,
    updateTime: new Date()
  });
console.log('更新成功,影响行数:', res.updated);

条件更新

// 条件更新
const res = await db.collection('users')
  .where({ name: '张三' })
  .update({
    age: 26,
    updateTime: new Date()
  });
console.log('更新成功,影响行数:', res.updated);

字段自增

// 字段自增
const res = await db.collection('users')
  .doc('document-id')
  .update({
    age: db.command.inc(1) // 年龄自增 1
  });
console.log('更新成功,影响行数:', res.updated);

字段数组操作

// 向数组添加元素
const res = await db.collection('users')
  .doc('document-id')
  .update({
    hobbies: db.command.push('reading') // 向 hobbies 数组添加元素
  });
console.log('更新成功,影响行数:', res.updated);

// 从数组删除元素
const res = await db.collection('users')
  .doc('document-id')
  .update({
    hobbies: db.command.pull('reading') // 从 hobbies 数组删除元素
  });
console.log('更新成功,影响行数:', res.updated);

3.5 删除数据

删除单个文档

// 删除单个文档
const res = await db.collection('users').doc('document-id').remove();
console.log('删除成功,影响行数:', res.deleted);

条件删除

// 条件删除
const res = await db.collection('users')
  .where({ age: { $lt: 18 } })
  .remove();
console.log('删除成功,影响行数:', res.deleted);

4. 云数据库的高级操作

4.1 索引的使用

索引可以提高查询性能,特别是在处理大量数据时。uniCloud 云数据库支持创建索引:

创建索引

  1. 在云数据库管理页面中创建

    • 打开云数据库管理页面
    • 选择要创建索引的集合
    • 点击 "索引管理" 选项卡
    • 点击 "创建索引" 按钮
    • 输入索引名称、字段和排序方式
    • 点击 "确定" 按钮
  2. 常用索引类型

    • 单字段索引:基于单个字段创建的索引
    • 复合索引:基于多个字段创建的索引
    • 地理索引:用于地理空间查询

索引使用建议

  • 为常用查询字段创建索引
  • 避免创建过多索引,影响写入性能
  • 合理设计复合索引的字段顺序
  • 定期检查和优化索引

4.2 事务处理

事务是一组原子操作,要么全部成功,要么全部失败,用于确保数据的一致性。uniCloud 云数据库支持事务操作:

使用事务

// 使用事务
const transaction = await db.startTransaction();
try {
  // 操作 1:扣除用户余额
  await transaction.collection('users')
    .doc('user-id')
    .update({
      balance: db.command.inc(-100)
    });
  
  // 操作 2:添加订单记录
  const orderRes = await transaction.collection('orders')
    .add({
      userId: 'user-id',
      amount: 100,
      createTime: new Date()
    });
  
  // 操作 3:更新商品库存
  await transaction.collection('products')
    .doc('product-id')
    .update({
      stock: db.command.inc(-1)
    });
  
  // 提交事务
  await transaction.commit();
  console.log('事务执行成功');
} catch (error) {
  // 回滚事务
  await transaction.rollback();
  console.error('事务执行失败:', error);
  throw error;
}

事务使用场景

  • 转账操作:从一个账户扣除金额,向另一个账户添加金额
  • 订单处理:扣除库存,创建订单,更新用户余额
  • 批量操作:确保多个操作要么全部成功,要么全部失败

4.3 聚合操作

聚合操作用于对数据进行统计、分组、计算等处理,uniCloud 云数据库支持多种聚合操作:

计数

// 计数
const res = await db.collection('users').count();
console.log('文档数量:', res.total);

分组统计

// 分组统计
const res = await db.collection('orders')
  .aggregate()
  .group({
    _id: '$userId', // 按用户 ID 分组
    totalAmount: { $sum: '$amount' }, // 计算总金额
    orderCount: { $sum: 1 } // 计算订单数量
  })
  .end();
console.log('聚合结果:', res.data);

平均值计算

// 平均值计算
const res = await db.collection('users')
  .aggregate()
  .group({
    _id: null, // 不分组,计算所有文档的平均值
    avgAge: { $avg: '$age' } // 计算平均年龄
  })
  .end();
console.log('平均年龄:', res.data[0].avgAge);

最大值和最小值

// 最大值和最小值
const res = await db.collection('products')
  .aggregate()
  .group({
    _id: null,
    maxPrice: { $max: '$price' }, // 计算最大价格
    minPrice: { $min: '$price' } // 计算最小价格
  })
  .end();
console.log('最大价格:', res.data[0].maxPrice);
console.log('最小价格:', res.data[0].minPrice);

5. 云数据库的安全与权限

5.1 数据权限控制

uniCloud 云数据库提供了细粒度的数据权限控制,确保数据安全:

集合权限设置

  • 只读:用户只能读取数据,不能写入或删除数据
  • 读写:用户可以读取、写入和删除数据
  • 自定义:根据具体需求设置权限

文档级权限控制

  • 基于用户 ID 的权限控制:用户只能访问自己的数据
  • 基于角色的权限控制:根据用户角色设置不同的权限

5.2 安全最佳实践

  • 使用参数校验:对用户输入进行严格校验,防止注入攻击
  • 使用环境变量:将敏感信息存储在环境变量中,避免硬编码
  • 限制查询结果:使用 limit 限制查询返回的文档数量,防止过度查询
  • 使用索引:为常用查询字段创建索引,提高查询性能
  • 定期清理数据:定期清理无用数据,减少存储成本
  • 备份数据:定期备份重要数据,防止数据丢失

6. 云数据库的性能优化

6.1 查询优化

  • 使用索引:为常用查询字段创建索引
  • 避免全表扫描:使用合适的查询条件,避免查询所有文档
  • 限制返回字段:使用 field 方法限制返回的字段,减少数据传输
  • 合理使用排序:排序操作会影响性能,特别是在大数据集上
  • 分页查询:使用 skip 和 limit 进行分页查询,避免一次性查询过多数据

6.2 写入优化

  • 批量操作:使用批量操作减少网络请求次数
  • 避免过多索引:索引会影响写入性能,避免创建过多索引
  • 合理设计文档结构:避免文档过大,合理设计嵌套结构
  • 使用事务:对于相关操作,使用事务确保数据一致性

6.3 存储优化

  • 数据压缩:对大型文本字段进行压缩存储
  • 合理使用数据类型:选择合适的数据类型,减少存储空间
  • 定期清理数据:定期清理无用数据,减少存储成本
  • 使用合适的集合结构:根据数据特点选择合适的集合结构

实用案例分析

案例一:开发一个商品管理系统

需求分析

开发一个商品管理系统,包括商品的添加、查询、更新、删除等功能。

实现步骤

  1. 创建集合

创建以下集合:

  • products:存储商品信息
  1. 设计商品集合结构
{
  _id: 'product-id', // 商品 ID,系统自动生成
  name: '商品名称', // 商品名称
  price: 99.99, // 商品价格
  stock: 100, // 商品库存
  category: '商品分类', // 商品分类
description: '商品描述', // 商品描述
  images: ['image1.jpg', 'image2.jpg'], // 商品图片
  createTime: new Date(), // 创建时间
  updateTime: new Date() // 更新时间
}
  1. 实现商品管理功能

添加商品

async function addProduct(product) {
  try {
    const db = uniCloud.database();
    const res = await db.collection('products').add({
      ...product,
      createTime: new Date(),
      updateTime: new Date()
    });
    console.log('添加商品成功:', res.id);
    return res.id;
  } catch (error) {
    console.error('添加商品失败:', error);
    throw error;
  }
}

// 调用函数
addProduct({
  name: 'iPhone 13',
  price: 5999,
  stock: 50,
  category: '手机',
description: 'Apple iPhone 13',
  images: ['iphone13-1.jpg', 'iphone13-2.jpg']
});

查询商品

async function getProducts(params) {
  try {
    const db = uniCloud.database();
    let query = db.collection('products');
    
    // 条件查询
    if (params.category) {
      query = query.where({ category: params.category });
    }
    
    if (params.minPrice) {
      query = query.where({ price: { $gte: params.minPrice } });
    }
    
    if (params.maxPrice) {
      query = query.where({ price: { $lte: params.maxPrice } });
    }
    
    // 排序
    if (params.sortBy) {
      query = query.orderBy(params.sortBy, params.sortOrder || 'asc');
    } else {
      query = query.orderBy('createTime', 'desc');
    }
    
    // 分页
    const page = params.page || 1;
    const pageSize = params.pageSize || 10;
    const offset = (page - 1) * pageSize;
    
    query = query.skip(offset).limit(pageSize);
    
    const res = await query.get();
    const countRes = await db.collection('products').count();
    
    return {
      products: res.data,
      total: countRes.total,
      page,
      pageSize
    };
  } catch (error) {
    console.error('查询商品失败:', error);
    throw error;
  }
}

// 调用函数
getProducts({
  category: '手机',
  minPrice: 3000,
  maxPrice: 8000,
  sortBy: 'price',
  sortOrder: 'desc',
  page: 1,
  pageSize: 10
});

更新商品

async function updateProduct(id, updates) {
  try {
    const db = uniCloud.database();
    const res = await db.collection('products')
      .doc(id)
      .update({
        ...updates,
        updateTime: new Date()
      });
    console.log('更新商品成功,影响行数:', res.updated);
    return res.updated;
  } catch (error) {
    console.error('更新商品失败:', error);
    throw error;
  }
}

// 调用函数
updateProduct('product-id', {
  price: 5499,
  stock: 45,
description: 'Apple iPhone 13 (更新版)'
});

删除商品

async function deleteProduct(id) {
  try {
    const db = uniCloud.database();
    const res = await db.collection('products').doc(id).remove();
    console.log('删除商品成功,影响行数:', res.deleted);
    return res.deleted;
  } catch (error) {
    console.error('删除商品失败:', error);
    throw error;
  }
}

// 调用函数
deleteProduct('product-id');
  1. 添加索引

products 集合添加以下索引:

  • category 索引:加速按分类查询
  • price 索引:加速按价格查询和排序
  • createTime 索引:加速按创建时间排序

案例二:开发一个用户订单系统

需求分析

开发一个用户订单系统,包括订单的创建、查询、更新状态等功能。

实现步骤

  1. 创建集合

创建以下集合:

  • orders:存储订单信息
  • orderItems:存储订单商品信息
  1. 设计集合结构

orders 集合结构

{
  _id: 'order-id', // 订单 ID,系统自动生成
  userId: 'user-id', // 用户 ID
  orderNo: '202301010001', // 订单号
  totalAmount: 999.99, // 总金额
  status: 'pending', // 订单状态:pending(待支付)、paid(已支付)、shipped(已发货)、completed(已完成)、cancelled(已取消)
  createTime: new Date(), // 创建时间
  payTime: null, // 支付时间
  shipTime: null, // 发货时间
  completeTime: null, // 完成时间
  cancelTime: null // 取消时间
}

orderItems 集合结构

{
  _id: 'order-item-id', // 订单商品 ID,系统自动生成
  orderId: 'order-id', // 订单 ID
  productId: 'product-id', // 商品 ID
  productName: '商品名称', // 商品名称
  price: 99.99, // 商品价格
  quantity: 2, // 商品数量
  subtotal: 199.98 // 商品小计
}
  1. 实现订单管理功能

创建订单

async function createOrder(orderData, items) {
  try {
    const db = uniCloud.database();
    const transaction = await db.startTransaction();
    
    try {
      // 生成订单号
      const orderNo = 'ORD' + Date.now() + Math.floor(Math.random() * 1000);
      
      // 计算总金额
      const totalAmount = items.reduce((sum, item) => sum + item.subtotal, 0);
      
      // 创建订单
      const orderRes = await transaction.collection('orders').add({
        userId: orderData.userId,
        orderNo,
        totalAmount,
        status: 'pending',
        createTime: new Date(),
        payTime: null,
        shipTime: null,
        completeTime: null,
        cancelTime: null
      });
      
      const orderId = orderRes.id;
      
      // 创建订单商品
      for (const item of items) {
        await transaction.collection('orderItems').add({
          orderId,
          productId: item.productId,
          productName: item.productName,
          price: item.price,
          quantity: item.quantity,
          subtotal: item.subtotal
        });
        
        // 更新商品库存
        await transaction.collection('products')
          .doc(item.productId)
          .update({
            stock: db.command.inc(-item.quantity)
          });
      }
      
      // 提交事务
      await transaction.commit();
      console.log('创建订单成功:', orderId);
      return orderId;
    } catch (error) {
      // 回滚事务
      await transaction.rollback();
      console.error('创建订单失败:', error);
      throw error;
    }
  } catch (error) {
    console.error('创建订单失败:', error);
    throw error;
  }
}

// 调用函数
createOrder(
  {
    userId: 'user-id'
  },
  [
    {
      productId: 'product-id-1',
      productName: 'iPhone 13',
      price: 5999,
      quantity: 1,
      subtotal: 5999
    },
    {
      productId: 'product-id-2',
      productName: 'AirPods Pro',
      price: 1999,
      quantity: 1,
      subtotal: 1999
    }
  ]
);

查询订单

async function getOrders(userId, params) {
  try {
    const db = uniCloud.database();
    let query = db.collection('orders').where({ userId });
    
    // 状态查询
    if (params.status) {
      query = query.where({ status: params.status });
    }
    
    // 时间范围查询
    if (params.startDate) {
      query = query.where({ createTime: { $gte: new Date(params.startDate) } });
    }
    
    if (params.endDate) {
      query = query.where({ createTime: { $lte: new Date(params.endDate) } });
    }
    
    // 排序
    query = query.orderBy('createTime', 'desc');
    
    // 分页
    const page = params.page || 1;
    const pageSize = params.pageSize || 10;
    const offset = (page - 1) * pageSize;
    
    const res = await query.skip(offset).limit(pageSize).get();
    const countRes = await db.collection('orders').where({ userId }).count();
    
    // 获取订单商品
    const orders = res.data;
    for (const order of orders) {
      const itemsRes = await db.collection('orderItems')
        .where({ orderId: order._id })
        .get();
      order.items = itemsRes.data;
    }
    
    return {
      orders,
      total: countRes.total,
      page,
      pageSize
    };
  } catch (error) {
    console.error('查询订单失败:', error);
    throw error;
  }
}

// 调用函数
getOrders('user-id', {
  status: 'paid',
  startDate: '2023-01-01',
  endDate: '2023-01-31',
  page: 1,
  pageSize: 10
});

更新订单状态

async function updateOrderStatus(orderId, status) {
  try {
    const db = uniCloud.database();
    const updates = { status };
    
    // 根据状态更新时间
    switch (status) {
      case 'paid':
        updates.payTime = new Date();
        break;
      case 'shipped':
        updates.shipTime = new Date();
        break;
      case 'completed':
        updates.completeTime = new Date();
        break;
      case 'cancelled':
        updates.cancelTime = new Date();
        // 恢复商品库存
        const itemsRes = await db.collection('orderItems')
          .where({ orderId })
          .get();
        
        for (const item of itemsRes.data) {
          await db.collection('products')
            .doc(item.productId)
            .update({
              stock: db.command.inc(item.quantity)
            });
        }
        break;
    }
    
    const res = await db.collection('orders')
      .doc(orderId)
      .update(updates);
    
    console.log('更新订单状态成功,影响行数:', res.updated);
    return res.updated;
  } catch (error) {
    console.error('更新订单状态失败:', error);
    throw error;
  }
}

// 调用函数
updateOrderStatus('order-id', 'paid');
  1. 添加索引

为以下集合添加索引:

orders 集合

  • userId 索引:加速按用户 ID 查询
  • status 索引:加速按状态查询
  • createTime 索引:加速按创建时间排序

orderItems 集合

  • orderId 索引:加速按订单 ID 查询
  • productId 索引:加速按商品 ID 查询

学习目标

通过本章节的学习,您应该能够:

  1. 了解云数据库的概念和特点
  2. 掌握云数据库集合的创建和结构设计方法
  3. 学会云数据库的基本操作:插入、查询、更新、删除
  4. 掌握云数据库的高级操作:索引、事务、聚合
  5. 了解云数据库的安全与权限控制
  6. 掌握云数据库的性能优化方法
  7. 能够开发实际的数据库应用,如商品管理系统和订单系统

总结与展望

云数据库是 uni-app 开发中的重要组成部分,它为开发者提供了一种简单、高效的数据存储和管理方式,无需关心数据库的搭建和维护。通过本章节的学习,您已经掌握了云数据库的创建、配置、基本操作、高级操作、安全与权限控制和性能优化,为您开发数据驱动的应用奠定了基础。

在后续的学习中,我们将继续探索 uni-app 的高级特性,包括云数据库与前端的结合使用、云数据库的监控和调试、云数据库的迁移和备份等内容,帮助您成为一名更加全面的 uni-app 开发者。

记住,数据库设计和操作是应用开发的核心部分,合理的数据库设计和高效的数据库操作可以显著提高应用的性能和用户体验。在开发过程中,要注意数据库结构的设计、索引的使用、查询的优化和数据的安全,确保应用的稳定运行和数据的安全可靠。

« 上一篇 uni-app 云函数开发 下一篇 » uni-app 云存储使用