Skip to content

模型删除数据 (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 }
  ]
}

注意事项

  1. 软删除特性

    • 软删除记录实际上仍然保存在数据库中,只是设置了deleted_at不为null
    • 常规查询不会检索软删除的记录,需要使用自定义条件查询
    • 软删除记录占用数据库空间,长期积累可能影响性能
  2. 永久删除风险

    • Destroy相关方法执行的是真正的DELETE操作,数据无法恢复
    • 建议在执行永久删除前进行确认或数据备份
    • 生产环境中谨慎使用批量永久删除
  3. 批量删除注意事项

    • 大量数据批量删除可能导致数据库锁定和性能问题
    • 建议使用事务并考虑分批处理
    • 批量删除前先估算影响范围,例如使用count方法
  4. 关联数据处理

    • 删除记录前应考虑关联数据的处理策略
    • 设置适当的外键约束或在代码中处理关联
    • 可以使用事务确保关联数据一致性
  5. 性能优化

    • 删除大量数据考虑使用原生SQL或分批处理
    • 定期清理软删除数据可以优化数据库性能
    • 为常用查询条件字段添加适当的索引

相关主题

  • 模型查询:用于在删除前查询数据
  • 模型关联:了解关联数据的处理方式
  • 数据库事务:确保数据一致性
  • 模型钩子:在删除前后执行自定义逻辑