SQLite
 sql >> Baza danych >  >> RDS >> SQLite

Jakich metod można użyć do zarządzania różnymi wersjami wcześniej istniejących baz danych?

Krótko mówiąc, jednym ze sposobów byłoby skopiowanie nowego pliku zasobu do odpowiedniej lokalizacji, zachowując kopię oryginalnego pliku bazy danych, a następnie zastosuj aktualizacje, aby skutecznie zachować dane użytkownika w nowo skopiowanej bazie danych. ALE tylko wtedy, gdy aplikacja jest aktualizowana pod kątem tej konkretnej zmiany i jeśli istnieje baza danych.

Jeśli baza danych nie istnieje, należy wykonać standardową kopię pliku zasobów.

Oto przykład, jak można to zrobić.

Ten przykład w dużej mierze opiera się na procedurach, które pozwalają na zarządzanie i pewne przeglądanie pliku w folderze zasobów i/lub samego pliku bazy danych.

Klasa o nazwie DBAssetHandler.java spełnia powyższe wymagania (jak również możliwość wyodrębnienia user_version AKA wersji bazy danych podczas korzystania z SQLiteOpenHelper).

  • Zwróć uwagę, że klasa została również przetestowana, a zatem obsługuje Android Pie, a więc rejestrowanie z wyprzedzeniem (WAL to ustawienie domyślne w Pie), a także tryb dziennika, poprzedni domyślny.

  • Pamiętaj również, że jeśli używasz WAL, powinieneś upewnić się, że baza danych jest w pełni sprawdzona, patrz - Rejestrowanie z wyprzedzeniem

to jest :-

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();
            }
        }
    }
}
  • Mam nadzieję, że nazwy metod i komentarze powinny wyjaśniać powyższy kod.

Pliki zasobów są dwa :-

  • pev1.db - oryginalna wcześniej istniejąca baza danych, jak opisano powyżej
  • pev1mod.db - zmodyfikowany (dodatkowa kolumna, ograniczenie UNIQUE i dodatkowy wiersz).

Pomocnik bazy danych (podklasa SQLOpenHelper), a mianowicie PEV2DBHelper.java , Należy zauważyć, że wersja bazy danych (DBVERSION ) służy do kontroli i jako taka różni się od wersji APK (która może zmieniać się częściej niż DB)

  • wykryto problemy podczas próby korzystania z onUpgrade metoda, a zatem alternatywne podejście, polegające na pobraniu wersji_użytkownika bazy danych z pliku, a nie przez SQLiteDatabase.

Oto 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) {

    }
}
  • Zauważ, że metody dodawania, usuwania i wyodrębniania wierszy są niewielkie. Jest jednak nieco zbyt skomplikowany, ponieważ obsługuje przełączanie między wersjami, aby ułatwić demonstrację.

Na koniec przykładowe działanie, które wywołuje PEV2DBHelper, zapisując schemat i wiersze z tabeli do dziennika.

Użyta aktywność to MainActivity.java i jest :-

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);
    }
}

Wyniki

1. Przy pierwszym uruchomieniu DBVERSION jako 1 (zupełnie nowa aplikacja)

W tym przypadku plik zasobów pev1.db jest kopiowany z folderu zasobów, wynik jest :-

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 [email protected]
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 [email protected]
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 [email protected]
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 to wersja, ponieważ nie istniał żaden plik, dlatego próba pobrania wersji z pliku zwróciła wartość domyślną, aby wskazać, że nie można uzyskać wersji.

2. Po drugie uruchom to samo, Z WYJĄTKIEM, że numer wersji to 1.

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. Następne uruchomienie po zmianie DBVERSION na 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 [email protected]
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 [email protected]
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 protected]
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


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Wyprowadzanie wyników zapytania SQLite w postaci listy rozdzielanej tabulatorami

  2. Błąd bazy danych SQLite, niepomocny dziennik

  3. Usuwanie elementu z ListView i bazy danych za pomocą OnItemClickListener

  4. onCreate() funkcji RoomDatabase.Callback() nie został wywołany po pomyślnym wywołaniu funkcji .build()

  5. Najlepszy sposób na buforowanie json