简体   繁体   中英

Upgrade sqlite database that I copy from assets folder (Android)

I use this tutorial: Tutorial Custom database

I copy database from assets folder and copy it to my device(or emulator). Everything is correct. I see my database in DDMS perspective. But I want aslo upgrade my database sometimes so i did:

super(context, DB_NAME, null, 2); //changed version from 1 to 2

and modify onUpgrade method:

@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
    if(newVersion > oldVersion){
        this.myContext.deleteDatabase(DB_NAME);
        try {
            this.copyDataBase();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

But after run I have still old version of database on my device. How I can delete old version of database and copy new. DB_NAME is my database name(with format, but not path) and copyDataBase() is method that copy database to device(and it's work).

I paste my all code:

public class DataBaseHelper extends SQLiteOpenHelper{

    private static String DB_PATH = "/data/data/sitcom.quiz/databases/";

    private static String DB_NAME = "sitcoms.sqlite";

    private SQLiteDatabase myDataBase; 

    private final Context myContext;

    public DataBaseHelper(Context context) {

        super(context, DB_NAME, null, 4);
        this.myContext = context;
    }   

    public void createDataBase() throws IOException{

        boolean dbExist = checkDataBase();

        if(dbExist){
            //do nothing - database already exist
        }else{

            this.getReadableDatabase();

            try {

                copyDataBase();

            } catch (IOException e) {

                throw new Error("Error copying database");

            }
        }

    }

    private boolean checkDataBase(){

        SQLiteDatabase checkDB = null;

        try{
            String myPath = DB_PATH + DB_NAME;
            checkDB = SQLiteDatabase.openDatabase(myPath, null, SQLiteDatabase.OPEN_READONLY);

        }catch(SQLiteException e){
            //database does't exist yet.
        }

        if(checkDB != null){
            checkDB.close();
        }

        return checkDB != null ? true : false;
    }


    private void copyDataBase() throws IOException{

        //Open your local db as the input stream
        InputStream myInput = myContext.getAssets().open(DB_NAME);

        // Path to the just created empty db
        String outFileName = DB_PATH + DB_NAME;

        //Open the empty db as the output stream
        OutputStream myOutput = new FileOutputStream(outFileName);

        //transfer bytes from the inputfile to the outputfile
        byte[] buffer = new byte[1024];
        int length;
        while ((length = myInput.read(buffer))>0){
            myOutput.write(buffer, 0, length);
        }

        //Close the streams
        myOutput.flush();
        myOutput.close();
        myInput.close();

    }

    public void openDataBase() throws SQLException{

        //Open the database
        String myPath = DB_PATH + DB_NAME;
        myDataBase = SQLiteDatabase.openDatabase(myPath, null, SQLiteDatabase.OPEN_READONLY);

    }

    @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) {
        Log.d("adas", "dasd");

        if(newVersion > oldVersion){
            String myPath = DB_PATH + DB_NAME;
            this.myContext.deleteDatabase(myPath);
            try {
                this.copyDataBase();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }


}

and my activity:

DataBaseHelper myDbHelper = new DataBaseHelper(this);
        myDbHelper = new DataBaseHelper(this);

        try {
            myDbHelper.createDataBase();
        } catch (IOException ioe) {
            throw new Error("Unable to create database");
        }
        try {
            myDbHelper.openDataBase();
        }catch(SQLException sqle){
            throw sqle;
        }

Thanks if you can give me reason or only hint why this don' t work.

android already opened the db when the function onUpgrade() is called. i don't think it is possible to delete it at this time. check the log for more info. if you wanna do it this way, you need to move the delete and copy code to a place where the db is not open yet.

Try this code...I think it will solve your problem.

public void onUpgrade(SQLiteDatabase database, int oldVersion,
        int newVersion) {
    Log.w(DatabaseHelper.class.getName(),
            "Upgrading database from version " + oldVersion  + " to "
                    + newVersion + ", which will destroy all old data");
    database.execSQL("DROP TABLE IF EXISTS " + DATABASE_NAME);
    onCreate(database);
}

When database is copied from assets, by default is version is 0. In your method to copy databse from assets, set its version using method setVersion(int version) for your database object. If db exists, then check its version by method getVersion() on your database object. Now handle its onUpgrade yourself ie if db evrsion has been updated noe, then upgrade database and then setVersion(int new dbVersion) .

You can delete old Db before upgrading DB.

    if(dbExist){
                Log.v("com.db","db exists");
                myContext.deleteDatabase(DB_NAME);`enter code here`
                //openDataBase();
                //do nothing - database already exist
            }else{
                Log.v("com.db","dbnot exists");
                //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.
                this.getReadableDatabase();
}

Just an addition:

in createDataBase() there is the following check;

this.getReadableDatabase();

This checks if there is already a database with the provided name and if not creates an empty database such that it can be overwritten with the one in the assets folder. On newer devices this works flawlessly but there are some devices on which this doesn't work. Mainly older devices. I do not know exactly why, but it seems like the getReadableDatabase() function not only gets the database but also opens it. If you then copy the database from the assets folder over it, it still has the pointer to an empty database and you will get table does not exist errors.

So in order to make it work on all devices you should modify it to the following lines:

SQLiteDatabase db = this.getReadableDatabase();
if (db.isOpen()){
    db.close();
}

Even if the database is opened in the check, it is closed thereafter and it will not give you any more trouble.

The main idea is you should use SharedPreferences to store your database version whenever you create the new database.
After that, when you create database, you must check if database have new version then you delete old database and create new one
This code work with me

private static class DatabaseHelper extends SQLiteOpenHelper {

        private static final String DATABASE_NAME = "database.name";
        private static final int DATABASE_VERSION = 1;
        private static final String KEY_DB_VER = "database_version";
        private final Context mContext;

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

        /**
         * Initializes database. Creates database if doesn't exist.
         */
        private void initialize() {
            if (databaseExists()) {
                SharedPreferences prefs = PreferenceManager
                        .getDefaultSharedPreferences(mContext);
                int dbVersion = prefs.getInt(KEY_DB_VER, 1);
                //
                if (DATABASE_VERSION != dbVersion) {
                    File dbFile = mContext.getDatabasePath(DATABASE_NAME);
                    // delete the old databse   
                    if (!dbFile.delete()) {
                        Log.w(TAG, "Unable to update database");
                    }
                }
            }
            // create database if needed
            if (!databaseExists()) {
                createDatabase();
            }
        }

        /**
         * Returns true if database file exists, false otherwise.
         * @return
         */
        private boolean databaseExists() {
            File dbFile = mContext.getDatabasePath(DATABASE_NAME);
            return dbFile.exists();
        }

        public void createDataBase() throws IOException {
            // If database not exists copy it from the assets
            boolean mDataBaseExist = databaseExists();

            if (!mDataBaseExist) {
                this.getReadableDatabase();
                this.close();
                try {
                    // Copy the database from assests
                    copyDataBase();

                    //**save the database version by SharedPreferences**

                    SharedPreferences prefs = PreferenceManager
                            .getDefaultSharedPreferences(mContext);
                    SharedPreferences.Editor editor = prefs.edit();
                    editor.putInt(KEY_DB_VER, DATABASE_VERSION);
                    editor.commit();

                    Log.e(TAG, "createDatabase database created");

                } catch (IOException mIOException) {
                    throw new Error("ErrorCopyingDataBase");
                }
            }
        }

        // Copy the database from assets
        private void copyDataBase() throws IOException {
            Log.i("TAG", "copy database");
            InputStream mInput = mContext.getAssets().open(DB_NAME);
            String outFileName = DB_PATH + DB_NAME;
            OutputStream mOutput = new FileOutputStream(outFileName);
            byte[] mBuffer = new byte[1024];
            int mLength;
            while ((mLength = mInput.read(mBuffer)) > 0) {
                mOutput.write(mBuffer, 0, mLength);
            }
            mOutput.flush();
            mOutput.close();
            mInput.close();
        }

        @Override
        public void onCreate(SQLiteDatabase db) {
        }

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM