
1. 为什么 SQLiteOpenHelper 的建表与迁移总让人心里没底Android 本地存储这块SQLiteOpenHelper 算是绕不开的老朋友。你写一个类继承它onCreate 里建表onUpgrade 里做版本迁移然后 getWritableDatabase 拿到 SQLiteDatabase 开始增删改查。听起来简单但真正落到项目里坑往往出在两个地方一是建表 SQL 写错字段类型或者漏了约束二是版本升级时迁移脚本没写全导致老用户升级后表结构对不上、数据丢失。我见过不少项目onUpgrade 里直接 drop table 再 create数据全没了用户一升级就炸。也有人把迁移逻辑写在 Activity 里版本号一改就手忙脚乱。更麻烦的是随着表越来越多、字段越来越复杂手写迁移 SQL 的出错概率直线上升。这时候如果有一个统一的 AI 编码通道让模型帮你生成建表语句和迁移脚本再配合一次真实的升级测试心里就踏实多了。这篇就聚焦 Android 本地存储场景把 SQLiteOpenHelper 的 onCreate/onUpgrade 维护流程拆开讲同时给出可复制的 settings.json 与 config.toml 骨架把 TaoToken 作为统一 Key/API 通道接入 AI 编码工具。最后附上验证动作生成迁移 SQL 后跑一次升级测试确认表结构与数据无损。适合正在维护 Android 本地数据库、想用 AI 辅助写迁移脚本的开发者。2. 前置准备TaoToken 统一 Key 与 AI 编码工具接入在开始写 SQLiteOpenHelper 之前先把 AI 编码工具的通道配好。TaoToken 在这里扮演的角色是统一 Key/API 通道你不需要在多个工具之间来回切换 Key也不用担心不同模型供应商的接口差异。官网地址是 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content API 入口是 https://taotoken.net/api 。先到控制台创建一个 API Key然后根据你用的工具选择对应的配置方式。下面给出两个常见工具的骨架配置你可以直接复制修改。2.1 settings.json 骨架适用于 Claude Code 类工具{ env: { ANTHROPIC_BASE_URL: https://taotoken.net/api, ANTHROPIC_AUTH_TOKEN: 你的_TaoToken_API_Key, ANTHROPIC_MODEL: claude-sonnet-4-20250514 }, permissions: { allow: [ Read, Write, Bash ] } }这个配置把 Anthropic 的请求地址指向 TaoToken 的 API 入口Key 填你在控制台生成的那一串。模型名按你实际订阅的填这里只是示例。2.2 config.toml 骨架适用于 Codex 类工具model gpt-4.1 model_provider taotoken [model_providers.taotoken] name TaoToken base_url https://taotoken.net/api env_key TAOTOKEN_API_KEY [model_providers.taotoken.headers] Content-Type application/json环境变量TAOTOKEN_API_KEY在终端里 export 一下或者写进你的 shell 配置文件。这样 AI 编码工具就能通过 TaoToken 统一通道调用模型帮你生成建表 SQL 和迁移脚本。注意API Key 不要硬编码在提交到 Git 的文件里用环境变量或者本地配置文件记得加 .gitignore。3. 可复制配置SQLiteOpenHelper 建表与迁移骨架配置好 AI 通道后回到 Android 项目本身。先写一个基础的 SQLiteOpenHelper 子类把建表和迁移的骨架搭出来。下面这个类包含两个版本v1 建一张 text 表v2 增加一个 category 字段并新建一张 category 表。package com.example.mytest; import android.content.Context; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper; public class MySQLiteOpenHelper extends SQLiteOpenHelper { private static final String DB_NAME text.db; private static final int DB_VERSION 2; public MySQLiteOpenHelper(Context context) { super(context, DB_NAME, null, DB_VERSION); } Override public void onCreate(SQLiteDatabase db) { db.execSQL(CREATE TABLE text ( id INTEGER PRIMARY KEY AUTOINCREMENT, str VARCHAR(20), category VARCHAR(20) DEFAULT default)); db.execSQL(CREATE TABLE category ( id INTEGER PRIMARY KEY AUTOINCREMENT, name VARCHAR(20) NOT NULL)); } Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { if (oldVersion 2) { db.execSQL(ALTER TABLE text ADD COLUMN category VARCHAR(20) DEFAULT default); db.execSQL(CREATE TABLE IF NOT EXISTS category ( id INTEGER PRIMARY KEY AUTOINCREMENT, name VARCHAR(20) NOT NULL)); } } }这里有几个关键点。onCreate 只在数据库第一次创建时调用所以建表语句要写全。onUpgrade 里用oldVersion 2做判断保证从 v1 升到 v2 时执行迁移从 v2 升到 v3 时不会重复执行。ALTER TABLE加字段时给了默认值避免老数据出现 null 导致查询异常。如果你用 AI 工具生成迁移 SQL可以把上面的类作为上下文丢给模型让它帮你补全 v3 的迁移逻辑。比如新增一个索引或者修改字段类型模型会基于现有结构给出建议。生成后不要直接信先跑一次升级测试。4. 验证请求生成迁移 SQL 后跑一次升级测试迁移脚本写完最怕的就是没验证。我试过在 onUpgrade 里写错字段名结果升级后查询直接崩。所以每次改完版本号和迁移逻辑都要跑一次真实的升级测试。4.1 用 AI 生成迁移 SQL 的提示词在 AI 编码工具里你可以这样描述需求当前 SQLiteOpenHelper 版本为 2表 text 有 id、str、category 三个字段 表 category 有 id、name 两个字段。现在要升级到版本 3 需要给 text 表增加一个 created_at INTEGER 字段 并给 category 表的 name 字段加唯一索引。请生成 onUpgrade 中的迁移 SQL。模型返回的 SQL 大概是这样ALTER TABLE text ADD COLUMN created_at INTEGER DEFAULT 0; CREATE UNIQUE INDEX IF NOT EXISTS idx_category_name ON category(name);拿到 SQL 后先别急着合并。写一个测试用例模拟从 v2 升级到 v3 的过程。4.2 升级测试代码public void testUpgradeFromV2ToV3(Context context) { // 先以 v2 版本创建数据库 MySQLiteOpenHelper helperV2 new MySQLiteOpenHelper(context); SQLiteDatabase dbV2 helperV2.getWritableDatabase(); dbV2.execSQL(INSERT INTO text (str, category) VALUES (hello, greeting)); dbV2.close(); // 再以 v3 版本打开触发 onUpgrade MySQLiteOpenHelper helperV3 new MySQLiteOpenHelper(context); SQLiteDatabase dbV3 helperV3.getWritableDatabase(); // 验证新字段存在 Cursor cursor dbV3.rawQuery(PRAGMA table_info(text), null); boolean hasCreatedAt false; while (cursor.moveToNext()) { if (created_at.equals(cursor.getString(cursor.getColumnIndex(name)))) { hasCreatedAt true; } } cursor.close(); // 验证老数据还在 Cursor dataCursor dbV3.rawQuery(SELECT str FROM text WHERE category greeting, null); boolean dataIntact dataCursor.moveToFirst(); dataCursor.close(); dbV3.close(); if (!hasCreatedAt || !dataIntact) { throw new AssertionError(升级测试失败字段缺失或数据丢失); } }跑通这个测试说明迁移脚本至少没把表结构搞坏老数据也还在。实测下来这一步能拦掉大部分低级错误。5. 本篇常见错排查5.1 onCreate 不调用有人改了 DB_VERSION 之后发现 onCreate 没执行以为代码没生效。其实 onCreate 只在数据库文件第一次创建时调用。如果你已经装过 App数据库文件存在了改版本号只会触发 onUpgrade。想重新触发 onCreate卸载重装或者手动删掉数据库文件。5.2 onUpgrade 里重复执行如果 onUpgrade 里没有版本判断每次升级都会执行一遍 ALTER TABLE第二次就会报 duplicate column 错误。正确做法是用if (oldVersion N)包起来每个版本一个判断块。5.3 Cursor 和 SQLiteDatabase 没关原文里特别提了这一点确实重要。Cursor 用完必须 closeSQLiteDatabase 在 Activity 销毁时 close。不然内存泄露时间长了 ANR。可以用 try-with-resources 或者 finally 块保证关闭。5.4 迁移 SQL 在 AI 生成后没验证AI 生成的 SQL 看起来对但字段类型、默认值、索引名可能和你的预期有偏差。一定要跑升级测试用 PRAGMA table_info 检查表结构用查询确认数据没丢。5.5 多版本连续升级用户可能从 v1 直接升到 v3onUpgrade 里要保证 v1 到 v2、v2 到 v3 的迁移都执行。用if (oldVersion 2)和if (oldVersion 3)依次判断不要用 else if 跳过中间版本。6. 把 TaoToken 接入你的 Android 数据库工作流SQLiteOpenHelper 的建表和迁移本质上是一个需要反复验证的工程活。用 TaoToken 统一 Key 通道接入 AI 编码工具后你可以把表结构描述丢给模型让它生成建表 SQL 和迁移脚本然后跑升级测试验证。整个流程下来比手写 SQL 再逐条检查要快不少。如果你在配置 AI 工具时遇到 Key 或接入问题可以到 API Keys 页面重新生成或者翻一下接入文档。想先验证模型生成的 SQL 是否符合预期用模型对话快速试几轮。长期做 Android 编码和 Agent 辅助开发的话Coding Plan 更适合你不用每次单独配 Key。数据库迁移这事宁可多跑一次测试也别让用户升级后丢数据。把升级测试写成单元测试每次改版本号都跑一遍配合 AI 生成的迁移 SQL基本能覆盖大部分场景。