【问题标题】:SQLite database not getting copied (No such a table exists error)SQLite 数据库没有被复制(没有这样的表存在错误)
【发布时间】:2022-01-23 01:16:45
【问题描述】:

我认为我的数据库没有被复制,因为我的 assets 文件夹中的数据库文件为 32KB,而 设备文件资源管理器中显示的文件为 12KB。所以我尝试从设备文件资源管理器上传数据库文件,然后没有错误。请问有人可以帮我解决这个问题吗?

这是我遇到的错误

这是我的 DatabaseHelper 类

public class DatabaseAdapter {

    DatabaseHelper helper;
    SQLiteDatabase db;
    List<Contacts> contactsList = new ArrayList<>();
    List<Station> stationsList = new ArrayList<>();
    List<Tip> tipsList = new ArrayList<>();

    public DatabaseAdapter(Context context) {
        helper = new DatabaseHelper(context);
        db = helper.getWritableDatabase();
    }

    public List<Contacts> getAllContacts() {
        String[] columns = {DatabaseHelper.KEY_NAME, DatabaseHelper.KEY_POSITION, DatabaseHelper.KEY_STATION, DatabaseHelper.KEY_PHONE_NUMBER, DatabaseHelper.KEY_EMAIL};
        Cursor cursor = db.query(DatabaseHelper.TABLE_NAME1, columns, null, null, null, null, null, null);
        while (cursor.moveToNext()) {

            int index2 = cursor.getColumnIndex(DatabaseHelper.KEY_NAME);
            String name = cursor.getString(index2);
            int index3 = cursor.getColumnIndex(DatabaseHelper.KEY_POSITION);
            String position = cursor.getString(index3);
            int index4 = cursor.getColumnIndex(DatabaseHelper.KEY_STATION);
            String station = cursor.getString(index4);
            int index5 = cursor.getColumnIndex(DatabaseHelper.KEY_PHONE_NUMBER);
            String phone_number = cursor.getString(index5);
            int index6 = cursor.getColumnIndex(DatabaseHelper.KEY_EMAIL);
            String email = cursor.getString(index6);

            Contacts contact = new Contacts(name, position, station, phone_number, email);
            contactsList.add(contact);
        }
        return contactsList;
    }

    public List<Station> getAllStations() {
        String[] columns = {DatabaseHelper.KEY_S_NAME, DatabaseHelper.KEY_S_ADDRESS, DatabaseHelper.KEY_S_PHONE_NUMBER, DatabaseHelper.KEY_S_TOTAL_FIGHTERS, DatabaseHelper.KEY_S_TOTAL_VEHICLES, DatabaseHelper.KEY_S_LAT, DatabaseHelper.KEY_S_LON};
        Cursor cursor = db.query(DatabaseHelper.TABLE_NAME2, columns, null, null, null, null, null, null);
        while (cursor.moveToNext()) {

            int index1 = cursor.getColumnIndex(DatabaseHelper.KEY_S_NAME);
            String name = cursor.getString(index1);
            int index2 = cursor.getColumnIndex(DatabaseHelper.KEY_S_ADDRESS);
            String address = cursor.getString(index2);
            int index3 = cursor.getColumnIndex(DatabaseHelper.KEY_S_PHONE_NUMBER);
            String phone_number = cursor.getString(index3);
            int index4 = cursor.getColumnIndex(DatabaseHelper.KEY_S_TOTAL_FIGHTERS);
            int total_f = cursor.getInt(index4);
            int index5 = cursor.getColumnIndex(DatabaseHelper.KEY_S_TOTAL_VEHICLES);
            int total_v = cursor.getInt(index5);
            int index6 = cursor.getColumnIndex(DatabaseHelper.KEY_S_LAT);
            String lat = cursor.getString(index6);
            int index7 = cursor.getColumnIndex(DatabaseHelper.KEY_S_LON);
            String lon = cursor.getString(index7);

            Station station = new Station(name, address, phone_number, total_f, total_v, lat, lon);
            stationsList.add(station);
        }
        return stationsList;
    }

    public List<Tip> getAllTips() {
        String[] columns = {DatabaseHelper.KEY_TIP_TOPIC, DatabaseHelper.KEY_TIP_SUB_TOPIC};
        Cursor cursor = db.query(DatabaseHelper.TABLE_NAME3, columns, null, null, null, null, null, null);
        while (cursor.moveToNext()) {

            int index2 = cursor.getColumnIndex(DatabaseHelper.KEY_TIP_TOPIC);
            String topic = cursor.getString(index2);
            int index3 = cursor.getColumnIndex(DatabaseHelper.KEY_TIP_SUB_TOPIC);
            String sub_topic = cursor.getString(index3);

            Tip tip = new Tip(topic, sub_topic);
            tipsList.add(tip);
        }
        return tipsList;
    }

    private static class DatabaseHelper extends SQLiteOpenHelper {

        private static final String DATABASE_NAME = "Firefighters.db";
        private static final String TABLE_NAME1 = "contact_list";
        private static final String TABLE_NAME2 = "station_list";
        private static final String TABLE_NAME3 = "tips";

        private static final int DATABASE_VERSION = 2;

        private static final String KEY_NAME = "name";
        private static final String KEY_POSITION = "position";
        private static final String KEY_STATION = "station";
        private static final String KEY_PHONE_NUMBER = "phone_number";
        private static final String KEY_EMAIL = "email";

        private static final String KEY_S_NAME = "name";
        private static final String KEY_S_ADDRESS = "address";
        private static final String KEY_S_PHONE_NUMBER = "phone_number";
        private static final String KEY_S_TOTAL_FIGHTERS = "total_fighters";
        private static final String KEY_S_TOTAL_VEHICLES = "total_vehicles";
        private static final String KEY_S_LAT = "lat";
        private static final String KEY_S_LON = "long";

        private static final String KEY_TIP_TOPIC = "tip_topic";
        private static final String KEY_TIP_SUB_TOPIC = "tip_sub_topic";

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

        @Override
        public void onOpen(SQLiteDatabase db) {
            super.onOpen(db);
            db.disableWriteAheadLogging();
        }

        @Override
        public void onCreate(SQLiteDatabase db) {
        }

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

这是我的数据库副本类

public class PreCreateDB {

    public static void copyDB(Context context){
        try{
            String destPath = "/data/data/"+ context.getPackageName()
                    + "/databases";
            File f = new File(destPath);
            if(!f.exists()){
                f.mkdir();
                rawCopy(context.getAssets().open("Firefighters.db"), new FileOutputStream(destPath + "/Firefighters.db"));
            }
        } catch (IOException e){
            e.printStackTrace();
        }
    }

    public static void rawCopy(InputStream inputStream, OutputStream outputStream) throws IOException{
        byte[] buffer = new byte[1024];
        int length;
        while((length = inputStream.read(buffer)) > 0){
            outputStream.write(buffer, 0, length);
        }
        inputStream.close();
        outputStream.close();
    }
}

我就是这样称呼他们的

        PreCreateDB.copyDB(mContext);
        databaseAdapter = new DatabaseAdapter(mContext);
        stationsList = databaseAdapter.getAllStations();
        recyclerView.setHasFixedSize(true);
        layoutManager = new LinearLayoutManager(mContext);
        recyclerView.setLayoutManager(layoutManager);
        stationsAdapter = new StationsAdapter(mContext, stationsList, recyclerView);
        recyclerView.setAdapter(stationsAdapter);

-- 对我有用的解决方案--

我尝试了所有方法,但似乎都不起作用,所以我尝试了这个 SQLiteAssetHelper 库,它成功了!我所做的只是用 SQLiteAssetHelper 扩展数据库助手。这是我现在的数据库助手。

private static class DatabaseHelper extends SQLiteAssetHelper {

        private static final String DATABASE_NAME = "Firefighters.db";
        private static final String TABLE_NAME1 = "contact_list";
        private static final String TABLE_NAME2 = "station_list";
        private static final String TABLE_NAME3 = "tips";

        private static final int DATABASE_VERSION = 2;

        private static final String KEY_NAME = "name";
        private static final String KEY_POSITION = "position";
        private static final String KEY_STATION = "station";
        private static final String KEY_PHONE_NUMBER = "phone_number";
        private static final String KEY_EMAIL = "email";

        private static final String KEY_S_NAME = "name";
        private static final String KEY_S_ADDRESS = "address";
        private static final String KEY_S_PHONE_NUMBER = "phone_number";
        private static final String KEY_S_TOTAL_FIGHTERS = "total_fighters";
        private static final String KEY_S_TOTAL_VEHICLES = "total_vehicles";
        private static final String KEY_S_LAT = "lat";
        private static final String KEY_S_LON = "long";

        private static final String KEY_TIP_TOPIC = "tip_topic";
        private static final String KEY_TIP_SUB_TOPIC = "tip_sub_topic";

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

【问题讨论】:

  • 如果在收盘前添加outputStream.flush(); 会发生什么?您确定数据库中的表名匹配表名吗?我建议在 stationsList = databaseAdapter.getAllStations(); 之前运行基于 SELECT * FROM sqlite_master 的查询;并使用 DatabaseUtils dumpCursor 方法检查哪些组件构成了复制的数据库。 developer.android.com/reference/android/database/…
  • 是的,我没有发现任何表名不匹配,当我从设备文件资源管理器上传数据库时,它运行良好,就像所有数据都被读取和显示一样,但我会尝试添加 outputStream.flush() ;
  • 发现了问题,数据库文件夹/目录存在,因此不尝试复制并创建新的空表(没有您的表)。因此找不到表。使用建议的 PreCreateDB.copyDB 方法更正了答案,但保留了其他答案,因为它们可能会有所帮助。

标签: java android sqlite android-sqlite


【解决方案1】:

更正

您的问题是您有其他数据库/文件(感谢谷歌的东西)。所以数据库文件夹已经存在,因此它不会复制文件,而是会打开一个只有 android_metadata 表的新数据库

尝试使用:-

public static void copyDB(Context context){
    String destPath = context.getDatabasePath("FireFighters.db").getAbsolutePath();
    File f = new File(destPath);
    if(!f.exists()){
        if(!f.getParentFile().exists()) {
            f.getParentFile().mkdirs();
        }
        try {
            rawCopy(context.getAssets().open("Firefighters.db"), new FileOutputStream(destPath + "/Firefighters.db"));
        } catch (IOException e) {
            e.printStackTrace();
        } 
    }
}

所以这会检查数据库文件 (Firefighters.db) 本身是否存在。如果没有,那么它将:-

  1. 如果数据库目录不存在则创建它
  2. 无论数据库目录如何存在,然后复制 Firefighters.db

如果 Firefighters.db 文件存在,则不会复制该文件。

上一个答案(可能不适用)

我怀疑复制的内容不是您所期望的。 12KB 连同消息(未找到表)表示两件事。存在一个有效的数据库,并且它可能包含一些组件。即(表、视图、索引、触发器)(每个将占用至少 4KB(1 页))

首先要尝试卸载应用程序并重新运行以消除现有尝试留下有效但空的组件(表等)数据库。

如果不能解决问题,那么我建议添加以下内容

DatabaseUtils.dumpCursor(databaseAdapter.db.rawQuery("SELECT * FROM sqlite_master",null));

stationsList = databaseAdapter.getAllStations();行前

这将提取数据库中所有组件(表、视图触发器、索引)的列表,例如类似的东西:-

2022-01-23 13:01:13.611 I/System.out: >>>>> Dumping cursor android.database.sqlite.SQLiteCursor@45ef147
2022-01-23 13:01:13.611 I/System.out: 0 {
2022-01-23 13:01:13.612 I/System.out:    type=table
2022-01-23 13:01:13.612 I/System.out:    name=android_metadata
2022-01-23 13:01:13.612 I/System.out:    tbl_name=android_metadata
2022-01-23 13:01:13.612 I/System.out:    rootpage=3
2022-01-23 13:01:13.612 I/System.out:    sql=CREATE TABLE android_metadata (locale TEXT)
2022-01-23 13:01:13.612 I/System.out: }
2022-01-23 13:01:13.612 I/System.out: <<<<<

即以上只有 SQLiteOpenHelper 创建的 android_metadata 表。

请注意,上面是使用您的助手创建的,没有调用 PreCreateDB,copyDB。低端设备资源管理器显示:-

因此它看起来不像 PreCreateDB.copyDB 正在复制文件,或者如果它可能已损坏,在这种情况下 SQLiteOpenHelper 将向您展示一个全新但空的数据库栏 android_metadata 表。

附加

快速测试您的代码,但使用包含其他表的数据库(为了我的方便,而不是在研究您的代码后创建一个)并使用 outStream().flush。

然后使用:-

    PreCreateDB.copyDB(this);
    databaseAdapter = new DatabaseAdapter(this);
    DatabaseUtils.dumpCursor(databaseAdapter.db.rawQuery("SELECT * FROM sqlite_master",null));

结果:-

2022-01-23 13:25:25.439 I/System.out: >>>>> Dumping cursor android.database.sqlite.SQLiteCursor@dda8d74
2022-01-23 13:25:25.440 I/System.out: 0 {
2022-01-23 13:25:25.440 I/System.out:    type=table
2022-01-23 13:25:25.440 I/System.out:    name=kmmAccounts
2022-01-23 13:25:25.440 I/System.out:    tbl_name=kmmAccounts
2022-01-23 13:25:25.440 I/System.out:    rootpage=2
2022-01-23 13:25:25.441 I/System.out:    sql=CREATE TABLE kmmAccounts (id varchar(32) NOT NULL, institutionId varchar(32), parentId varchar(32), lastReconciled timestamp, lastModified timestamp, openingDate date, accountNumber mediumtext, accountType varchar(16) NOT NULL, accountTypeString mediumtext, isStockAccount char(1), accountName mediumtext, description mediumtext, currencyId varchar(32), balance mediumtext, balanceFormatted mediumtext, transactionCount bigint unsigned, PRIMARY KEY (id))
2022-01-23 13:25:25.441 I/System.out: }
2022-01-23 13:25:25.441 I/System.out: 1 {
2022-01-23 13:25:25.441 I/System.out:    type=index
2022-01-23 13:25:25.441 I/System.out:    name=sqlite_autoindex_kmmAccounts_1
2022-01-23 13:25:25.441 I/System.out:    tbl_name=kmmAccounts
2022-01-23 13:25:25.441 I/System.out:    rootpage=3
2022-01-23 13:25:25.441 I/System.out:    sql=null
2022-01-23 13:25:25.441 I/System.out: }
2022-01-23 13:25:25.441 I/System.out: 2 {
2022-01-23 13:25:25.441 I/System.out:    type=table
2022-01-23 13:25:25.441 I/System.out:    name=kmmAccountsPayeeIdentifier
2022-01-23 13:25:25.441 I/System.out:    tbl_name=kmmAccountsPayeeIdentifier
2022-01-23 13:25:25.442 I/System.out:    rootpage=4
2022-01-23 13:25:25.442 I/System.out:    sql=CREATE TABLE kmmAccountsPayeeIdentifier (accountId varchar(32) NOT NULL, userorder smallint unsigned NOT NULL, identifierId varchar(32) NOT NULL, PRIMARY KEY (accountId, userorder))
2022-01-23 13:25:25.442 I/System.out: }
....

设备资源管理器显示:-

因此,添加了刷新的代码可以正常工作。所以它要么排除刷新或有错误的资产文件,我猜资产文件要么不是正确的文件名,要么是空组件,要么已损坏。

损坏的文件

如果它已损坏,则日志应显示为例如(作为 Firfighters.db 复制到资产中的图像):-

2022-01-23 13:37:07.973 E/SQLiteLog: (26) file is not a database
2022-01-23 13:37:07.974 E/DefaultDatabaseErrorHandler: Corruption reported by sqlite on database: /data/user/0/a.a.so70599236javasqlitenorows/databases/Firefighters.db
2022-01-23 13:37:07.974 E/DefaultDatabaseErrorHandler: deleting the database file: /data/user/0/a.a.so70599236javasqlitenorows/databases/Firefighters.db
2022-01-23 13:37:08.013 I/System.out: >>>>> Dumping cursor android.database.sqlite.SQLiteCursor@dda8d74
2022-01-23 13:37:08.014 I/System.out: 0 {
2022-01-23 13:37:08.014 I/System.out:    type=table
2022-01-23 13:37:08.014 I/System.out:    name=android_metadata
2022-01-23 13:37:08.014 I/System.out:    tbl_name=android_metadata
2022-01-23 13:37:08.014 I/System.out:    rootpage=3
2022-01-23 13:37:08.014 I/System.out:    sql=CREATE TABLE android_metadata (locale TEXT)
2022-01-23 13:37:08.014 I/System.out: }
2022-01-23 13:37:08.014 I/System.out: <<<<<

命名资源文件不正确

将资产重命名为 Firefighters.db 以外的任何内容(例如 NotFirefighters.db)会导致日志包括:-

2022-01-23 13:43:18.204 W/System.err: java.io.FileNotFoundException: Firefighters.db
2022-01-23 13:43:18.204 W/System.err:     at android.content.res.AssetManager.nativeOpenAsset(Native Method)
2022-01-23 13:43:18.204 W/System.err:     at android.content.res.AssetManager.open(AssetManager.java:744)
2022-01-23 13:43:18.204 W/System.err:     at android.content.res.AssetManager.open(AssetManager.java:721)
2022-01-23 13:43:18.204 W/System.err:     at a.a.so70599236javasqlitenorows.PreCreateDB.copyDB(PreCreateDB.java:20)
2022-01-23 13:43:18.204 W/System.err:     at a.a.so70599236javasqlitenorows.MainActivity.onCreate(MainActivity.java:27)
2022-01-23 13:43:18.204 W/System.err:     at android.app.Activity.performCreate(Activity.java:7136)
2022-01-23 13:43:18.204 W/System.err:     at android.app.Activity.performCreate(Activity.java:7127)
2022-01-23 13:43:18.205 W/System.err:     at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1271)
2022-01-23 13:43:18.205 W/System.err:     at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2893)
2022-01-23 13:43:18.205 W/System.err:     at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3048)
2022-01-23 13:43:18.205 W/System.err:     at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:78)
2022-01-23 13:43:18.205 W/System.err:     at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:108)
2022-01-23 13:43:18.205 W/System.err:     at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:68)
2022-01-23 13:43:18.205 W/System.err:     at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1808)
2022-01-23 13:43:18.205 W/System.err:     at android.os.Handler.dispatchMessage(Handler.java:106)
2022-01-23 13:43:18.205 W/System.err:     at android.os.Looper.loop(Looper.java:193)
2022-01-23 13:43:18.205 W/System.err:     at android.app.ActivityThread.main(ActivityThread.java:6669)
2022-01-23 13:43:18.205 W/System.err:     at java.lang.reflect.Method.invoke(Native Method)
2022-01-23 13:43:18.205 W/System.err:     at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
2022-01-23 13:43:18.206 W/System.err:     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)
2022-01-23 13:43:18.233 I/System.out: >>>>> Dumping cursor android.database.sqlite.SQLiteCursor@dda8d74
2022-01-23 13:43:18.234 I/System.out: 0 {
2022-01-23 13:43:18.234 I/System.out:    type=table
2022-01-23 13:43:18.234 I/System.out:    name=android_metadata
2022-01-23 13:43:18.234 I/System.out:    tbl_name=android_metadata
2022-01-23 13:43:18.234 I/System.out:    rootpage=3
2022-01-23 13:43:18.234 I/System.out:    sql=CREATE TABLE android_metadata (locale TEXT)
2022-01-23 13:43:18.234 I/System.out: }
2022-01-23 13:43:18.234 I/System.out: <<<<<

所以通过添加sqlite提取和游标转储,卸载应用程序后运行并查看日志应该可以确定原因。

【讨论】:

  • 非常感谢您的宝贵时间,我尝试了您所说的一切,但应用程序一直崩溃,然后我发现了 SQLiteAssetHelper 库,然后在扩展它之后,应用程序现在可以完美运行了!
猜你喜欢
  • 1970-01-01
  • 2019-12-06
  • 2010-12-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-11-01
  • 2016-11-18
相关资源
最近更新 更多