尧图网络科技YAOTU DIGITAL 获取报价
获取报价
首页 / 资讯中心 / 文章详情

Android SQLite 增删改查实战:用 TaoToken 统一 Key 打通 AI 辅助建表与 CRUD 调试

发布时间:2026/9/26 13:24:14

资讯中心
01
ARTICLE

Android SQLite 增删改查实战:用 TaoToken 统一 Key 打通 AI 辅助建表与 CRUD 调试

Android SQLite 增删改查实战:用 TaoToken 统一 Key 打通 AI 辅助建表与 CRUD 调试
1. Android SQLite 建表与 CRUD 到底难在哪Android 本地 SQLite 是每个 App 都绕不开的持久化方案它轻量、无需网络、随 App 一起打包适合存联系人、订单草稿、离线缓存这类结构化数据。但真到动手写的时候很多人会卡在三个地方一是SQLiteOpenHelper的onCreate只在数据库第一次创建时执行改表结构忘了升版本号结果新字段死活不生效二是Cursor用完不关、SQLiteDatabase频繁开关跑久了就报SQLiteDatabaseLockedException三是 DAO 里insert、update、delete、rawQuery的写法各不一样参数占位符一多就容易写错。这篇面向需要快速生成 DAO 骨架、又想顺手调试 SQL 的 Android 开发者。我会先给出一套可直接复制的PersonSQLiteOpenHelperPersonDao完整代码覆盖建表、插入、查询、更新、删除五个动作再讲怎么用 TaoToken 的统一 Key 把 AI 辅助工具接进来让它在写 DAO 骨架、排查 SQL 报错、生成测试用例时省掉反复配 Key 的麻烦。适合刚接触 Android 数据存储的新手也适合想把 AI 编码助手接进日常开发流的老手。2. 用 TaoToken 统一 Key 接入 AI 辅助工具写 SQLite 代码时我经常需要 AI 帮我做几件事根据表结构生成 DAO 方法骨架、解释Cursor越界报错、把execSQL改写成ContentValues写法。问题是不同 AI 编码工具各要各的 Key配一次换一个地方很烦。TaoToken 的思路是提供一个统一的 API 入口你申请一个 Key就能在多个支持自定义 Base URL 的工具里复用。它的 API 地址是https://taotoken.net/api兼容常见的 OpenAI 风格调用格式。你需要在官网注册后到控制台创建 API Key然后把它填进工具的配置里。对于 Android 开发场景最实用的两个入口是模型对话用来问 SQL 语法、贴报错让它分析和 Coding Plan用来做长期的代码补全和 Agent 式改代码。如果你用的是 Claude Code 这类工具TaoToken 也提供了对应的接入文档。注意TaoToken 是 API 接入服务不是编辑器替代品你的 Android Studio、Gradle、SQLite 调试流程都不变只是把 AI 工具的请求地址指向它。具体操作路径先访问官网https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content注册账号进控制台创建 Key然后按你要用的工具去接入文档里找对应的settings.json或环境变量配置方式。下面第三节我会给出一个可直接复制的配置片段。3. 可复制的 SQLiteOpenHelper 与 DAO 配置3.1 建表PersonSQLiteOpenHelper先看数据库创建类。核心是构造函数里传数据库名和版本号onCreate里写建表语句。这里用person.db、版本 1表结构是id自增主键、name、number两个文本字段。package com.examp.use_SQLite; import android.content.Context; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper; public class PersonSQLiteOpenHelper extends SQLiteOpenHelper { public PersonSQLiteOpenHelper(Context context) { super(context, person.db, null, 1); } Override public void onCreate(SQLiteDatabase db) { db.execSQL(create table person (id integer primary key autoincrement, name varchar(20), number varchar(20))); } Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { // 版本升级时在这里做表结构迁移 } }这里有个坑要提前说onCreate只在数据库文件不存在时调用一次。你如果改了建表语句但版本号还是 1App 重装前新表结构不会生效。调试阶段最省事的做法是卸载 App 或清应用数据让数据库重建。3.2 实体类 Person实体类就是三个字段加 getter/settertoString方便测试时打印。package com.examp.use_SQLite.domain; public class Person { private int id; private String name; private String number; public Person() {} public Person(int id, String name, String number) { this.id id; this.name name; this.number number; } public int getId() { return id; } public void setId(int id) { this.id id; } public String getName() { return name; } public void setName(String name) { this.name name; } public String getNumber() { return number; } public void setNumber(String number) { this.number number; } Override public String toString() { return Person [id id , name name , number number ]; } }3.3 DAO 骨架增删改查五个方法DAO 是重点。插入用ContentValues是官方推荐写法查询用rawQuery拿Cursor更新和删除用execSQL带占位符。注意每个方法里db.close()的位置以及Cursor的关闭。package com.examp.use_SQLite.dao; import java.util.ArrayList; import java.util.List; import android.content.ContentValues; import android.content.Context; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import com.examp.use_SQLite.PersonSQLiteOpenHelper; import com.examp.use_SQLite.domain.Person; public class PersonDao { private PersonSQLiteOpenHelper helper; public PersonDao(Context context) { helper new PersonSQLiteOpenHelper(context); } // 增 public void add(String name, String number) { SQLiteDatabase db helper.getWritableDatabase(); ContentValues values new ContentValues(); values.put(name, name); values.put(number, number); db.insert(person, null, values); db.close(); } // 查按姓名 public Person findByName(String name) { SQLiteDatabase db helper.getReadableDatabase(); Cursor cursor db.rawQuery(select * from person where name?, new String[]{name}); Person person null; if (cursor.moveToNext()) { int id cursor.getInt(cursor.getColumnIndex(id)); String named cursor.getString(cursor.getColumnIndex(name)); String number cursor.getString(cursor.getColumnIndex(number)); person new Person(id, named, number); } cursor.close(); db.close(); return person; } // 改 public void update(String name, String newnumber) { SQLiteDatabase db helper.getWritableDatabase(); db.execSQL(update person set number? where name?, new Object[]{newnumber, name}); db.close(); } // 删 public void delete(String name) { SQLiteDatabase db helper.getWritableDatabase(); db.execSQL(delete from person where name?, new Object[]{name}); db.close(); } // 查全部 public ListPerson findAll() { SQLiteDatabase db helper.getReadableDatabase(); Cursor cursor db.rawQuery(select * from person, null); ListPerson persons new ArrayListPerson(); while (cursor.moveToNext()) { int id cursor.getInt(cursor.getColumnIndex(id)); String named cursor.getString(cursor.getColumnIndex(name)); String number cursor.getString(cursor.getColumnIndex(number)); persons.add(new Person(id, named, number)); } cursor.close(); db.close(); return persons; } }3.4 测试类与 Manifest 配置用AndroidTestCase跑单元测试每个方法对应一个 CRUD 动作。Manifest 里要加instrumentation和uses-library否则测试跑不起来。package com.examp.use_SQLite.test; import java.util.List; import com.examp.use_SQLite.PersonSQLiteOpenHelper; import com.examp.use_SQLite.dao.PersonDao; import com.examp.use_SQLite.domain.Person; import android.test.AndroidTestCase; public class TestPerson extends AndroidTestCase { public void testCreateDB() throws Exception { PersonSQLiteOpenHelper helper new PersonSQLiteOpenHelper(getContext()); helper.getWritableDatabase(); } public void testAdd() { PersonDao dao new PersonDao(getContext()); dao.add(donghongyu, 1231231); } public void testFindByname() { PersonDao dao new PersonDao(getContext()); Person person dao.findByName(lisi); System.out.println(person.toString()); } public void testUpdate() { PersonDao dao new PersonDao(getContext()); dao.update(lisi, 11111111111); } public void testDelete() { PersonDao dao new PersonDao(getContext()); dao.delete(lisi); } public void testFindAll() { PersonDao dao new PersonDao(getContext()); ListPerson persons dao.findAll(); for (Person person : persons) { System.out.println(person); } } }Manifest 关键片段manifest xmlns:androidhttp://schemas.android.com/apk/res/android packagecom.examp.use_SQLite android:versionCode1 android:versionName1.0 instrumentation android:nameandroid.test.InstrumentationTestRunner android:targetPackagecom.examp.use_SQLite / uses-sdk android:minSdkVersion8 android:targetSdkVersion18 / application android:allowBackuptrue android:icondrawable/ic_launcher android:labelstring/app_name android:themestyle/AppTheme uses-library android:nameandroid.test.runner / activity android:namecom.examp.use_SQLite.MainActivity android:labelstring/app_name intent-filter action android:nameandroid.intent.action.MAIN / category android:nameandroid.intent.category.LAUNCHER / /intent-filter /activity /application /manifest3.5 TaoToken 的 settings.json 配置片段如果你用支持自定义 Base URL 的 AI 编码工具配置大致长这样。把api_key换成你在控制台创建的那个 Keybase_url指向 TaoToken 的 API 地址。{ ai_provider: { base_url: https://taotoken.net/api, api_key: sk-你的TaoToken密钥, model: claude-sonnet } }配好之后你在工具里问「帮我给 person 表加一个 age 字段并生成迁移代码」或者把CursorIndexOutOfBoundsException贴进去让它分析请求都会走 TaoToken 这个统一入口。需要长期做代码补全和 Agent 改代码的话可以看 Coding Plan 的接入方式只是临时问 SQL 语法用模型对话就够了。4. 验证请求与预期结果代码写完要验证。最直接的方式是跑TestPerson里的测试方法看 Logcat 输出。按下面顺序执行每一步都有明确的预期结果。第一步跑testCreateDB。预期数据库person.db在/data/data/com.examp.use_SQLite/databases/下生成Logcat 无异常。如果报no such table说明onCreate没执行检查数据库名和版本号。第二步跑testAdd插入donghongyu / 1231231。预期无异常数据库里多一行。可以紧接着跑testFindAll预期打印出Person [id1, namedonghongyu, number1231231]。第三步跑testFindByname查lisi。如果之前没插过 lisi预期person为 null打印时会抛NullPointerException这是正常的说明查询逻辑走到了「无结果」分支。想看到结果先testAdd插一条 lisi 再查。第四步跑testUpdate把 lisi 的 number 改成11111111111。预期无异常再跑testFindByname(lisi)时 number 变成新值。第五步跑testDelete删 lisi。预期无异常再查 lisi 返回 nulltestFindAll里不再有 lisi。如果你用 AI 工具辅助可以把每一步的 Logcat 输出贴给模型对话让它判断是否符合预期。比如贴Person [id1, namedonghongyu, number1231231]问「这个输出说明插入和查询都成功了吗」它会帮你确认。5. 本篇常见错误排查5.1 no such table: person最常见。原因通常是onCreate没被调用或者数据库版本号没变但表结构改了。排查确认PersonSQLiteOpenHelper构造函数里的数据库名和onCreate里的表名一致调试阶段直接卸载 App 或清应用数据强制重建数据库。如果是在onUpgrade里改表记得把版本号从 1 改成 2。5.2 CursorIndexOutOfBoundsExceptionCursor的游标位置不对。moveToNext()返回 false 时说明没有下一行此时调getInt、getString就会越界。正确写法是先判断if (cursor.moveToNext())或while (cursor.moveToNext())再取值。另外getColumnIndex返回 -1 时也会出问题说明字段名拼错了检查 SQL 里的列名和getColumnIndex的参数是否一致。5.3 SQLiteDatabaseLockedException数据库被占用。常见于多线程同时读写或者上一个SQLiteDatabase没关就开新的。排查确认每个方法里db.close()都执行到了如果多线程访问考虑用单例持有SQLiteOpenHelper而不是每次 new 一个。getWritableDatabase和getReadableDatabase返回的可能是同一个连接不要重复关闭。5.4 测试类跑不起来AndroidTestCase需要在 Manifest 里声明instrumentation和uses-library缺一个都会报InstrumentationTestRunner not found。另外targetPackage要和manifest的package一致。如果用的是新版 Android Studioandroid.test相关 API 已废弃建议迁移到androidx.test但本文的代码在旧项目里仍可直接用。5.5 AI 工具请求失败配了 TaoToken 之后如果请求报 401先检查 Key 是否复制完整、有没有多余空格。报 404 通常是base_url写错了确认是https://taotoken.net/api而不是带其他路径。模型名写错会报 400去接入文档里核对当前支持的模型标识。这些报错都可以直接贴给模型对话让它帮你定位。6. 把 AI 接进 Android 数据层开发流SQLite 的 CRUD 代码本身不复杂难的是表结构一变、字段一多DAO 里几十个方法要同步改手写容易漏。我的做法是把 TaoToken 的 Key 配进 AI 编码工具让它根据PersonSQLiteOpenHelper里的建表语句自动生成 DAO 骨架改表时再让它生成对应的迁移代码和测试用例。这样建表和 CRUD 调试这两块最耗时的活就交出去了。具体入口按你的需求选只是临时问 SQL 语法、贴报错分析用模型对话要长期做代码补全、Agent 式改代码看 Coding PlanKey 的管理和创建在控制台接入细节在接入文档。如果你用 Claude Code也有对应的接入说明。把这些配好Android 本地数据库这块的开发节奏会顺很多。
02
RELATED NEWS

相关资讯

更多网站建设与数字化升级内容

03
WHY YAOTU

想打造同款高转化官网?

懂行业、懂生意,从建站到增长一站式陪跑

◈

场景化定制

不做模板站,围绕你的业务场景量身设计,小众不撞款。

◐

营销型架构

以转化目标组织内容与路径,让官网真正带来询盘。

▲

全周期服务

设计、开发、运营、运维一体,上线只是开始。

免费获取你的建站方案

留下需求,专属顾问 24 小时内为你输出方案建议。