ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

SQLite数据库笔记:用TaoToken统一Key打通SQL查询与ListView数据展示

2026/9/28 11:32:58 拓冰建站 浏览量
SQLite数据库笔记:用TaoToken统一Key打通SQL查询与ListView数据展示 1. 从一次真实的 Android 本地数据翻车说起SQLite 是 Android 系统内置的轻量级关系型数据库不需要单独安装服务端一个.db文件就能存下整张表的数据。它能做增删改查、事务、分页适合谁适合所有需要在手机本地持久化结构化数据的场景比如通讯录、记账本、离线缓存列表。而 ListView 是把这些数据渲染到屏幕上的经典控件两者配合就是 Android 入门阶段最核心的一条数据链路建库建表 → SQL 增删改查 → Cursor 读取 → 封装 Bean → Adapter 绑定 → ListView 渲染。我见过太多人卡在中间某一环表建好了但查询返回空 Cursor或者数据明明查出来了 ListView 却一片空白。问题往往不在 SQL 本身而在几个固定位置——onCreate没执行、列名拼错、getCount()返回 0、getView()里没复用 convertView。这篇笔记就按这条链路走一遍交付可直接复制的DBHelper与BaseAdapter骨架同时演示怎么用 TaoToken 的统一 Key 接入 AI 辅助生成和检查 SQL 语句把「写 SQL 靠猜」变成「写 SQL 有验证」。2. TaoToken 前置统一 Key 与 API 通道准备TaoToken 在这里的角色是一个统一的模型调用入口。你不需要为每个模型单独申请一套凭证一个 Key 就能走通对话、代码生成等通道。对于本篇场景我们主要用它来辅助生成 SQL 语句、解释 Cursor 报错、检查建表字段类型是否合理。先拿到凭证。打开官网 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 注册后进入控制台在 API Keys 页面创建一个新 Key。控制台地址是 https://taotoken.net/console?utm_sourcetaotoken_aicg_blog_endutm_contentconsoleutm_campaignrewrite Key 管理页在 https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi-keysutm_campaignrewrite 。创建后立刻复制保存页面刷新后就不再完整显示。接口基地址统一用 https://taotoken.net/api 注意这个地址不带任何查询参数。调用时把 Key 放在请求头的 Authorization 字段里格式是Bearer 你的Key。如果你只是想先在网页上验证模型能不能正常回答 SQL 相关问题可以直接用模型对话页 https://taotoken.net/chat?utm_sourcetaotoken_aicg_blog_endutm_contentchatutm_campaignrewrite 试一句「帮我写一条查询 person 表里 money 大于 5000 的 SQL」看返回是否符合预期再去写代码。注意Key 只存在于你的本地配置或环境变量里不要硬编码进提交到 Git 的源码。Android 项目里可以放在local.properties或gradle.properties通过 BuildConfig 注入。3. 可复制配置DBHelper 与 Adapter 骨架3.1 建库建表MyOpenHelper 完整写法SQLite 在 Android 里通过SQLiteOpenHelper管理数据库的创建和版本升级。必须定义一个构造方法参数依次是上下文、数据库文件名、游标工厂一般传 null、版本号。数据库第一次被创建时会回调onCreate版本号变大时会回调onUpgrade。public class MyOpenHelper extends SQLiteOpenHelper { public MyOpenHelper(Context context, String name, SQLiteDatabase.CursorFactory factory, int version) { super(context, name, factory, version); } Override public void onCreate(SQLiteDatabase db) { db.execSQL(create table person ( _id integer primary key autoincrement, name char(10), phone char(20), money integer(20))); } Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { db.execSQL(drop table if exists person); onCreate(db); } }获取数据库对象时getWritableDatabase()打开可读写库getReadableDatabase()在磁盘空间不足时退化为只读否则同样返回可读写库。实际开发中直接用getWritableDatabase()即可。MyOpenHelper oh new MyOpenHelper(getContext(), person.db, null, 1); SQLiteDatabase db oh.getWritableDatabase();3.2 增删改查execSQL 与 API 两种方式原生 SQL 方式适合复杂语句execSQL支持占位符能防注入db.execSQL(insert into person (name, phone, money) values (?, ?, ?);, new Object[]{张三, 15987461, 75000}); Cursor cs db.rawQuery(select _id, name, money from person where name ?;, new String[]{张三});API 方式更类型安全插入返回主键出错返回 -1删除和修改返回影响行数ContentValues cv new ContentValues(); cv.put(name, 刘能); cv.put(phone, 1651646); cv.put(money, 3500); long rowId db.insert(person, null, cv); int deleted db.delete(person, _id ? and name ?, new String[]{1, 张三}); ContentValues updateCv new ContentValues(); updateCv.put(money, 25000); int updated db.update(person, updateCv, name ?, new String[]{赵四}); Cursor queryCs db.query(person, new String[]{name, money}, name ?, new String[]{张三}, null, null, null); while (queryCs.moveToNext()) { String name queryCs.getString(queryCs.getColumnIndex(name)); String money queryCs.getString(queryCs.getColumnIndex(money)); System.out.println(name ; money); }事务用于保证多条 SQL 要么全成功要么全失败典型场景是转账try { db.beginTransaction(); // 多条 SQL 操作 db.setTransactionSuccessful(); } finally { db.endTransaction(); }3.3 用 TaoToken 辅助生成与检查 SQL写 SQL 时最容易犯的错是列名拼错、类型不匹配、where 条件漏占位符。这时候可以把表结构贴给模型让它生成语句并解释。用 curl 走 TaoToken 的 API 通道curl https://taotoken.net/api/v1/chat/completions \ -H Authorization: Bearer $TAOTOKEN_KEY \ -H Content-Type: application/json \ -d { model: claude-sonnet-4-20250514, messages: [ {role: user, content: 表结构person(_id integer primary key autoincrement, name char(10), phone char(20), money integer(20))。请生成一条查询 money 大于 5000 且按 money 降序排列的 SQL并说明每个字段含义。} ] }返回的 SQL 可以直接粘进rawQuery验证。如果模型给的列名和你的建表语句不一致说明你的表结构描述有歧义这正是提前发现问题的价值。长期做 Android 开发、需要频繁让模型辅助生成代码片段的可以了解 Coding Plan https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding-planutm_campaignrewrite 接入文档在 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite 。3.4 ListView 绑定BaseAdapter 骨架ListView 遵循 MVC 结构Model 是要显示的数据集合View 是 ListView 本身Controller 是 Adapter。每个条目都是一个 View 对象。BaseAdapter 必须实现getCount()和getView()。public class PersonAdapter extends BaseAdapter { private ListPerson people; private Context context; public PersonAdapter(Context context, ListPerson people) { this.context context; this.people people; } Override public int getCount() { return people.size(); } Override public Object getItem(int position) { return people.get(position); } Override public long getItemId(int position) { return position; } Override public View getView(int position, View convertView, ViewGroup parent) { ViewHolder holder; if (convertView null) { convertView LayoutInflater.from(context).inflate(R.layout.item_person, parent, false); holder new ViewHolder(); holder.tvName convertView.findViewById(R.id.tv_name); holder.tvMoney convertView.findViewById(R.id.tv_money); convertView.setTag(holder); } else { holder (ViewHolder) convertView.getTag(); } Person p people.get(position); holder.tvName.setText(p.getName()); holder.tvMoney.setText(String.valueOf(p.getMoney())); return convertView; } static class ViewHolder { TextView tvName; TextView tvMoney; } }从数据库读取全部数据并封装成集合ListPerson people new ArrayList(); Cursor cs db.query(person, null, null, null, null, null, null); while (cs.moveToNext()) { String name cs.getString(cs.getColumnIndex(name)); String phone cs.getString(cs.getColumnIndex(phone)); String money cs.getString(cs.getColumnIndex(money)); people.add(new Person(name, phone, money)); } cs.close(); ListView lv findViewById(R.id.lv); lv.setAdapter(new PersonAdapter(this, people));分页查询用limit参数db.query的最后一个参数传0, 10表示从第 0 条取 10 条。4. 验证请求与成功结果4.1 验证 TaoToken 通道是否通先用一条最小请求确认 Key 和网络都正常curl -s https://taotoken.net/api/v1/chat/completions \ -H Authorization: Bearer $TAOTOKEN_KEY \ -H Content-Type: application/json \ -d {model:claude-sonnet-4-20250514,messages:[{role:user,content:回复 OK}]}返回 JSON 里choices[0].message.content包含内容说明通道正常。如果返回 401检查 Key 是否复制完整返回 404检查路径是否写成了带 UTM 的地址——接口地址就是https://taotoken.net/api不要加多余参数。4.2 验证数据库写入与查询插入一条数据后立刻查回来确认onCreate确实执行了Cursor check db.rawQuery(select count(*) from person, null); if (check.moveToFirst()) { Log.d(SQLiteCheck, 总行数 check.getInt(0)); } check.close();如果这里抛no such table: person说明onCreate没被调用通常是数据库文件已存在但版本号没变或者构造方法参数传错。4.3 验证 ListView 渲染在getView里打一行日志观察调用次数Log.d(ListViewCheck, getView position position convertView (convertView null ? null : reused));屏幕能显示几个条目getView就被调用几次。向下滑动时继续调用但convertView应该开始出现reused说明复用生效。如果每次都是null检查是否在getView里错误地每次都inflate而没有判断convertView null。5. 本篇常见错排查报错一no such table: person原因通常是数据库已存在但表没建。onCreate只在数据库文件第一次创建时调用。解决办法卸载应用重装或把版本号从 1 改成 2 触发onUpgrade或在onUpgrade里先 drop 再 create。报错二Cursor 返回 0 行但表里明明有数据检查where条件的占位符和参数数组是否一一对应。db.query(person, null, name ?, new String[]{张三}, ...)里问号数量必须和数组长度一致。另外注意char类型在 SQLite 里实际按 TEXT 存储比较时不要混用数字和字符串。报错三ListView 一片空白先确认getCount()返回值大于 0。如果集合是空的回到 Cursor 那一步打印行数。如果getCount()正常但界面空白检查item_person.xml的根布局高度是不是wrap_content以及setAdapter是否在数据加载完成之后调用。报错四getView里findViewById返回 null检查inflate的布局文件里控件 id 是否和代码里一致。用 ViewHolder 模式时setTag和getTag要成对出现否则复用时会拿到错误的控件引用。报错五TaoToken 返回 401 或 403Key 失效或格式不对。确认请求头是Authorization: Bearer sk-xxx中间有一个空格。如果 Key 是在控制台刚创建的等几秒再试。模型名写错也会返回错误先用模型对话页确认可用模型名。报错六主线程查询导致 ANR数据量大时db.query放在主线程会卡界面。正确做法是把查询放到子线程查完通过 Handler 或 runOnUiThread 更新 Adapter 并调用notifyDataSetChanged()。6. 继续把这条链路用起来数据库和 ListView 这条链路跑通之后下一步可以做的事很具体把PersonAdapter换成SimpleAdapter或ArrayAdapter对比体验给查询加分页参数做上拉加载把事务用在批量插入上。需要模型帮你生成复杂 SQL 或解释 Cursor 报错时走模型对话 https://taotoken.net/chat?utm_sourcetaotoken_aicg_blog_endutm_contentchatutm_campaignrewrite 最快要在 Android 项目里长期集成代码辅助看 Coding Plan https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding-planutm_campaignrewrite 接入细节和参数说明在文档 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite 。Key 管理和新建在 API Keys https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi-keysutm_campaignrewrite 。接口基地址始终是 https://taotoken.net/api 。