SQLite 插入约束字段时出错 - 为啥?

Posted

技术标签:

【中文标题】SQLite 插入约束字段时出错 - 为啥?【英文标题】:SQLite error inserting constraint field - why?SQLite 插入约束字段时出错 - 为什么? 【发布时间】:2020-04-09 14:31:52 【问题描述】:

我是 android 编程和使用 SQLite 的新手。我正在尝试插入一个名为“menu”的数据库,该数据库有一个名为“items”的表。该表的属性是“名称”和“价格”,它们分别是文本和实数。 (我想为所有代码道歉,但我认为这些代码会有用)。我的“mysqliteHelper”类这样创建表:

package com.example.sqlite;

import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.util.Log;

public class MySQLiteHelper extends SQLiteOpenHelper 

    //Creating the items table
    public  static final String TABLE_ITEMS = "items";
    public  static final String ITEMS_COLUMN_NAME = "name";
    public  static final String ITEMS_PRICE = "price";



    private static final String DATABASE_NAME  = "menu";
    private static final int DATABASE_VERSION  = 1;

    // create table comments(
    //   _id integer primary key autoincrement,
    //   comment text not null );
    private static final String TABLE_CREATE_ITEMS = "create table "
            + TABLE_ITEMS + "( " + ITEMS_COLUMN_NAME + " text primary key, "
            + ITEMS_PRICE + " real not null );";

    public MySQLiteHelper( Context context ) 
        super( context, DATABASE_NAME, null, DATABASE_VERSION );
    

    @Override
    public void onCreate( SQLiteDatabase database ) 
        //Create the items table
        database.execSQL( TABLE_CREATE_ITEMS );
        //Create the combos table
        //database.execSQL(TABLE_CREATE_COMBO);
    

    @Override
    public void onUpgrade( SQLiteDatabase db, int oldVersion, int newVersion ) 
        Log.w( MySQLiteHelper.class.getName( ), "Upgrading database from version "
                + oldVersion + " to " + newVersion + ", which will destroy all old data" );
        db.execSQL( "DROP TABLE IF EXISTS" + TABLE_CREATE_ITEMS );
        //db.execSQL( "DROP TABLE IF EXISTS" + TABLE_CREATE_COMBO );
        onCreate( db );
    

我可以确认数据库“菜单”是在 data/data/package/databases 文件夹中创建的。虽然我无法使用 adb shell 连接到 SQLite,所以我无法确认表模式。单击插入按钮后,我的 datasource.createComment(String, Float) 被调用:

public void onClick (View v) 
    @SuppressWarnings("unchecked")
    //Load up the current values in the adapter into this adapter object
            ArrayAdapter<Comment> adapter = (ArrayAdapter<Comment>)getListAdapter();
    //Set comment to null
    Comment comment = null;
    //Check to see which button was pressed
    switch (v.getId()) 
        case R.id.insert:
            String[] option = new String[] "One", "Two", "Three";
            int index = new Random().nextInt(3);
            //When inserting comment you just have to pass value
            comment = datasource.createComment("TEST", (float) 12.30);
            adapter.add(comment); //Adding to listview
            break;
        case R.id.delete:
            if(getListAdapter().getCount() > 0) 
                comment = (Comment)getListAdapter().getItem(0);
                adapter.remove(comment);//removing from listview
                datasource.deleteComment(comment); //delete from db.
            
            break;
    
    //Updating the adapter
    adapter.notifyDataSetChanged();

通过一些调试(打印语句),我可以确认 database.insert 返回 -1。插入不成功。在我的 Logcat 中打印出以下错误

12-16 23:42:30.002 11063-11063/com.example.sqlite E/SQLiteDatabase: Error inserting price=12.3 name=TEST
    android.database.sqlite.SQLiteConstraintException: error code 19: constraint failed
        at android.database.sqlite.SQLiteStatement.native_executeInsert(Native Method)
        at android.database.sqlite.SQLiteStatement.executeInsert(SQLiteStatement.java:113)
        at android.database.sqlite.SQLiteDatabase.insertWithOnConflict(SQLiteDatabase.java:1718)
        at android.database.sqlite.SQLiteDatabase.insert(SQLiteDatabase.java:1591)
        at com.example.sqlite.CommentsDataSource.createComment(CommentsDataSource.java:47)
        at com.example.sqlite.InsertItems.onClick(InsertItems.java:50)
        at java.lang.reflect.Method.invokeNative(Native Method)
        at java.lang.reflect.Method.invoke(Method.java:511)
        at android.view.View$1.onClick(View.java:3039)
        at android.view.View.performClick(View.java:3511)
        at android.view.View$PerformClick.run(View.java:14105)
        at android.os.Handler.handleCallback(Handler.java:605)
        at android.os.Handler.dispatchMessage(Handler.java:92)
        at android.os.Looper.loop(Looper.java:137)
        at android.app.ActivityThread.main(ActivityThread.java:4424)
        at java.lang.reflect.Method.invokeNative(Native Method)
        at java.lang.reflect.Method.invoke(Method.java:511)
        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:784)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:551)
        at dalvik.system.NativeStart.main(Native Method)

我的“CommentsDataSource”的代码如下。请忽略 createComment() 之后的方法。我正在一次编辑我的教授代码一种方法。

public class CommentsDataSource 

    // Database fields
    //Used toe xecute commands on db.
    private SQLiteDatabase database;
    //Opening up the mysqlhelper which is in charge of opening up connection and maintaining
    private MySQLiteHelper dbHelper;
    //Holds all the values from the database
    private String[] allColumnsItems =  MySQLiteHelper.ITEMS_COLUMN_NAME,
            MySQLiteHelper.ITEMS_PRICE ;

    //Creating the default constructor
    public CommentsDataSource(Context context) 
        //Give context to the class that manages the connection
        dbHelper = new MySQLiteHelper(context);
    

    //Opening up the connction to the db
    public void open() throws SQLException 
        //Open the db for reading and writing
        database = dbHelper.getWritableDatabase(); //getWritableDatabase is what opens the connection
    

    //Closing the database
    public void close() throws SQLException 
        dbHelper.close();
    

    //Creating and commiting comment
    public Comment createComment(String comment, float price) 
        //Insert into comments values('')
        ContentValues values = new ContentValues();
        //Load the column id and value into ContentValues
        values.put(MySQLiteHelper.ITEMS_COLUMN_NAME, comment);
        values.put("price", price);
        //Now insert into table
        long insertID = database.insert(MySQLiteHelper.TABLE_ITEMS, null, values);
        if(insertID == -1) 
            System.out.println("JSDLFJLSJDFL");
        
        //Create a cursor to keep track of results. When creating cursors they equal database.query
        Cursor cursor =  database.query(MySQLiteHelper.TABLE_ITEMS, allColumnsItems, null, null, null, null, null);
        //Move cursor tot he front
        cursor.moveToFirst();
        //Return the comment which is added to listview
        return cursorToComment(cursor);
    

    public void deleteComment(Comment comment) 
        // select id, comment from comments;
        long id = comment.getId();
        //Debug purposes
        System.out.println("Comment to be deleted: " + id);
        //Deleting from db
        database.delete(MySQLiteHelper.TABLE_ITEMS, MySQLiteHelper.ITEMS_COLUMN_NAME + "=" + comment, null);
    

    public List<Comment> getAllComments() 
        //Select * from comments
        List<Comment> comments = new ArrayList<Comment>();
        //Fetching the results of query into cursor
        Cursor cursor = database.query(MySQLiteHelper.TABLE_ITEMS, allColumnsItems, null, null, null, null, null);
        //Move to front of cursor
        cursor.moveToFirst();
        //Iterate through cursor
        while(!cursor.isAfterLast()) 
            Comment comment = cursorToComment(cursor);
            //Add results to arraylist
            comments.add(comment);
            cursor.moveToNext();

        
        cursor.close();
        return comments;
    

    public Comment cursorToComment(Cursor cursor) 
        Comment comment = new Comment();
        comment.setId(cursor.getLong(0));
        comment.setComment(cursor.getString(1));
        return comment;
    


我正在努力找出我的错误在哪里。我的约束应该都是有效的?

【问题讨论】:

【参考方案1】:

查看您的代码后,我可以假设您正在尝试在表格中使用唯一的项目名称,对吧?如果是这样,那么这个错误是显而易见的。在您的表items 中,您将name 作为主键,并且您每次都尝试插入TEST。表中已经存在。

有 3 种方法可以解决这个问题。

    name 列中删除主键约束。但是,您可以在此之后输入重复的名称。

    不要在测试时对值进行硬编码。生成唯一名称并插入它。如下所示

    int index = new Random().nextInt(3);
    comment = datasource.createComment("TEST" + index, (float) 12.30);
    
    // concatenate `index` variable to `TEST` that's it.
    

    从用户 (GUI) 获取输入并将其插入到表格中。

PSnextInt(3) 方法将返回从 0 到 2 的数字。当您测试代码时,您会得到重复的名称。因此,在使用随机数生成器时要小心。我想推荐我的最后一点。

【讨论】:

添加索引似乎已修复它。我完全忽略了这个事实。谢谢!如此简单的修复。 正如其他答案中所建议的,请将带有主键的_id 列添加到您的表中并自动递增。执行DELETEUPDATESELECT 等操作会很有帮助。祝您编码愉快! 谢谢!我打算将评论或“测试”部分作为用户的输入。【参考方案2】:

在这里,您尝试使用文本主键创建表,理想情况下应该是自动增量。这就是它的返回约束失败的原因。

 private static final String TABLE_CREATE_ITEMS = "create table "
        + TABLE_ITEMS + "( " + ITEMS_COLUMN_NAME + " text primary key, "
        + ITEMS_PRICE + " real not null );";

应该是

CREATE TABLE TableName(
_id     INTEGER PRIMARY KEY,      -- autoincrementing, for Android
NAME    TEXT,
[...]);

你可以参考 Inserting values to SQLite table in Android

【讨论】:

啊,是的,我确实忘记了_id,但我仍然遇到同样的错误。【参考方案3】:

请试试这个:

public void onClick (View v) 
@SuppressWarnings("unchecked")
    //Load up the current values in the adapter into this adapter object
            ArrayAdapter<Comment> adapter = (ArrayAdapter<Comment>)getListAdapter();
    //Set comment to null
    Comment comment = new Comment(); //have to change this from null to new Comment();
    //Check to see which button was pressed
    switch (v.getId()) 
        case R.id.insert:
            String[] option = new String[] "One", "Two", "Three";
            int index = new Random().nextInt(3);
            //When inserting comment you just have to pass value
            comment = datasource.createComment("TEST", (float) 12.30);
            adapter.add(comment); //Adding to listview
            break;
        case R.id.delete:
            if(getListAdapter().getCount() > 0) 
                comment = (Comment)getListAdapter().getItem(0);
                adapter.remove(comment);//removing from listview
                datasource.deleteComment(comment); //delete from db.
            
            break;
    
    //Updating the adapter
    adapter.notifyDataSetChanged();

还有你的SQLiteOpenHelper

public class MySQLiteHelper extends SQLiteOpenHelper 

    //Creating the items table
    public  static final String TABLE_ITEMS = "items";
    public  static final String ITEMS_COLUMN_NAME = "name";
    public  static final String ITEMS_PRICE = "price";



    private static final String DATABASE_NAME  = "menu";
    private static final int DATABASE_VERSION  = 1;

    // create table comments(
    //   _id integer primary key autoincrement,
    //   comment text not null );
     private static final String TABLE_CREATE_ITEMS = "CREATE TABLE IF NOT EXISTS " + TABLE_ITEMS + "("
            + ITEMS_AUTO_ID + " INTEGER PRIMARY KEY AUTOINCREMENT," //this is just _id
            + ITEMS_COLUMN_NAME + " VARCHAR,"
            + ITEMS_PRICE + " FLOAT)";

    public MySQLiteHelper( Context context ) 
        super( context, DATABASE_NAME, null, DATABASE_VERSION );
    

    @Override
    public void onCreate( SQLiteDatabase database ) 
        //Create the items table
        database.execSQL( TABLE_CREATE_ITEMS );
        //Create the combos table
        //database.execSQL(TABLE_CREATE_COMBO);
    

    @Override
    public void onUpgrade( SQLiteDatabase db, int oldVersion, int newVersion ) 
        Log.w( MySQLiteHelper.class.getName( ), "Upgrading database from version "
                + oldVersion + " to " + newVersion + ", which will destroy all old data" );
        db.execSQL( "DROP TABLE IF EXISTS" + TABLE_CREATE_ITEMS );
        //db.execSQL( "DROP TABLE IF EXISTS" + TABLE_CREATE_COMBO );
        onCreate( db );
    

【讨论】:

我已经在我的 createComment 方法中使用 ContentViews。基本上我所做的就是打包这些值,然后运行 ​​SQliteDatabase 命令,对吧? 请试试这个。根据阅读***.com/questions/8117685/… .. 有一个空 另外,请删除您的onUpgrade() 代码。如果您更改数据库版本,它将删除数据库(即使您的表上有数据) 不幸的是,我遇到了同样的错误。我也不完全确定如何在 android studio 上正确调试。 尝试检查logcat并点击蓝色链接

以上是关于SQLite 插入约束字段时出错 - 为啥?的主要内容,如果未能解决你的问题,请参考以下文章

如果插入的值与唯一约束冲突,则 SQL 触发器在插入时更新字段

[QT][SQL]sql学习记录3_sqlite 使用

将图片插入sql表时出错

为啥我在 subView 中添加一些约束时会出错?

外键SQL语句的编写

android sqlite禁止重复插入数据