sql >> データベース >  >> RDS >> SQLite

事前入力されたデータベースはAPI28で機能せず、そのようなテーブル例外はスローされません

    SQLiteを使用し、既存のデータベースをコピーしてAPI 28で突然機能しなくなるアプリの一般的な原因は、データベースの問題を回避することです。 フォルダが存在しない場合(ディレクトリが存在しない場合はコピーが失敗します)、空のデータベースを作成してからデータベースを上書きします。

    ただし、デフォルトでは、 API 28から、SDKはWAL(先行書き込みログ)を使用します また、上書きする空のデータベースを作成すると、-shmファイルと-walファイルが作成されます。コピー後にデータベースが空になるのは、これらのファイルの存在です。

    • これは、コピーされたデータベースが開かれると、ミスマッハが検出され、SDKのメソッドが空の使用可能なデータベースを作成するためだと思います(これは推測であり、実際には表示されていません)。

    クイックフィックス

    迅速ですが、推奨されない修正は、 onConfigureをオーバーライドすることです。 disableWriteAheadLoggingを使用するためにSQLiteOpenHelperをサブクラス化するクラスのメソッド データベースがジャーナルモードで開かれるようにする方法。

    • 以下の完全なコード(2番目のコード)にはこれが含まれていますが、その行はコメント化されています。

    推奨される修正

    WALの利点を活用するために推奨される方法は、上書きするデータベースを作成するのではなく、データベースディレクトリの存在を確認し、存在しない場合はディレクトリを作成することです(したがって-データベースのコピー時にshmおよび-walファイルは存在しません

    以下は、データベースが存在するかどうかを確認するときにディレクトリを確認/作成する方法の例です(明らかにこれはそれに応じて調整する必要があります ):-

    private boolean checkDataBase() {
        /**
         * Does not open the database instead checks to see if the file exists
         * also creates the databases directory if it does not exists
         * (the real reason why the database is opened, which appears to result in issues)
         */
    
        File db = new File(myContext.getDatabasePath(DB_NAME).getPath()); //Get the file name of the database
        if (db.exists()) return true; // If it exists then return doing nothing
    
        // Get the parent (directory in which the database file would be)
        File dbdir = db.getParentFile();
        // If the directory does not exits then make the directory (and higher level directories)
        if (!dbdir.exists()) {
            db.getParentFile().mkdirs();
            dbdir.mkdirs();
        }
        return false;
    }
    
    • これは、データベース名(データベースファイルのファイル名)である変数DB_NAMEに依存し、データベースの最終的な場所は標準の場所(data / data / the_package / databases /)であることに注意してください。

    上記は、SQLiteOpenHelperの次のサブクラスから抽出されました:-

    public class DBHelper extends SQLiteOpenHelper {
    
        private static String DB_NAME = "db";
        private SQLiteDatabase myDataBase;
        private final Context myContext;
    
        private int bytes_copied = 0;
        private static int buffer_size = 1024;
        private int blocks_copied = 0;
    
        public DBHelper(Context context) {
            super(context, DB_NAME, null, 1);
    
            this.myContext = context;
            // Check for and create (copy DB from assets) when constructing the DBHelper
            if (!checkDataBase()) {
                bytes_copied = 0;
                blocks_copied = 0;
                createDataBase();
            }
        }
    
        /**
         * Creates an empty database on the system and rewrites it with your own database.
         * */
        public void createDataBase() {
    
            boolean dbExist = checkDataBase(); // Double check
            if(dbExist){
                //do nothing - database already exists
            } else {
                //By calling this method an 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.
                //this.getReadableDatabase();
                //<<<<<<<<<< Dimsiss the above comment
                //By calling this method an empty database IS NOT created nor are the related -shm and -wal files
                //The method that creates the database is flawed and was only used to resolve the issue
                //of the copy failing in the absence of the databases directory.
                //The dbExist method, now utilised, checks for and creates the database directory, so there
                //is then no need to create the database just to create the databases library. As a result
                //the -shm and -wal files will not exist and thus result in the error associated with
                //Android 9+ failing with due to tables not existining after an apparently successful
                //copy.
                try {
                    copyDataBase();
                } catch (IOException e) {
                    File db = new File(myContext.getDatabasePath(DB_NAME).getPath());
                    if (db.exists()) {
                        db.delete();
                    }
                    e.printStackTrace();
                    throw new RuntimeException("Error copying database (see stack-trace above)");
                }
            }
        }
    
        /**
         * Check if the database already exist to avoid re-copying the file each time you open the application.
         * @return true if it exists, false if it doesn't
         */
        private boolean checkDataBase() {
            /**
             * Does not open the database instead checks to see if the file exists
             * also creates the databases directory if it does not exists
             * (the real reason why the database is opened, which appears to result in issues)
             */
    
            File db = new File(myContext.getDatabasePath(DB_NAME).getPath()); //Get the file name of the database
            Log.d("DBPATH","DB Path is " + db.getPath()); //TODO remove for Live App
            if (db.exists()) return true; // If it exists then return doing nothing
    
            // Get the parent (directory in which the database file would be)
            File dbdir = db.getParentFile();
            // If the directory does not exits then make the directory (and higher level directories)
            if (!dbdir.exists()) {
                db.getParentFile().mkdirs();
                dbdir.mkdirs();
            }
            return false;
        }
    
        /**
         * Copies your database from your local assets-folder to the just created empty database in the
         * system folder, from where it can be accessed and handled.
         * This is done by transfering bytestream.
         * */
        private void copyDataBase() throws IOException {
    
            final String TAG = "COPYDATABASE";
    
            //Open your local db as the input stream
            Log.d(TAG,"Initiated Copy of the database file " + DB_NAME + " from the assets folder."); //TODO remove for Live App
            InputStream myInput = myContext.getAssets().open(DB_NAME); // Open the Asset file
            String dbpath = myContext.getDatabasePath(DB_NAME).getPath();
            Log.d(TAG,"Asset file " + DB_NAME + " found so attmepting to copy to " + dbpath); //TODO remove for Live App
    
            // Path to the just created empty db
            //String outFileName = DB_PATH + DB_NAME;
            //Open the empty db as the output stream
            File outfile = new File(myContext.getDatabasePath(DB_NAME).toString());
            Log.d("DBPATH","path is " + outfile.getPath()); //TODO remove for Live App
            //outfile.setWritable(true); // NOT NEEDED as permission already applies
            //OutputStream myoutputx2 = new FileOutputStream(outfile);
            /* Note done in checkDatabase method
            if (!outfile.getParentFile().exists()) {
                outfile.getParentFile().mkdirs();
            }
            */
    
            OutputStream myOutput = new FileOutputStream(outfile);
            //transfer bytes from the inputfile to the outputfile
            byte[] buffer = new byte[buffer_size];
            int length;
            while ((length = myInput.read(buffer))>0) {
                blocks_copied++;
                Log.d(TAG,"Ateempting copy of block " + String.valueOf(blocks_copied) + " which has " + String.valueOf(length) + " bytes."); //TODO remove for Live App
                myOutput.write(buffer, 0, length);
                bytes_copied += length;
            }
            Log.d(TAG,
                    "Finished copying Database " + DB_NAME +
                            " from the assets folder, to  " + dbpath +
                            String.valueOf(bytes_copied) + "were copied, in " +
                            String.valueOf(blocks_copied) + " blocks of size " +
                            String.valueOf(buffer_size) + "."
            ); //TODO remove for Live App
            //Close the streams
            myOutput.flush();
            myOutput.close();
            myInput.close();
            Log.d(TAG,"All Streams have been flushed and closed."); //TODO remove for Live App
        }
    
    
        @Override
        public synchronized void close() {
            if(myDataBase != null)
                myDataBase.close();
            super.close();
        }
    
        @Override
        public void onCreate(SQLiteDatabase db) {
        }
    
        @Override
        public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        }
    
        @Override
        public void onConfigure(SQLiteDatabase db) {
            super.onConfigure(db);
            Log.d("DBCONFIGURE","Database has been configured "); //TODO remove for Live App
            //db.disableWriteAheadLogging(); //<<<<<<<<<< un-comment to force journal mode
        }
    
        @Override
        public void onOpen(SQLiteDatabase db) {
            super.onOpen(db);
            Log.d("DBOPENED","Database has been opened."); //TODO remove for live App
        }
    }
    
    • 上記のコードは開発/実験を目的としているため、削除できるコードが含まれていることに注意してください。



    1. Entity FrameworkのOperatorのように?

    2. PostgreSQL ROLE(ユーザー)が存在しない場合は作成します

    3. AzureSQLデータベースでの自動インデックス管理

    4. 19データベース設計エラーについて学ぶためのオンラインリソース