【问题标题】:Cause by: android.database.sqlite.SQLiteException no such table: Category (code 1 SQLITE_ERROR): , while compiling: SELECT *FROM Category WHERE tag =?原因:android.database.sqlite.SQLiteException no such table: Category (code 1 SQLITE_ERROR): ,编译时:SELECT *FROM Category WHERE tag =?
【发布时间】:2021-05-27 09:50:36
【问题描述】:

在我的应用程序中,我使用 sqlite db 文件来获取一些数据。它适用于大多数用户,但其中一些用户崩溃原因: android.database.sqlite.SQLiteException: no such table: getCtgList (code 1): ,

编译时:"SELECT *FROM Category WHERE tag =?", new String[]{string}"

下面是我创建和复制数据库的 SQLite DBHelper 类。在 asster/font.db 中有一个 Category 表。复制此表后,某些设备缺少此表。我进行了很多搜索以修复此错误,但没有找到任何解决方案。而且我在谷歌游戏商店上遇到了应用程序崩溃的趋势问题。请帮我解决这个错误。

代码:

package com.softtechbd.nickname_finder.Database;

import android.content.Context;
import android.content.SharedPreferences;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.preference.PreferenceManager;
import android.util.Log;
import android.widget.Toast;

import androidx.annotation.Nullable;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;

public class DBHelper extends SQLiteOpenHelper {
    public static final String location = "/data/data/com.com.softtechbd.stylishnicknamegenerator/databases";
    private static final String DB_Name = "font_data.db";
    private static final int DB_Ver = 2;
    private Context mContext;
    private SQLiteDatabase database;
    private Object String;

    public DBHelper(@Nullable Context context) {
        super(context, DB_Name, null, DB_Ver);
        this.mContext= context;
        initialize();
    }

    private void initialize() {
        if (databaseExists() ){
            SharedPreferences prefs = PreferenceManager
                    .getDefaultSharedPreferences(mContext);
            int dbVersion = prefs.getInt("db_ver", 1);
            if (DB_Ver != dbVersion) {
                File dbFile = mContext.getDatabasePath(DB_Name);
                if (!dbFile.delete()) {
                    Log.w("dbexists", "Unable to update database");
                }
            }
        }
        if (!databaseExists()){
            this.getReadableDatabase();
            try {
                copyDatabase();
            }catch (Exception e){
                e.printStackTrace();
            }
            copyDatabase();
            //By calling this method and empty database will be created into the default system path
            //of your application so we are gonna be able to overwrite that database with our database.

        }
    }

    private void copyDatabase(){
        try {
            InputStream inputStream = mContext.getAssets().open(DB_Name);
            FileOutputStream fileOutputStream = new FileOutputStream(
                    "/data/data/"+mContext.getPackageName()+"/databases/"+DB_Name);
            byte[]arrby = new byte[1024];
            do {
                int n;
                if((n=inputStream.read(arrby)) <=0 ){
                    fileOutputStream.flush();
                    fileOutputStream.close();
                    SharedPreferences.Editor editor = PreferenceManager.getDefaultSharedPreferences((mContext)).edit();
                    editor.putInt("db_ver",DB_Ver);
                    editor.apply();
//                    Toast.makeText(mContext, "SQLite DB copied", Toast.LENGTH_SHORT).show();
                    return;
                }
                fileOutputStream.write(arrby,0,n);
            }while (true);

        }catch (Exception e){
            e.printStackTrace();
        }
    }

    private void closeDatabase(){
        SQLiteDatabase sqLiteDatabase = this.database;
        if(sqLiteDatabase!= null){
            sqLiteDatabase.close();
        }
    }


    private boolean databaseExists() {
        File dbFile = mContext.getDatabasePath(DB_Name);
        return dbFile.exists();
    }

    @Override
    public void onCreate(SQLiteDatabase db) {

    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {

    }
    public void openDatabase(){
        String string = this.mContext.getDatabasePath(DB_Name).getPath();
        SQLiteDatabase sqLiteDatabase = this.database;
        if(sqLiteDatabase!=null && sqLiteDatabase.isOpen()){
            return;
        }
        this.database=SQLiteDatabase.openDatabase(string,null,0);
    }

    public ArrayList<String> getSymbols() {
        ArrayList<String> arrayList = new ArrayList<>();
        openDatabase();
        Cursor cursor = database.rawQuery("SELECT *FROM symbols ", null);
        cursor.moveToFirst();
        while (!cursor.isAfterLast()){
            arrayList.add(cursor.getString( cursor.getColumnIndex("content")));
            cursor.moveToNext();
        }
        cursor.close();
        closeDatabase();
        return arrayList;
    }

    public ArrayList<String> getHotName (){
        ArrayList<String> arrayList = new ArrayList<>();
        openDatabase();
        Cursor cursor = database.rawQuery("SELECT *FROM hot_names", null);
        cursor.moveToFirst();
        while (!cursor.isAfterLast()){
            arrayList.add(cursor.getString( cursor.getColumnIndex("contents")));
            cursor.moveToNext();
        }
        cursor.close();
        closeDatabase();
        return arrayList;
    }

    public ArrayList<String> getList(String string){
        ArrayList<String> arrayList = new ArrayList<>();
        this.openDatabase();
        Cursor cursor = this.database.rawQuery("SELECT * FROM emotication_detail WHERE parent_id =?",new String[]{string});
        cursor.moveToFirst();
        while (!cursor.isAfterLast()){

            arrayList.add( cursor.getString( cursor.getColumnIndex("content")) );
//            item sample = new item(cursor.getInt(0),cursor.getString(1));
            cursor.moveToNext();
        }
        cursor.close();
        this.closeDatabase();
        return arrayList;
    }

    public ArrayList<String> getCtgList (String string){
        ArrayList<String> arrayList = new ArrayList<>();
        openDatabase();
        Cursor cursor = database.rawQuery("SELECT *FROM Category WHERE tag =?", new String[]{string});
        cursor.moveToFirst();
        while (!cursor.isAfterLast()){
            arrayList.add(cursor.getString( cursor.getColumnIndex("content")));
            cursor.moveToNext();
        }
        cursor.close();
        closeDatabase();
        return arrayList;
    }
}

错误日志:

Fatal Exception: android.database.sqlite.SQLiteException: no such table: Category (code 1 SQLITE_ERROR): , while compiling: SELECT *FROM Category WHERE tag =?
       at android.database.sqlite.SQLiteConnection.nativePrepareStatement(SQLiteConnection.java)
       at android.database.sqlite.SQLiteConnection.acquirePreparedStatement(SQLiteConnection.java:903)
       at android.database.sqlite.SQLiteConnection.prepare(SQLiteConnection.java:514)
       at android.database.sqlite.SQLiteSession.prepare(SQLiteSession.java:588)
       at android.database.sqlite.SQLiteProgram.<init>(SQLiteProgram.java:58)
       at android.database.sqlite.SQLiteQuery.<init>(SQLiteQuery.java:37)
       at android.database.sqlite.SQLiteDirectCursorDriver.query(SQLiteDirectCursorDriver.java:46)
       at android.database.sqlite.SQLiteDatabase.rawQueryWithFactory(SQLiteDatabase.java:1408)
       at android.database.sqlite.SQLiteDatabase.rawQuery(SQLiteDatabase.java:1347)
       at com.softtechbd.nickname_finder.Database.DBHelper.getCtgList(DBHelper.java:18)
       at com.softtechbd.nickname_finder.Fragments.pick_category_list.onCreateView(pick_category_list.java:4)
       at androidx.fragment.app.Fragment.R(Fragment.java:15)
       at androidx.fragment.app.FragmentManagerImpl.moveToState(FragmentManagerImpl.java:27)
       at androidx.fragment.app.FragmentManagerImpl.b0(FragmentManagerImpl.java:49)
       at androidx.fragment.app.FragmentManagerImpl.moveToState(FragmentManagerImpl.java:42)
       at androidx.fragment.app.BackStackRecord.executeOps(BackStackRecord.java:6)
       at androidx.fragment.app.FragmentManagerImpl.executeOps(FragmentManagerImpl.java:7)
       at androidx.fragment.app.FragmentManagerImpl.removeRedundantOperationsAndExecute(FragmentManagerImpl.java:88)
       at androidx.fragment.app.FragmentManagerImpl.execPendingActions(FragmentManagerImpl.java:4)
       at androidx.fragment.app.FragmentManagerImpl$2.run(FragmentManagerImpl.java:2)
       at android.os.Handler.handleCallback(Handler.java:873)
       at android.os.Handler.dispatchMessage(Handler.java:99)
       at android.os.Looper.loop(Looper.java:193)
       at android.app.ActivityThread.main(ActivityThread.java:6758)
       at java.lang.reflect.Method.invoke(Method.java)
       at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:497)
       at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:912)

【问题讨论】:

  • 第一次你需要检查数据库是否存在\n 第二次检查日志同时复制数据库\n & 第三次检查表名(拼写)\n 因为错误:QLiteException: no such table: Category (code 1 SQLITE_ERROR)

标签: android sqlite


【解决方案1】:

我建议改变:-

private boolean databaseExists() {
    File dbFile = mContext.getDatabasePath(DB_Name);
    return dbFile.exists();
}

到:-

private boolean databaseExists() {
    File dbFile = mContext.getDatabasePath(DB_Name);
    if (dbFile.exists()) {
        return true;
    }
    dbFile.mkdirs(); //<<<<<<<<<< creates the databases folder
    return false;
}

连同删除行this.getReadableDatabase();

使用getReadableDatabase(或 getWritableDatabase,因为它们在大多数情况下实际上是相同的) 隐藏了真正的潜在问题,即 databases 文件夹不存在结果在副本失败中。也就是说,getReadableDatabase 将创建缺少的 databases 文件夹。

虽然这适用于旧设备,但新设备 (Android 9+) 默认使用预写日志 (WAL)。如果使用 WAL 创建数据库,则会创建两个附加文件,即 WAL 文件(以 -wal 为后缀的数据库文件名)和共享内存文件(以 -shm 为后缀的数据库文件名)。

如果仅覆盖数据库文件(根据 cmets),则 -wal 文件与新数据库不兼容并且尝试打开数据库失败,捕获失败,而是创建并返回一个空的数据库文件因此丢失任何表。 因此您面临的问题

如果数据库文件不存在,则创建 databases 文件夹,无需使用 getReadable 数据库创建 databases 文件夹,因此无需创建 -wal 和 - shm 文件。因此为什么不应该在资产文件的副本之前使用 getReadableDatabase。

  • 或者,您可以在复制之前删除 -wal 和 -shm 文件(如果它们存在)。

即厘米

  • WAL 不是在日志中记录更改以允许它们回滚(日志模式),而是将更改写入 WAL 文件,并在提交时将它们应用于实际数据库,因此 WAL 文件实际上是数据库的一部分(回滚实际上是删除 WAL 文件)。您不妨参考https://sqlite.org/wal.html

我还建议不要在行中对数据库路径进行硬编码

FileOutputStream fileOutputStream = new FileOutputStream("/data/data/"+mContext.getPackageName()+"/databases/"+DB_Name); 

而是使用mContext.getDatabasePath(DB_NAME) 来获取路径。

例如

FileOutputSream fileOutputStrean = new FileOutputStream(mContext.getDatabasePath(DB_NAME));

请注意,以上代码未经测试,可能存在一些错误,而是所传达的原理。

另一种方法是使用 journal_mode pragma 或 SQliteDatabase disableWriteAheadLogging 方法强制日记模式。 https://developer.android.com/reference/android/database/sqlite/SQLiteDatabase.html#disableWriteAheadLogging()

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-08-09
    • 2022-11-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-11-13
    • 2013-05-22
    • 1970-01-01
    相关资源
    最近更新 更多