我的移动应用程序在删除项目时崩溃

问题描述 投票:0回答:1

我正在尝试开发一个移动应用程序来熟悉SQLite数据库。在这里创建,添加方法正在工作。但是当我要在列表视图中删除时,移动应用程序崩溃了。这是我写的代码。

在MainActivity.java中

        lv_customerList.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                CustomerModel clickedCustomer = (CustomerModel) parent.getItemAtPosition(position);
                dataBaseHelper.deleteOne(clickedCustomer);
                extracted(dataBaseHelper);
                Toast.makeText(MainActivity.this, "Deleted" ,Toast.LENGTH_SHORT).show();
            }
        });



    }

    private void extracted(DataBaseHelper dataBaseHelper) {
        customerArrayAdapter = new ArrayAdapter<CustomerModel>(MainActivity.this, android.R.layout.simple_list_item_1, dataBaseHelper.getEveryone());
        lv_customerList.setAdapter(customerArrayAdapter);
    }


}

在databaseHelper.java类中

`

public boolean deleteOne(CustomerModel customerModel){

        SQLiteDatabase db = this.getWritableDatabase();
        String queryString = "DELETE FROM" + CUSTOMER_TABLE + " WHERE " + COLUMN_ID + " = " + customerModel.getId();

        Cursor cursor = db.rawQuery(queryString, null);

        if(cursor.moveToFirst()){
            return true;
        }else {
            return false;
        }
    }
android android-sqlite delete-row mobile-application
1个回答
0
投票

您省略了关键字

FROM
和表名称之间的空格,因此将遇到以下异常:-

 android.database.sqlite.SQLiteException: near "FROMcustomer": syntax error (code 1 SQLITE_ERROR): , while compiling: DELETE FROMcustomer WHERE _id = 9999
  • 请注意,9999 是从客户处提取的任何值。

但是,您总是会遇到从 DeleteOne 方法返回的结果

false
(参见下面的 Demo)。

这是由于两个问题造成的:a)

rawQuery
方法只能用于返回作为输出 SQL 语句提取的数据,即 SELECT 语句或某些 PRAGMA 语句;b) 其他语句(例如 DELETE)不返回任何内容,实际上会导致回滚,撤销他们所做的事情。

您应该使用:-

  1. 一个

    execSQL
    方法,同样不返回结果,

    1. exeSQL
      做该做的事,可能什么也没有,或者失败,或者
  2. 相应的便捷方法,例如

    delete
    删除方法。

    1. 便捷方法,除了执行操作之外,还返回适当的结果:-

      1. insert
        方法返回ROWID表的rowid或其别名
      2. delete
        update
        方法返回受影响的行数(删除或更新的行数)。
    2. 即便捷方法在执行后调用适当的 SQLite API 函数以返回值

演示

以下为示范:-

  1. DeleteOne 方法的工作(至少不会失败)版本,以及
  2. 使用
    execSQL
    方法然后返回有用结果的示例,以及
  3. 建议使用
    delete
    便捷方法

以下是用于 Demo 的 DatabaseHelper 类,该类基于可以从您的代码中确定的内容:-

class DatabaseHelper extends SQLiteOpenHelper {
    public static final String DATABASE_NAME = "the_database";
    public static final int DATABASE_VERSION = 1;

    public static final String CUSTOMER_TABLE = "customer";
    public static final String COLUMN_ID = BaseColumns._ID;
    public static final String COLUMN_WHATEVER = "_whatever";

    private DatabaseHelper(Context context) {
        super(context,DATABASE_NAME,null,DATABASE_VERSION);
    }

    private static volatile  DatabaseHelper instance;
    public static DatabaseHelper getInstance(Context context) {
        if (instance==null) {
            instance = new DatabaseHelper(context);
        }
        return instance;
    }

    @Override
    public void onCreate(SQLiteDatabase db) {
        db.execSQL(
                "CREATE TABLE IF NOT EXISTS " + CUSTOMER_TABLE +"(" +
                COLUMN_ID + " INTEGER PRIMARY KEY" +
                "," + COLUMN_WHATEVER + " TEXT" +
                ");"
        );
    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int i, int i1) {

    }

    public boolean deleteOne(/*CustomerModel customerModel*/ long id){
        SQLiteDatabase db = this.getWritableDatabase();
        String queryString = "DELETE FROM " + CUSTOMER_TABLE + " WHERE " + COLUMN_ID + " = " + /*customerModel.getId()*/ + id;
        Cursor cursor = db.rawQuery(queryString, null);
        if(cursor.moveToFirst()){
            return true;
        }else {
            return false;
        }
    }

    /* Using EXECSQL to return result */
    public long DeleteByExecSQL(long id) {
        SQLiteDatabase db = this.getWritableDatabase();
        long beforeDeletionCount, afterDeletionCount;

        db.beginTransaction();
        beforeDeletionCount = getTableRowCount(CUSTOMER_TABLE,db);
        db.execSQL("DELETE FROM " + CUSTOMER_TABLE + " WHERE " + COLUMN_ID + "=?",new String[]{String.valueOf(id)});
        afterDeletionCount = getTableRowCount(CUSTOMER_TABLE,db);
        db.setTransactionSuccessful();
        db.endTransaction();
        return beforeDeletionCount - afterDeletionCount;
    }

    /* Using Convenience DELETE */
    public long DeleteViaConvenienceMethod(long id) {
        SQLiteDatabase db = this.getWritableDatabase();
        return db.delete(CUSTOMER_TABLE,COLUMN_ID+"=?",new String[]{String.valueOf(id)});
    }

    /* method to get the number of rows in the table */
    public long getTableRowCount(String table, SQLiteDatabase db) {
        long rv=0;
        if (db == null) {
            db = this.getWritableDatabase();
        }
        Cursor csr = db.query(table,new String[]{"count(*)"},null,null,null,null,null);
        if (csr.moveToFirst()) rv = csr.getLong(0);
        csr.close();
        return rv;
    }

    /* Insert using the convenience INSERT method (returns rowid or alias thereof of inserted row) */
    public long insertCustomer(Long id, String whatever) {
        ContentValues cv = new ContentValues();
        if (id!=null) cv.put(COLUMN_ID,id);
        cv.put(COLUMN_WHATEVER,whatever);
        return this.getWritableDatabase().insert(CUSTOMER_TABLE,null,cv);
    }
   /* for generated id */
    public long insertCustomer(String whatever) {
        return insertCustomer(null,whatever);
    }
}
  • 您的
    deleteOne
    方法的更正版本
  • deleteByExecSQL
    方法,使用execSQL方法进行删除,但也通过询问实际删除前后表中的行数并计算差值来返回已删除的行数。
  • 建议
    deleteViaConvenienceMethod
  • 支持上述内容的其他方法。
  • 请参阅评论。

为了实际演示这 3 种方法,请使用以下活动代码:-

public class MainActivity extends AppCompatActivity {

    DatabaseHelper dbh;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        dbh = DatabaseHelper.getInstance(this);


        dbh.insertCustomer("C1");
        dbh.insertCustomer("C2");
        dbh.insertCustomer(100L,"C3");
        dbh.insertCustomer("C4");
        dbh.insertCustomer("C5");

        try {
            Log.d("DBINFO_DLT1_1","DELETED=" + dbh.deleteOne(9999));
            Log.d("DBINFO_DLTE_1","DELETED=" + dbh.deleteByExecSQL(9999));
            Log.d("DBINFO_DLTC_1","DELETED=" + dbh.deleteViaConvenienceMethod(9999));

            Log.d("DBINFO_DLT1_2","DELETED="+dbh.deleteOne(100));
            dbh.insertCustomer(100L,"C3");
            Log.d("DBINFO_DLTE_1","DELETED=" + dbh.deleteByExecSQL(100));
            dbh.insertCustomer(100L,"C3");
            Log.d("DBINFO_DLTC_1","DELETED=" + dbh.deleteViaConvenienceMethod(100));
        } catch (SQLiteException e) {
            e.printStackTrace();
        }
    }
}
  • 插入多行,其中一行的特定 id 为 100(要删除的行)
  • 使用每种删除方法(或尝试删除)的两组:
    • 第一组尝试删除 id 为 9999 的不存在的行
    • 第二组尝试删除 id 为 100 的现有行
      • 删除后再次插入该行
  • 日志记录围绕删除进行,以便可以从日志中提取结果
  • try/catch 可以捕获遇到的任何错误

运行时结果为:-

2023-11-19 12:20:27.995 D/DBINFO_DLT1_1: DELETED=false
2023-11-19 12:20:27.997 D/DBINFO_DLTE_1: DELETED=0
2023-11-19 12:20:27.997 D/DBINFO_DLTC_1: DELETED=0


2023-11-19 12:20:27.998 D/DBINFO_DLT1_2: DELETED=false
2023-11-19 12:20:28.000 D/DBINFO_DLTE_1: DELETED=1
2023-11-19 12:20:28.001 D/DBINFO_DLTC_1: DELETED=1
  • 正如所解释/预期的,即使行被删除,deleteOne 方法也会返回 false(没有输出,因此无法进行 moveToFirst,即 ALWAYS FALSE
  • 另外两个方法返回实际删除的行数
  • 显然,使用方便的
    delete
    方法的代码更简单、更高效(它使用适当的 SQLite 接口),并且不需要重新扫描表来获取计数
    • 按照获取行更改计数是使用 sqlite3_changes() 或 sqlite3_total_changes() 接口。
© www.soinside.com 2019 - 2024. All rights reserved.