You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android Studio根据RadioGroup选中项向SQLite分表插入数据异常

SQLite多表插入失败问题排查与修复

核心错误点

一、数据库定义类(DBSQLiteHelper)问题

  • 建表语句错误给表名包裹了单引号:SQLite中单引号是字符串值的边界符,不是标识符边界,最终创建的表名会自带单引号,后续用无引号的表名执行插入根本找不到对应表。
  • 分表结构与外键逻辑错误:已读、待读两张分表的主键设为id_leidos/id_pendientes并关联主表id,但插入分表时从未给该主键字段传值,主键非空约束+外键约束会直接拦截插入操作。
  • 数据库配置常量完全未生效:类中定义的库名常量为LIBROS、版本号为1,但Activity中初始化Helper时传入的库名是librosTotales、版本号是2,实际打开的是完全无关的新数据库,和初始化建表的库不是同一个。
  • 删表逻辑无容错:onUpgrade中执行DROP TABLE未加IF EXISTS判断,升级时如果表不存在会直接抛异常中断执行。

二、Activity业务逻辑问题

  • 控件完全未绑定:类中声明的作者、标题、备注输入框、RadioGroup变量,从未通过findViewById和布局控件绑定,变量全为null,不仅拿不到用户输入值,RadioGroup的选中状态判断也永远不生效,阅读状态标记变量默认恒为false。
  • ContentValues复用未清理:把ContentValues定义在onCreate中全局复用,插入主表时存入的leidoono字段会一直残留,而两张分表根本没有这个字段,insert方法遇到不存在的列会直接返回-1插入失败。
  • 分表插入缺必填值:分表主键关联主表id,但插入主表后从未获取新插入行的id,也没有给分表主键字段传值,直接触发约束报错。
  • 表名大小写错误:注释中插入待读表时写的表名是LibrosPendientes(首字母大写),和定义的小写开头表名不一致,SQLite默认表名大小写敏感,写错表名无法匹配。
  • 字段类型不匹配:主表leidoono字段定义为BOOLEAN类型(SQLite中本质是INTEGER,存0/1),但代码中存入的是"Leído"/"No Leído"字符串,存在类型兼容问题。

三、布局匹配问题

  • 布局中作者输入框ID为@+id/Autor、标题输入框ID为@+id/Titulo、备注输入框ID为@+id/edicion_text,和代码中读取的控件ID不匹配,且RadioGroup未设置ID,无法通过代码获取选中状态。

修复后代码

1. 修正后的DBSQLiteHelper

package Programa

import android.content.Context
import android.database.sqlite.SQLiteDatabase
import android.database.sqlite.SQLiteOpenHelper

class DBSQLiteHelper (context: Context?) :
    SQLiteOpenHelper(context, NAME_DATABASE, null, DATABASE_VERSION) {

    override fun onCreate(db: SQLiteDatabase) {
        // 去掉表名单引号,布尔类型字段用INTEGER存0/1
        db.execSQL(
            "CREATE TABLE $librosTotales ( " +
                    "id INTEGER PRIMARY KEY AUTOINCREMENT," +
                    "titulo TEXT NOT NULL," +
                    "autor TEXT NOT NULL," +
                    "comentario TEXT," +
                    "leidoono INTEGER)"
        )
        // 分表主键直接复用主表id,简化外键逻辑
        db.execSQL(
            "CREATE TABLE $librosLeidos ( " +
                    "id INTEGER PRIMARY KEY REFERENCES $librosTotales(id)," +
                    "titulo TEXT NOT NULL," +
                    "autor TEXT NOT NULL," +
                    "comentario TEXT)"
        )
        db.execSQL(
            "CREATE TABLE $librosPendientes ( " +
                    "id INTEGER PRIMARY KEY REFERENCES $librosTotales(id)," +
                    "titulo TEXT NOT NULL," +
                    "autor TEXT NOT NULL," +
                    "comentario TEXT)"
        )
        // 开启外键约束
        db.execSQL("PRAGMA foreign_keys = ON;")
    }

    override fun onUpgrade(db: SQLiteDatabase, oldVersion: Int, newVersion: Int) {
        // 加IF EXISTS避免删不存在的表报错,先删子表再删主表避免外键拦截
        db.execSQL("DROP TABLE IF EXISTS $librosLeidos")
        db.execSQL("DROP TABLE IF EXISTS $librosPendientes")
        db.execSQL("DROP TABLE IF EXISTS $librosTotales")
        onCreate(db)
    } 

    companion object {
        // 升级版本号为2,触发旧库表结构更新
        private const val DATABASE_VERSION = 2
        private const val NAME_DATABASE = "LIBROS"
        const val librosTotales = "librosTotales"
        const val librosLeidos = "librosLeidos"
        const val librosPendientes = "librosPendientes"
    }
}

2. 修正后的Activity核心逻辑

首先给布局里的RadioGroup补上ID属性:android:id="@+id/radioGroupLeido"

package Programa

import android.content.ContentValues
import android.content.Context
import android.database.sqlite.SQLiteDatabase
import android.os.Bundle
import android.util.Log
import android.view.inputmethod.InputMethodManager
import android.widget.Button
import android.widget.EditText
import android.widget.RadioGroup
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import com.example.organizarlibros.R

class annadirLibro : AppCompatActivity() {

    private lateinit var autor: EditText
    private lateinit var titulo: EditText
    private lateinit var comentario: EditText
    private lateinit var leidoono: RadioGroup
    private lateinit var con: DBSQLiteHelper

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_annadir_libro)

        // 绑定所有控件,ID和布局完全对应
        autor = findViewById(R.id.Autor)
        titulo = findViewById(R.id.Titulo)
        comentario = findViewById(R.id.edicion_text)
        leidoono = findViewById(R.id.radioGroupLeido)
        val buttonAnnadir = findViewById<Button>(R.id.buttonAnnadir)
        con = DBSQLiteHelper(this)

        buttonAnnadir.setOnClickListener {
            val autorStr = autor.text.toString().trim()
            val tituloStr = titulo.text.toString().trim()
            val comentarioStr = comentario.text.toString().trim()

            // 基础输入校验
            if (autorStr.isEmpty() || tituloStr.isEmpty()) {
                Toast.makeText(this, "作者和标题不能为空", Toast.LENGTH_SHORT).show()
                return@setOnClickListener
            }
            if (leidoono.checkedRadioButtonId == -1) {
                Toast.makeText(this, "请选择阅读状态", Toast.LENGTH_SHORT).show()
                return@setOnClickListener
            }

            val isRead = leidoono.checkedRadioButtonId == R.id.leido
            val db = con.writableDatabase
            // 开事务保证主表分表插入原子性,避免数据不一致
            db.beginTransaction()
            try {
                val mainValues = ContentValues().apply {
                    put("autor", autorStr)
                    put("titulo", tituloStr)
                    put("comentario", comentarioStr)
                    put("leidoono", if (isRead) 1 else 0)
                }
                // 插入主表,获取自增主键id
                val mainRowId = db.insert(DBSQLiteHelper.librosTotales, null, mainValues)
                if (mainRowId == -1L) throw Exception("主表插入失败")

                val subValues = ContentValues().apply {
                    put("id", mainRowId) // 分表主键传主表id,满足外键约束
                    put("autor", autorStr)
                    put("titulo", tituloStr)
                    put("comentario", comentarioStr)
                }

                val subInsertResult = if (isRead) {
                    db.insert(DBSQLiteHelper.librosLeidos, null, subValues)
                } else {
                    db.insert(DBSQLiteHelper.librosPendientes, null, subValues)
                }
                if (subInsertResult == -1L) throw Exception("分表插入失败")

                db.setTransactionSuccessful()
                Toast.makeText(this, "书籍添加成功", Toast.LENGTH_LONG).show()
                // 清空输入框
                autor.text.clear()
                titulo.text.clear()
                comentario.text.clear()
                leidoono.clearCheck()
            } catch (e: Exception) {
                Log.e("DB_ERROR", "插入失败", e)
                Toast.makeText(this, "添加失败:${e.message}", Toast.LENGTH_SHORT).show()
            } finally {
                db.endTransaction()
            }

            // 关闭软键盘
            val imm = getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
            imm.hideSoftInputFromWindow(buttonAnnadir.windowToken, 0)
        }
    }
}

注意:改完代码后先卸载手机上的旧版App再重新运行,清空本地残留的错误表结构,否则新的表定义不会生效。

内容的提问来源于stack exchange,提问作者Arcajp

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.28 21:33:44