可以使用哪些方法来管理现有数据库的不同版本? [英] Which methods can be used to manage differing versions of pre-existing databases?

查看:73
本文介绍了可以使用哪些方法来管理现有数据库的不同版本?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

注意,根据通常为使用SQLite的Android应用程序提供一个预先存在的数据库.该数据库通常放置在资产文件夹中,如果该数据库不存在,则应用程序将复制该数据库.

Often a pre-existing database is provided for an Android App that utilises SQLite. The database is often placed into the assets folder and the App will copy the database if it does not exist.

对于较新版本的App,更改资产文件夹中的数据库很好,因为将复制更改的数据库.但是,从早期版本升级App时,数据库将存在,因此将不会被复制,也无法轻松地将其无缝删除以从资产文件夹中进行复制.

For a newer version of the App changing the database in the assets folder is fine as the changed database will be copied. However, when upgrading an App from a previous version the database will exist and therefore it will not be copied, nor can it easily be seamlessly deleted to enable a copy from the assets folder.

如果需要保留用户数据,则可能会带来进一步的复杂性.

A further complexity can arise if user data needs to be preserved.

可以使用哪些方法来管理现有数据库的不同版本?

What methods can be used to manage differing versions of pre-existing databases?

例如,我最初部署的数据库只有一个表,该表具有使用:-

For example I originally deploy a database with a single table with 3 columns created with :-

CREATE TABLE "user" (
  "_id" INTEGER NOT NULL,
  "user" TEXT,
  "password" TEXT,
  PRIMARY KEY ("_id")
)

其中包含诸如:-

应用程序的下一个版本希望使用更改后的架构,并在 user 列上添加一个额外的列和一个UNIQUE约束,例如:-

And the next incarnation of the App wants to utilise a changed schema, with an extra column and a UNIQUE constraint on the user column such as :-

CREATE TABLE "user" (
  "_id" INTEGER NOT NULL,
  "user" TEXT,
  "password" TEXT,
  "email" TEXT,
  PRIMARY KEY ("_id"),
  CONSTRAINT "user" UNIQUE ("user")
);

此外,还添加了其他数据,例如现在是:-

And additionally additional data is also added e.g. it is now :-

用户具有添加自己的数据的能力,需要保留这些数据.因此,即使有可能,删除数据库文件也将删除用户的数据.

The user has had the ability to add their own data, which needs to be preserved. So deleting the database file, even if possible, would delete the user's data.

可以使用哪些方法来管理现有数据库的不同版本?

推荐答案

简而言之,一种方法是将新资产文件复制到适当的位置,同时保留原始数据库文件的副本,然后可以将更新应用于为了有效地将用户数据保存到新复制的数据库中. ,仅在针对特定更改升级应用程序且数据库存在的情况下.

In short one way would be to copy the new asset file to the appropriate location, whilst keeping a copy of the original database file you can then apply the updates in order to effectively preserve the user's data to the newly copied database. BUT only if the App is being upgraded for that specific change and if the database exists.

如果数据库不存在,则应使用资产文件的标准副本.

If the database doesn't exist then the standard copy of the asset file should be undertaken.

此示例在很大程度上依赖于例程,该例程允许管理和查询资产文件夹中的文件或数据库文件本身.

This example relies quite heavily on a routines that allow management and some interrogation of the file in the assets folder and or the database file itself.

一个名为 DBAssetHandler.java 的类可以满足上述要求(以及使用SQLiteOpenHelper时可以将user_version AKA提取为数据库版本的功能).

A class namely DBAssetHandler.java caters for the above (as well as an ability to extract the user_version AKA the Database Version when using the SQLiteOpenHelper).

  • 请注意,该类也已经在Android Pie上进行了测试,因此可以满足Android Pie的要求,因此可以满足预写日志记录(WAL是Pie中的默认设置)以及日记模式(以前​​的默认设置)的要求.

  • Note the class has also been tested on, and therefore caters for, Android Pie, and thus Write-Ahead logging (WAL the default in Pie) a well as journal mode, the previous default.

还请注意,如果使用WAL,则应确保数据库已完全检查点确定请参阅-预写日志记录

Also note that if using WAL, then you should ensure the the database is fully check-pointed see - Write-Ahead Logging

是:-

public class DBAssetHandler {

    static final String[] tempfiles = new String[]{"-journal","-wal","-shm"}; // temporary files to rename
    public static final String backup = "-backup"; //value to be appended to file name when renaming (psuedo delete)
    public static final  int OUCH = -666666666;

    /**
     * Check if the database already exists. NOTE will create the databases folder is it doesn't exist
     * @return true if it exists, false if it doesn't
     */
    public static boolean checkDataBase(Context context, String dbname) {

        File db = new File(context.getDatabasePath(dbname).getPath()); //Get the file name of the database
        Log.d("DBPATH","DB Path is " + db.getPath()); //TODO remove if publish 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;
    }


    /**
     * Copy database file from the assets folder
     * (long version caters for asset file name being different to the database name)
     * @param context           Context is needed to get the applicable package
     * @param dbname            name of the database file
     * @param assetfilename     name of the asset file
     * @param deleteExistingDB  true if an existing database file should be deleted
     *                              note will delete journal and wal files
     *                              note doen't actually delete the files rater it renames
     *                              the files by appended -backup to the file name
     *                              SEE/USE clearForceBackups below to delete the renamed files
     */
    public static void copyDataBase(Context context, String dbname, String assetfilename, boolean deleteExistingDB) {

        final String TAG = "COPYDATABASE";
        int stage = 0, buffer_size = 4096, blocks_copied = 0, bytes_copied = 0;
        File f = new File(context.getDatabasePath(dbname).toString());
        InputStream is;
        OutputStream os;

        /**
         * If forcing then effectively delete (rename) current database files
         */
        if (deleteExistingDB) {
            //String[] tempfiles = new String[]{"-journal","-wal","-shm"};
            //String backup = "-backup";
            f.renameTo(context.getDatabasePath(dbname + "-backup"));
            for (String s: tempfiles) {
                File tmpf = new File(context.getDatabasePath(dbname + s).toString());
                if (tmpf.exists()) {
                    tmpf.renameTo(context.getDatabasePath(dbname + s + backup));
                }
            }
        }


        //Open your local db as the input stream
        Log.d(TAG,"Initiated Copy of the database file " + assetfilename + " from the assets folder."); //TODO remove if publishing
        try {
            is = context.getAssets().open(assetfilename); // Open the Asset file
            stage++;
            Log.d(TAG, "Asset file " + assetfilename + " found so attmepting to copy to " + f.getPath()); //TODO remove if publishing

            os = new FileOutputStream(f);
            stage++;
            //transfer bytes from the inputfile to the outputfile
            byte[] buffer = new byte[buffer_size];
            int length;
            while ((length = is.read(buffer)) > 0) {
                blocks_copied++;
                Log.d(TAG, "Attempting copy of block " + String.valueOf(blocks_copied) + " which has " + String.valueOf(length) + " bytes."); //TODO remove if publishing
                os.write(buffer, 0, length);
                bytes_copied += length;
            }
            stage++;
            Log.d(TAG,
                    "Finished copying Database " + dbname +
                            " from the assets folder, to  " + f.getPath() +
                            String.valueOf(bytes_copied) + "were copied, in " +
                            String.valueOf(blocks_copied) + " blocks of size " +
                            String.valueOf(buffer_size) + "."
            ); //TODO remove if publishing
            //Close the streams
            os.flush();
            stage++;
            os.close();
            stage++;
            is.close();
            Log.d(TAG, "All Streams have been flushed and closed.");
        } catch (IOException e) {
            String exception_message = "";
            e.printStackTrace();
            switch (stage) {
                case 0:
                    exception_message = "Error trying to open the asset " + dbname;
                    break;
                case 1:
                    exception_message = "Error opening Database file for output, path is " + f.getPath();
                    break;
                case 2:
                    exception_message = "Error flushing written database file " + f.getPath();
                    break;
                case 3:
                    exception_message = "Error closing written database file " + f.getPath();
                    break;
                case 4:
                    exception_message = "Error closing asset file " + f.getPath();

            }
            throw new RuntimeException("Unable to copy the database from the asset folder." + exception_message + " see starck-trace above.");
        }
    }

    /**
     * Copy the databsse from the assets folder where asset name and dbname are the same
     * @param context
     * @param dbname
     * @param deleteExistingDB
     */
    public static void copyDataBase(Context context, String dbname, boolean deleteExistingDB) {
        copyDataBase(context, dbname,dbname,deleteExistingDB);
    }

    /**
     * Get the SQLite_user_vesrion from the DB in the asset folder
     *
     * @param context           needed to get the appropriate package assets
     * @param assetfilename     the name of the asset file (assumes/requires name matches database)
     * @return                  the version number as stored in the asset DB
     */
    public static int getVersionFromDBInAssetFolder(Context context, String assetfilename) {
        InputStream is;
        try {
            is = context.getAssets().open(assetfilename);
        } catch (IOException e) {
            return OUCH;
        }
        return getDBVersionFromInputStream(is);
    }

    /**
     * Get the version from the database itself without opening the database as an SQliteDatabase
     * @param context   Needed to ascertain package
     * @param dbname    the name of the dataabase
     * @return          the version number extracted
     */
    public static int getVersionFromDBFile(Context context, String dbname) {
        InputStream is;
        try {
            is = new FileInputStream(new File(context.getDatabasePath(dbname).toString()));
        } catch (IOException e) {
            return OUCH;
        }
        return getDBVersionFromInputStream(is);
    }

    /**
     * Get the Database Version (user_version) from an inputstream
     *  Note the inputstream is closed
     * @param is    The Inputstream
     * @return      The extracted version number
     */
    private static int getDBVersionFromInputStream(InputStream is) {
        int rv = -1, dbversion_offset = 60, dbversion_length = 4 ;
        byte[] dbfileheader = new byte[64];
        byte[] dbversion = new byte[4];
        try {
            is.read(dbfileheader);
            is.close();
        } catch (IOException e) {
            e.printStackTrace();
            return rv;
        }

        for (int i = 0; i < dbversion_length; i++ ) {
            dbversion[i] = dbfileheader[dbversion_offset + i];
        }
        return ByteBuffer.wrap(dbversion).getInt();
    }

    /**
     * Check to see if the asset file exists
     *
     * @param context           needed to get the appropriate package
     * @param assetfilename     the name of the asset file to check
     * @return                  true if the asset file exists, else false
     */
    public static boolean ifAssetFileExists(Context context, String assetfilename) {
        try {
            context.getAssets().open(assetfilename);
        } catch (IOException e) {
            return false;
        }
        return true;
    }


    /**
     * Delete the backup
     * @param context
     * @param dbname
     */
    public static void clearForceBackups(Context context, String dbname) {
        String[] fulllist = new String[tempfiles.length + 1];

        for (int i = 0;i < tempfiles.length; i++) {
            fulllist[i] = tempfiles[i];
        }
        fulllist[tempfiles.length] = ""; // Add "" so database file backup is also deleted
        for (String s: fulllist) {
            File tmpf = new File(context.getDatabasePath(dbname + s + backup).toString());
            if (tmpf.exists()) {
                tmpf.delete();
            }
        }
    }
}

  • 希望方法名称和注释可以解释上面的代码.
  • 资产文件有两个:-

    • pev1.db -如上所述的原始原始数据库
    • pev1mod.db -修改后的(额外的列,UNIQUE约束和附加的行).
    • pev1.db - the original pre-existing database as described above
    • pev1mod.db - the modified (extra column, UNIQUE constraint and the additional row).

    Database Helper(SQLOpenHelper的子类)即 PEV2DBHelper.java .应注意,数据库版本( DBVERSION )用于控制,因此不同于APK的版本(后者可能比数据库版本更频繁)

    The Database Helper (a subclass of SQLOpenHelper) namely PEV2DBHelper.java, It should be noted that the Database Version (DBVERSION) is used to control and is as such distinct from the APK's version (which may change more frequently than the DB)

      尝试使用 onUpgrade 方法时发现了
    • 问题,因此是另一种方法,即从文件而不是通过SQLiteDatabase获取数据库的 user_version .
    • li>
    • issues were found when trying to use the onUpgrade method, therefore an alternative approach, that of getting the databases's user_version from the file rather than via an SQLiteDatabase.

    这里是 PEV2DBHelper.java :-

    /**
     * MORE COMPLEX EXAMPLE RETAINING USER DATA
     */
    public class PEV2DBHelper extends SQLiteOpenHelper {
    
        public static final String DBNAME = "pev1.db";
        public static final String ASSETTOCOPY_DBV2 = "pev1mod.db"; //<<<<<<<<<< changed DB
        public static final int DBVERSION = 2; //<<<<<<<<<< increase and db file from assets will copied keeping existing data
        Context mContext;
    
        public PEV2DBHelper(Context context) {
            super(context, DBNAME, null, DBVERSION);
    
    
            int dbversion = DBAssetHandler.getVersionFromDBFile(context,DBNAME);
            Log.d("DBFILEVERSION","Database File Version = " + String.valueOf(dbversion));
            int af1version = DBAssetHandler.getVersionFromDBInAssetFolder(context,DBNAME);
            Log.d("DBFILEVERSION","Asset Database File Version = " + String.valueOf(af1version));
            int af2version = DBAssetHandler.getVersionFromDBInAssetFolder(context,ASSETTOCOPY_DBV2);
            Log.d("DBFILEVERSION","Asset Database File Version = " + String.valueOf(af2version));
    
            // cater for different DBVERSIONS (for testing )
            if (!DBAssetHandler.checkDataBase(context,DBNAME)) {
    
                //If new installation of the APP then copy the appropriate asset file for the DB
                switch (DBVERSION) {
                    case 1:
                        DBAssetHandler.copyDataBase(context,DBNAME,DBNAME,false);
                        break;
                    case 2:
                        DBAssetHandler.copyDataBase(context,DBNAME,ASSETTOCOPY_DBV2,false);
                        break;
                }
            }
    
            // If DBVERSION upgraded to 2 with modified DB but wanting to preserve used data
            if (DBAssetHandler.checkDataBase(context,DBNAME) & (DBVERSION > DBAssetHandler.getVersionFromDBFile(context, DBNAME)) & (DBVERSION == 2) ) {
    
                String[] oldcolumns = new String[]{"user","password"};
    
                // Copy in the new DB noting that delete option renames old (truue flag important)
                DBAssetHandler.copyDataBase(context,DBNAME,ASSETTOCOPY_DBV2,true);
    
                //Get the newly copied database
                SQLiteDatabase newdb = SQLiteDatabase.openDatabase(context.getDatabasePath(DBNAME).toString(),null,SQLiteDatabase.OPEN_READWRITE);
                //Get the old database (backup copy)
                SQLiteDatabase olddb =  SQLiteDatabase.openDatabase(context.getDatabasePath(DBNAME + DBAssetHandler.backup).toString(),null,SQLiteDatabase.OPEN_READWRITE);
    
                //Prepare to insert old rows (note user column is UNIQUE so pretty simple scenario just try inserting all and duplicates will be rejected)
                ContentValues cv = new ContentValues();
                Cursor oldcsr = olddb.query("user",null,null,null,null,null,null);
                newdb.beginTransaction();
                while (oldcsr.moveToNext()) {
                    cv.clear();
                    for (String columnname: oldcolumns) {
                        cv.put(columnname,oldcsr.getString(oldcsr.getColumnIndex(columnname)));
                    }
                    newdb.insert("user",null,cv);
                }
                newdb.setTransactionSuccessful();
                newdb.endTransaction();
                newdb.close();
                olddb.close();
    
                // Finally delete the renamed old database
                DBAssetHandler.clearForceBackups(context,DBNAME);
            }
        }
    
        @Override
        public void onCreate(SQLiteDatabase db) {
    
        }
    
        @Override
        public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
    
        }
    }
    

    • 请注意,添加,删除提取行的方法几乎没有膨胀.但是,它有点复杂,因为它处理版本之间的切换以方便演示.
    • 最后是一个示例活动,该活动使用PEV2DBHelper进行调用,将模式和表中的行写入日志.

      Lastly is an example activity that invokes utilises PEV2DBHelper, writing the schema and the rows from the table to the log.

      使用的活动是 MainActivity.java ,并且是:-

      The activity used is MainActivity.java and is :-

      public class MainActivity extends AppCompatActivity {
      
          PEV2DBHelper mDBHlpr2; //DBHelper for example that retains user data
      
          @Override
          protected void onCreate(Bundle savedInstanceState) {
              super.onCreate(savedInstanceState);
              setContentView(R.layout.activity_main);
      
              doPEV2(); // Test simple more complex scenario
          }
      
      
          private void doPEV2() {
      
              mDBHlpr2 = new PEV2DBHelper(this);
              SQLiteDatabase db = mDBHlpr2.getWritableDatabase();
              Cursor csr = db.query("sqlite_master",null,null,null,null,null,null);
              DatabaseUtils.dumpCursor(csr);
              csr = db.query("user",null,null,null,null,null,null);
              DatabaseUtils.dumpCursor(csr);
              if (PEV2DBHelper.DBVERSION == 1) {
                  addUserData(db);
                  csr = db.query("user",null,null,null,null,null,null);
                  DatabaseUtils.dumpCursor(csr);
              }
              csr.close();
              db.close();
          }
      
          /**
           * Add some user data for testing presevation of that data
           * @param db    the SQLitedatabase
           */
          private void addUserData(SQLiteDatabase db) {
              ContentValues cv = new ContentValues();
              cv.put("user","mr new user");
              cv.put("password","a password");
              db.insert("user",null,cv);
          }
      }
      

      结果

      1.首次以DBVERSION 1(全新应用)运行时

      在这种情况下,资产文件 pev1.db 是从资产文件夹复制的,输出为:-

      Results

      1. When first run with the DBVERSION as 1 (brand new App)

      In this case the asset file pev1.db is copied from the assets folder the output is :-

      2019-02-22 19:07:54.676 28670-28670/? D/DBFILEVERSION: Database File Version = -666666666
      2019-02-22 19:07:54.677 28670-28670/? D/DBFILEVERSION: Asset Database File Version = 0
      2019-02-22 19:07:54.677 28670-28670/? D/DBFILEVERSION: Asset Database File Version = 0
      2019-02-22 19:07:54.677 28670-28670/? D/DBPATH: DB Path is /data/user/0/mjt.so54807516/databases/pev1.db
      2019-02-22 19:07:54.677 28670-28670/? D/COPYDATABASE: Initiated Copy of the database file pev1.db from the assets folder.
      2019-02-22 19:07:54.677 28670-28670/? D/COPYDATABASE: Asset file pev1.db found so attmepting to copy to /data/user/0/mjt.so54807516/databases/pev1.db
      2019-02-22 19:07:54.677 28670-28670/? D/COPYDATABASE: Attempting copy of block 1 which has 4096 bytes.
      2019-02-22 19:07:54.677 28670-28670/? D/COPYDATABASE: Attempting copy of block 2 which has 4096 bytes.
      2019-02-22 19:07:54.677 28670-28670/? D/COPYDATABASE: Finished copying Database pev1.db from the assets folder, to  /data/user/0/mjt.so54807516/databases/pev1.db8192were copied, in 2 blocks of size 4096.
      2019-02-22 19:07:54.678 28670-28670/? D/COPYDATABASE: All Streams have been flushed and closed.
      2019-02-22 19:07:54.678 28670-28670/? D/DBPATH: DB Path is /data/user/0/mjt.so54807516/databases/pev1.db
      2019-02-22 19:07:54.701 28670-28670/? I/System.out: >>>>> Dumping cursor android.database.sqlite.SQLiteCursor@71528f1
      2019-02-22 19:07:54.701 28670-28670/? I/System.out: 0 {
      2019-02-22 19:07:54.701 28670-28670/? I/System.out:    type=table
      2019-02-22 19:07:54.701 28670-28670/? I/System.out:    name=user
      2019-02-22 19:07:54.701 28670-28670/? I/System.out:    tbl_name=user
      2019-02-22 19:07:54.702 28670-28670/? I/System.out:    rootpage=2
      2019-02-22 19:07:54.702 28670-28670/? I/System.out:    sql=CREATE TABLE "user" (
      2019-02-22 19:07:54.702 28670-28670/? I/System.out:   "_id" INTEGER NOT NULL,
      2019-02-22 19:07:54.702 28670-28670/? I/System.out:   "user" TEXT,
      2019-02-22 19:07:54.702 28670-28670/? I/System.out:   "password" TEXT,
      2019-02-22 19:07:54.702 28670-28670/? I/System.out:   PRIMARY KEY ("_id")
      2019-02-22 19:07:54.702 28670-28670/? I/System.out: )
      2019-02-22 19:07:54.702 28670-28670/? I/System.out: }
      2019-02-22 19:07:54.702 28670-28670/? I/System.out: 1 {
      2019-02-22 19:07:54.702 28670-28670/? I/System.out:    type=table
      2019-02-22 19:07:54.702 28670-28670/? I/System.out:    name=android_metadata
      2019-02-22 19:07:54.702 28670-28670/? I/System.out:    tbl_name=android_metadata
      2019-02-22 19:07:54.702 28670-28670/? I/System.out:    rootpage=3
      2019-02-22 19:07:54.702 28670-28670/? I/System.out:    sql=CREATE TABLE android_metadata (locale TEXT)
      2019-02-22 19:07:54.702 28670-28670/? I/System.out: }
      2019-02-22 19:07:54.702 28670-28670/? I/System.out: <<<<<
      2019-02-22 19:07:54.703 28670-28670/? I/System.out: >>>>> Dumping cursor android.database.sqlite.SQLiteCursor@1e20cd6
      2019-02-22 19:07:54.703 28670-28670/? I/System.out: 0 {
      2019-02-22 19:07:54.703 28670-28670/? I/System.out:    _id=1
      2019-02-22 19:07:54.703 28670-28670/? I/System.out:    user=Fred
      2019-02-22 19:07:54.703 28670-28670/? I/System.out:    password=fredpassword
      2019-02-22 19:07:54.703 28670-28670/? I/System.out: }
      2019-02-22 19:07:54.703 28670-28670/? I/System.out: 1 {
      2019-02-22 19:07:54.703 28670-28670/? I/System.out:    _id=2
      2019-02-22 19:07:54.703 28670-28670/? I/System.out:    user=Mary
      2019-02-22 19:07:54.704 28670-28670/? I/System.out:    password=marypassword
      2019-02-22 19:07:54.704 28670-28670/? I/System.out: }
      2019-02-22 19:07:54.704 28670-28670/? I/System.out: <<<<<
      2019-02-22 19:07:54.705 28670-28670/? I/System.out: >>>>> Dumping cursor android.database.sqlite.SQLiteCursor@acdb57
      2019-02-22 19:07:54.705 28670-28670/? I/System.out: 0 {
      2019-02-22 19:07:54.705 28670-28670/? I/System.out:    _id=1
      2019-02-22 19:07:54.705 28670-28670/? I/System.out:    user=Fred
      2019-02-22 19:07:54.705 28670-28670/? I/System.out:    password=fredpassword
      2019-02-22 19:07:54.706 28670-28670/? I/System.out: }
      2019-02-22 19:07:54.706 28670-28670/? I/System.out: 1 {
      2019-02-22 19:07:54.706 28670-28670/? I/System.out:    _id=2
      2019-02-22 19:07:54.706 28670-28670/? I/System.out:    user=Mary
      2019-02-22 19:07:54.706 28670-28670/? I/System.out:    password=marypassword
      2019-02-22 19:07:54.706 28670-28670/? I/System.out: }
      2019-02-22 19:07:54.706 28670-28670/? I/System.out: 2 {
      2019-02-22 19:07:54.706 28670-28670/? I/System.out:    _id=3
      2019-02-22 19:07:54.706 28670-28670/? I/System.out:    user=mr new user
      2019-02-22 19:07:54.706 28670-28670/? I/System.out:    password=a password
      2019-02-22 19:07:54.706 28670-28670/? I/System.out: }
      2019-02-22 19:07:54.706 28670-28670/? I/System.out: <<<<<
      

      • -666666666是版本,因为不存在文件,因此从文件中获取版本的尝试返回了默认值,指示无法获取该版本.
      • 2019-02-22 19:09:43.724 28730-28730/mjt.so54807516 D/DBFILEVERSION: Database File Version = 1
        2019-02-22 19:09:43.724 28730-28730/mjt.so54807516 D/DBFILEVERSION: Asset Database File Version = 0
        2019-02-22 19:09:43.724 28730-28730/mjt.so54807516 D/DBFILEVERSION: Asset Database File Version = 0
        2019-02-22 19:09:43.725 28730-28730/mjt.so54807516 D/DBPATH: DB Path is /data/user/0/mjt.so54807516/databases/pev1.db
        2019-02-22 19:09:43.725 28730-28730/mjt.so54807516 D/DBPATH: DB Path is /data/user/0/mjt.so54807516/databases/pev1.db
        2019-02-22 19:09:43.729 28730-28730/mjt.so54807516 I/System.out: >>>>> 
        ..... etc
        

        3.将DBVERSION更改为2后进行下一次运行

        2019-02-22 19:13:49.157 28866-28866/mjt.so54807516 D/DBFILEVERSION: Database File Version = 1
        2019-02-22 19:13:49.158 28866-28866/mjt.so54807516 D/DBFILEVERSION: Asset Database File Version = 0
        2019-02-22 19:13:49.158 28866-28866/mjt.so54807516 D/DBFILEVERSION: Asset Database File Version = 0
        2019-02-22 19:13:49.158 28866-28866/mjt.so54807516 D/DBPATH: DB Path is /data/user/0/mjt.so54807516/databases/pev1.db
        2019-02-22 19:13:49.158 28866-28866/mjt.so54807516 D/DBPATH: DB Path is /data/user/0/mjt.so54807516/databases/pev1.db
        2019-02-22 19:13:49.158 28866-28866/mjt.so54807516 D/COPYDATABASE: Initiated Copy of the database file pev1mod.db from the assets folder.
        2019-02-22 19:13:49.159 28866-28866/mjt.so54807516 D/COPYDATABASE: Asset file pev1mod.db found so attmepting to copy to /data/user/0/mjt.so54807516/databases/pev1.db
        2019-02-22 19:13:49.159 28866-28866/mjt.so54807516 D/COPYDATABASE: Attempting copy of block 1 which has 4096 bytes.
        2019-02-22 19:13:49.159 28866-28866/mjt.so54807516 D/COPYDATABASE: Attempting copy of block 2 which has 4096 bytes.
        2019-02-22 19:13:49.159 28866-28866/mjt.so54807516 D/COPYDATABASE: Attempting copy of block 3 which has 4096 bytes.
        2019-02-22 19:13:49.159 28866-28866/mjt.so54807516 D/COPYDATABASE: Attempting copy of block 4 which has 4096 bytes.
        2019-02-22 19:13:49.159 28866-28866/mjt.so54807516 D/COPYDATABASE: Finished copying Database pev1.db from the assets folder, to  /data/user/0/mjt.so54807516/databases/pev1.db16384were copied, in 4 blocks of size 4096.
        2019-02-22 19:13:49.159 28866-28866/mjt.so54807516 D/COPYDATABASE: All Streams have been flushed and closed.
        2019-02-22 19:13:49.186 28866-28866/mjt.so54807516 E/SQLiteDatabase: Error inserting password=fredpassword user=Fred
            android.database.sqlite.SQLiteConstraintException: UNIQUE constraint failed: user.user (code 2067 SQLITE_CONSTRAINT_UNIQUE)
                at 
            .........
                at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)
        2019-02-22 19:13:49.191 28866-28866/mjt.so54807516 E/SQLiteDatabase: Error inserting password=a password user=mr new user
            android.database.sqlite.SQLiteConstraintException: UNIQUE constraint failed: user.user (code 2067 SQLITE_CONSTRAINT_UNIQUE)
                at 
           .............
        2019-02-22 19:13:49.209 28866-28866/mjt.so54807516 I/System.out: >>>>> Dumping cursor android.database.sqlite.SQLiteCursor@34252b0
        2019-02-22 19:13:49.210 28866-28866/mjt.so54807516 I/System.out: 0 {
        2019-02-22 19:13:49.210 28866-28866/mjt.so54807516 I/System.out:    type=table
        2019-02-22 19:13:49.210 28866-28866/mjt.so54807516 I/System.out:    name=user
        2019-02-22 19:13:49.210 28866-28866/mjt.so54807516 I/System.out:    tbl_name=user
        2019-02-22 19:13:49.210 28866-28866/mjt.so54807516 I/System.out:    rootpage=2
        2019-02-22 19:13:49.210 28866-28866/mjt.so54807516 I/System.out:    sql=CREATE TABLE "user" (
        2019-02-22 19:13:49.210 28866-28866/mjt.so54807516 I/System.out:   "_id" INTEGER NOT NULL,
        2019-02-22 19:13:49.210 28866-28866/mjt.so54807516 I/System.out:   "user" TEXT,
        2019-02-22 19:13:49.210 28866-28866/mjt.so54807516 I/System.out:   "password" TEXT,
        2019-02-22 19:13:49.210 28866-28866/mjt.so54807516 I/System.out:   "email" TEXT,
        2019-02-22 19:13:49.210 28866-28866/mjt.so54807516 I/System.out:   PRIMARY KEY ("_id"),
        2019-02-22 19:13:49.210 28866-28866/mjt.so54807516 I/System.out:   CONSTRAINT "user" UNIQUE ("user")
        2019-02-22 19:13:49.211 28866-28866/mjt.so54807516 I/System.out: )
        2019-02-22 19:13:49.211 28866-28866/mjt.so54807516 I/System.out: }
        2019-02-22 19:13:49.211 28866-28866/mjt.so54807516 I/System.out: 1 {
        2019-02-22 19:13:49.211 28866-28866/mjt.so54807516 I/System.out:    type=index
        2019-02-22 19:13:49.211 28866-28866/mjt.so54807516 I/System.out:    name=sqlite_autoindex_user_1
        2019-02-22 19:13:49.211 28866-28866/mjt.so54807516 I/System.out:    tbl_name=user
        2019-02-22 19:13:49.211 28866-28866/mjt.so54807516 I/System.out:    rootpage=4
        2019-02-22 19:13:49.211 28866-28866/mjt.so54807516 I/System.out:    sql=null
        2019-02-22 19:13:49.211 28866-28866/mjt.so54807516 I/System.out: }
        2019-02-22 19:13:49.211 28866-28866/mjt.so54807516 I/System.out: 2 {
        2019-02-22 19:13:49.211 28866-28866/mjt.so54807516 I/System.out:    type=table
        2019-02-22 19:13:49.211 28866-28866/mjt.so54807516 I/System.out:    name=android_metadata
        2019-02-22 19:13:49.211 28866-28866/mjt.so54807516 I/System.out:    tbl_name=android_metadata
        2019-02-22 19:13:49.211 28866-28866/mjt.so54807516 I/System.out:    rootpage=3
        2019-02-22 19:13:49.211 28866-28866/mjt.so54807516 I/System.out:    sql=CREATE TABLE android_metadata (locale TEXT)
        2019-02-22 19:13:49.212 28866-28866/mjt.so54807516 I/System.out: }
        2019-02-22 19:13:49.212 28866-28866/mjt.so54807516 I/System.out: <<<<<
        2019-02-22 19:13:49.212 28866-28866/mjt.so54807516 I/System.out: >>>>> Dumping cursor android.database.sqlite.SQLiteCursor@c8f0529
        2019-02-22 19:13:49.213 28866-28866/mjt.so54807516 I/System.out: 0 {
        2019-02-22 19:13:49.213 28866-28866/mjt.so54807516 I/System.out:    _id=1
        2019-02-22 19:13:49.213 28866-28866/mjt.so54807516 I/System.out:    user=Fred
        2019-02-22 19:13:49.213 28866-28866/mjt.so54807516 I/System.out:    password=fredpassword
        2019-02-22 19:13:49.213 28866-28866/mjt.so54807516 I/System.out:    email=fred@email.com
        2019-02-22 19:13:49.213 28866-28866/mjt.so54807516 I/System.out: }
        2019-02-22 19:13:49.213 28866-28866/mjt.so54807516 I/System.out: 1 {
        2019-02-22 19:13:49.213 28866-28866/mjt.so54807516 I/System.out:    _id=2
        2019-02-22 19:13:49.213 28866-28866/mjt.so54807516 I/System.out:    
        ...... etc
        

        这篇关于可以使用哪些方法来管理现有数据库的不同版本?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆