模型删除数据 (Model Delete) 
概述 
Yao 提供了多种删除数据的方法,支持单条记录删除和批量删除,同时兼容软删除(Soft Delete)和永久删除(Hard Delete)两种模式。本文档详细介绍各种删除方式的特点、参数和使用场景。
删除方法概览 
| 方法名称 | 功能描述 | 支持软删除 | 使用场景 | 
|---|---|---|---|
| Delete | 删除单条记录 | ✅ 支持 | 需要保留删除记录的单条删除 | 
| Destroy | 永久删除单条记录 | ❌ 不支持 | 彻底清除数据的单条删除 | 
| DeleteWhere | 条件批量删除 | ✅ 支持 | 需要保留删除记录的批量删除 | 
| DestroyWhere | 条件批量永久删除 | ❌ 不支持 | 彻底清除数据的批量删除 | 
软删除机制 
软删除说明 
软删除不会真正从数据库中移除数据,而是通过添加删除时间标记(默认字段名deleted_at)来标识记录已被"删除"。这些被标记的记录在常规查询中不会被检索到,但仍然保存在数据库中,可以在需要时恢复。
配置软删除 
在模型 JSON 定义中启用软删除功能:
json
{
  "option": {
    "soft_deletes": true // 开启软删除功能
  }
}基本用法详解 
Delete 方法 
用于删除单条记录,支持软删除。
参数说明:
- 参数1:记录ID或主键值
 
返回值:
- 成功:返回被删除记录的条数(通常为1)
 - 失败:抛出异常
 
示例代码:
javascript
// 通过 ID 删除单条记录
function deleteRecord() {
  // 删除ID为10的记录
  return Process('models.category.delete', 10);
}
// 通过变量传入ID
function deleteById(id) {
  return Process('models.category.delete', id);
}Destroy 方法 
用于永久删除单条记录,不支持恢复。
参数说明:
- 参数1:记录ID或主键值
 
返回值:
- 成功:返回被删除记录的条数(通常为1)
 - 失败:抛出异常
 
示例代码:
javascript
// 永久删除指定 ID 的记录
function destroyRecord() {
  // 永久删除ID为9的记录
  return Process('models.category.destroy', 9);
}
// 在事务中使用永久删除
function destroyWithTransaction(id) {
  const q = new Query();
  // 开始事务
  q.Run({
    sql: {
      stmt: 'START TRANSACTION;'
    }
  });
  let success = true;
  let result;
  try {
    // 执行其他操作...
    // 永久删除记录
    result = Process('models.category.destroy', id);
    // 继续其他操作...
  } catch (err) {
    success = false;
    console.log('永久删除操作失败:', err.message);
  }
  // 根据操作结果提交或回滚
  if (success) {
    q.Run({
      sql: {
        stmt: 'COMMIT;'
      }
    });
    return result;
  } else {
    q.Run({
      sql: {
        stmt: 'ROLLBACK;'
      }
    });
    return { success: false, message: '操作失败,已回滚' };
  }
}DeleteWhere 方法 
用于条件批量删除记录,支持软删除。
参数说明:
- 参数1:查询条件对象,格式为 
{ wheres: [...] } 
返回值:
- 成功:返回被删除记录的条数
 - 失败:抛出异常
 
示例代码:
javascript
// 简单条件批量删除
function deleteBySimpleCondition() {
  return Process('models.category.deletewhere', {
    wheres: [
      { column: 'parent_id', value: 4 },
      { column: 'status', value: 'inactive' }
    ]
  });
}
// 使用多种操作符的条件删除
function deleteWithOperators() {
  return Process('models.category.deletewhere', {
    wheres: [
      { column: 'created_at', op: '<', value: '2023-01-01' },
      { column: 'view_count', op: '<=', value: 10 }
    ]
  });
}DestroyWhere 方法 
用于条件批量永久删除记录,不支持恢复。
参数说明:
- 参数1:查询条件对象,格式为 
{ wheres: [...] } 
返回值:
- 成功:返回被删除记录的条数
 - 失败:抛出异常
 
示例代码:
javascript
// 条件批量永久删除
function destroyByCondition() {
  return Process('models.category.destroywhere', {
    wheres: [{ column: 'parent_id', value: 4 }]
  });
}
// 组合条件永久删除
function destroyWithMultipleConditions() {
  return Process('models.category.destroywhere', {
    wheres: [
      { column: 'status', value: 'archived' },
      { column: 'updated_at', op: '<', value: '2022-01-01' }
    ]
  });
}高级用法 
复杂条件删除 
使用多种条件组合和逻辑操作符实现复杂的删除逻辑。
javascript
// 使用复杂条件和逻辑操作符
function complexDelete() {
  return Process('models.category.deletewhere', {
    wheres: [
      { column: 'parent_id', op: '>', value: 4 },
      { column: 'created_at', op: '<', value: '2023-01-01' },
      { method: 'or', column: 'status', value: 'archived' },
      {
        method: 'where',
        wheres: [
          { column: 'view_count', op: '<', value: 100 },
          { column: 'is_public', value: true }
        ]
      }
    ]
  });
}嵌套条件删除 
使用嵌套条件实现更复杂的逻辑组合。
javascript
// 使用嵌套条件组合
function nestedConditionDelete() {
  return Process('models.category.deletewhere', {
    wheres: [
      { column: 'status', value: 'published' },
      {
        method: 'where',
        wheres: [
          { method: 'or', column: 'view_count', op: '<', value: 10 },
          { method: 'or', column: 'comment_count', op: '=', value: 0 }
        ]
      }
    ]
  });
}关联删除 
删除记录时同时处理关联数据。
javascript
// 删除分类时同时删除相关书籍
function deleteWithRelated() {
  return Process('models.category.destroywhere', {
    wheres: [{ column: 'id', value: 1 }],
    withs: ['books'] // 同时删除关联的书籍
  });
}
// 删除前检查关联数据
function safeDeleteWithCheck() {
  // 先查询是否有关联数据
  const hasRelated = Process('models.category.find', 1, {
    withs: { books: { select: ['id'], limit: 1 } }
  });
  // 判断是否有关联书籍
  if (hasRelated.books && hasRelated.books.length > 0) {
    throw new Error('该分类下有关联书籍,请先删除或转移书籍');
  }
  // 安全删除
  return Process('models.category.delete', 1);
}常见模式与最佳实践 
事务中的删除操作 
在事务中执行删除操作,确保数据一致性。
javascript
// 在事务中执行多个删除操作
function transactionalDelete() {
  const q = new Query();
  // 开始事务
  q.Run({
    sql: {
      stmt: 'START TRANSACTION;'
    }
  });
  let hasError = false;
  let categoryResult, tagsResult;
  try {
    // 删除分类
    categoryResult = Process('models.category.delete', 5);
    // 相关数据清理
    tagsResult = Process('models.tag.deletewhere', {
      wheres: [{ column: 'category_id', value: 5 }]
    });
  } catch (err) {
    hasError = true;
    console.log('删除操作失败:', err.message);
  }
  // 根据操作结果提交或回滚事务
  if (hasError) {
    q.Run({
      sql: {
        stmt: 'ROLLBACK;'
      }
    });
    return { success: false, message: '删除操作失败,已回滚' };
  } else {
    q.Run({
      sql: {
        stmt: 'COMMIT;'
      }
    });
    return { success: true, category: categoryResult, tags: tagsResult };
  }
}
// 使用事务进行批量操作的另一个示例
function batchDeleteWithTransaction(ids) {
  const q = new Query();
  // 开始事务
  q.Run({
    sql: {
      stmt: 'START TRANSACTION;'
    }
  });
  let success = true;
  const results = [];
  // 循环删除多个记录
  for (const id of ids) {
    try {
      const result = Process('models.category.delete', id);
      results.push({ id: id, result: result });
    } catch (err) {
      success = false;
      results.push({ id: id, error: err.message });
      break;
    }
  }
  // 根据操作结果提交或回滚
  if (success) {
    q.Run({
      sql: {
        stmt: 'COMMIT;'
      }
    });
    console.log('所有删除操作已提交');
  } else {
    q.Run({
      sql: {
        stmt: 'ROLLBACK;'
      }
    });
    console.log('由于错误,所有删除操作已回滚');
  }
  return { success: success, results: results };
}批量删除与分页处理 
处理大量数据删除时的分页处理模式。
javascript
// 分批删除大量数据
function batchDeleteInChunks() {
  const pageSize = 100;
  let page = 1;
  let totalDeleted = 0;
  // 循环分批删除
  while (true) {
    // 查询一批数据
    const records = Process('models.category.paginate', {
      wheres: [{ column: 'status', value: 'archived' }],
      page: page,
      pagesize: pageSize
    });
    if (!records.data || records.data.length === 0) {
      break; // 没有更多数据,退出循环
    }
    // 提取ID列表
    const ids = records.data.map((item) => item.id);
    // 批量删除
    const result = Process('models.category.destroywhere', {
      wheres: [{ column: 'id', op: 'in', value: ids }]
    });
    totalDeleted += result;
    page++;
    // 可选:添加延迟,减轻数据库压力
    // Process('utils.sleep', 1000); // 暂停1秒
  }
  return totalDeleted;
}错误处理与调试 
javascript
// 带错误处理的删除操作
function safeDelete(id) {
  try {
    const result = Process('models.category.delete', id);
    return { success: true, count: result };
  } catch (err) {
    console.log('删除失败:', err.message);
    return { success: false, error: err.message };
  }
}
// 调试模式删除(先查询后删除)
function debugDelete(id) {
  // 先查询记录是否存在
  const record = Process('models.category.find', id);
  if (!record) {
    return { success: false, message: '记录不存在' };
  }
  console.log('准备删除记录:', JSON.stringify(record));
  // 执行删除
  const result = Process('models.category.delete', id);
  return { success: true, deletedRecord: record, result: result };
}软删除恢复操作 
javascript
// 恢复软删除的记录
function restoreDeleted(id) {
  // 查询包含已删除记录的数据
  const record = Process('models.category.find', id, {
    with_trashed: true // 包含已软删除的记录
  });
  if (!record) {
    return { success: false, message: '记录不存在' };
  }
  // 检查记录是否被软删除
  if (!record.deleted_at) {
    return { success: false, message: '记录未被删除' };
  }
  // 恢复记录(将 deleted_at 设为 null)
  const result = Process('models.category.save', {
    id: id,
    deleted_at: null
  });
  return { success: true, result: result };
}
// 使用自定义条件查询已删除记录
function queryDeletedRecords() {
  return Process('models.category.get', {
    wheres: [
      { column: 'status', value: 'archived' },
创建文件: `modesl/category.yao`
      { column: 'deleted_at', op: 'notnull' } // 明确查询已软删除的记录
    ]
  });
}完整模型定义示例 
json
{
  "name": "书籍分类",
  "table": {
    "name": "category",
    "comment": "书籍分类表"
  },
  "columns": [
    {
      "label": "ID",
      "name": "id",
      "type": "ID",
      "comment": "主键",
      "primary": true
    },
    {
      "label": "分类名称",
      "name": "name",
      "type": "string",
      "length": 50,
      "comment": "分类名称",
      "nullable": false,
      "index": true
    },
    {
      "label": "父分类ID",
      "name": "parent_id",
      "type": "integer",
      "comment": "父分类ID,顶级分类为0",
      "default": 0
    },
    {
      "label": "状态",
      "name": "status",
      "type": "string",
      "comment": "状态:published-已发布,draft-草稿,archived-已归档",
      "default": "published"
    },
    {
      "label": "排序",
      "name": "sort",
      "type": "integer",
      "comment": "排序值,越小越靠前",
      "default": 100
    }
  ],
  "relations": {
    "books": {
      "type": "hasMany",
      "model": "book",
      "key": "category_id",
      "foreign": "id"
    }
  },
  "option": {
    "timestamps": true,
    "soft_deletes": true
  },
  "values": [
    { "id": 1, "name": "技术", "parent_id": 0 },
    { "id": 2, "name": "文学", "parent_id": 0 }
  ]
}注意事项 
软删除特性:
- 软删除记录实际上仍然保存在数据库中,只是设置了
deleted_at不为null - 常规查询不会检索软删除的记录,需要使用自定义条件查询
 - 软删除记录占用数据库空间,长期积累可能影响性能
 
- 软删除记录实际上仍然保存在数据库中,只是设置了
 永久删除风险:
- Destroy相关方法执行的是真正的DELETE操作,数据无法恢复
 - 建议在执行永久删除前进行确认或数据备份
 - 生产环境中谨慎使用批量永久删除
 
批量删除注意事项:
- 大量数据批量删除可能导致数据库锁定和性能问题
 - 建议使用事务并考虑分批处理
 - 批量删除前先估算影响范围,例如使用count方法
 
关联数据处理:
- 删除记录前应考虑关联数据的处理策略
 - 设置适当的外键约束或在代码中处理关联
 - 可以使用事务确保关联数据一致性
 
性能优化:
- 删除大量数据考虑使用原生SQL或分批处理
 - 定期清理软删除数据可以优化数据库性能
 - 为常用查询条件字段添加适当的索引
 
相关主题 
- 模型查询:用于在删除前查询数据
 - 模型关联:了解关联数据的处理方式
 - 数据库事务:确保数据一致性
 - 模型钩子:在删除前后执行自定义逻辑