public class

RoomOpenHelper

extends SupportSQLiteOpenHelper.Callback

 java.lang.Object

androidx.sqlite.db.SupportSQLiteOpenHelper.Callback

↳androidx.room.RoomOpenHelper

Gradle dependencies

compile group: 'androidx.room', name: 'room-runtime', version: '2.5.0-alpha01'

  • groupId: androidx.room
  • artifactId: room-runtime
  • version: 2.5.0-alpha01

Artifact androidx.room:room-runtime:2.5.0-alpha01 it located at Google repository (https://maven.google.com/)

Androidx artifact mapping:

androidx.room:room-runtime android.arch.persistence.room:runtime

Androidx class mapping:

androidx.room.RoomOpenHelper android.arch.persistence.room.RoomOpenHelper

Overview

An open helper that holds a reference to the configuration until the database is opened.

Summary

Fields
from SupportSQLiteOpenHelper.Callbackversion
Constructors
publicRoomOpenHelper(DatabaseConfiguration configuration, RoomOpenHelper.Delegate delegate, java.lang.String legacyHash)

publicRoomOpenHelper(DatabaseConfiguration configuration, RoomOpenHelper.Delegate delegate, java.lang.String identityHash, java.lang.String legacyHash)

Methods
public voidonConfigure(SupportSQLiteDatabase db)

Called when the database connection is being configured, to enable features such as write-ahead logging or foreign key support.

public abstract voidonCreate(SupportSQLiteDatabase db)

Called when the database is created for the first time.

public voidonDowngrade(SupportSQLiteDatabase db, int oldVersion, int newVersion)

Called when the database needs to be downgraded.

public voidonOpen(SupportSQLiteDatabase db)

Called when the database has been opened.

public abstract voidonUpgrade(SupportSQLiteDatabase db, int oldVersion, int newVersion)

Called when the database needs to be upgraded.

from SupportSQLiteOpenHelper.CallbackonCorruption
from java.lang.Objectclone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait

Constructors

public RoomOpenHelper(DatabaseConfiguration configuration, RoomOpenHelper.Delegate delegate, java.lang.String identityHash, java.lang.String legacyHash)

public RoomOpenHelper(DatabaseConfiguration configuration, RoomOpenHelper.Delegate delegate, java.lang.String legacyHash)

Methods

public void onConfigure(SupportSQLiteDatabase db)

Called when the database connection is being configured, to enable features such as write-ahead logging or foreign key support.

This method is called before SupportSQLiteOpenHelper.Callback.onCreate(SupportSQLiteDatabase), SupportSQLiteOpenHelper.Callback.onUpgrade(SupportSQLiteDatabase, int, int), SupportSQLiteOpenHelper.Callback.onDowngrade(SupportSQLiteDatabase, int, int), or SupportSQLiteOpenHelper.Callback.onOpen(SupportSQLiteDatabase) are called. It should not modify the database except to configure the database connection as required.

This method should only call methods that configure the parameters of the database connection, such as SupportSQLiteDatabase.enableWriteAheadLogging() SupportSQLiteDatabase.setForeignKeyConstraintsEnabled(boolean), SupportSQLiteDatabase.setLocale(Locale), SupportSQLiteDatabase.setMaximumSize(long), or executing PRAGMA statements.

Parameters:

db: The database.

public abstract void onCreate(SupportSQLiteDatabase db)

Called when the database is created for the first time. This is where the creation of tables and the initial population of the tables should happen.

Parameters:

db: The database.

public abstract void onUpgrade(SupportSQLiteDatabase db, int oldVersion, int newVersion)

Called when the database needs to be upgraded. The implementation should use this method to drop tables, add tables, or do anything else it needs to upgrade to the new schema version.

The SQLite ALTER TABLE documentation can be found here. If you add new columns you can use ALTER TABLE to insert them into a live table. If you rename or remove columns you can use ALTER TABLE to rename the old table, then create the new table and then populate the new table with the contents of the old table.

This method executes within a transaction. If an exception is thrown, all changes will automatically be rolled back.

Parameters:

db: The database.
oldVersion: The old database version.
newVersion: The new database version.

public void onDowngrade(SupportSQLiteDatabase db, int oldVersion, int newVersion)

Called when the database needs to be downgraded. This is strictly similar to SupportSQLiteOpenHelper.Callback.onUpgrade(SupportSQLiteDatabase, int, int) method, but is called whenever current version is newer than requested one. However, this method is not abstract, so it is not mandatory for a customer to implement it. If not overridden, default implementation will reject downgrade and throws SQLiteException

This method executes within a transaction. If an exception is thrown, all changes will automatically be rolled back.

Parameters:

db: The database.
oldVersion: The old database version.
newVersion: The new database version.

public void onOpen(SupportSQLiteDatabase db)

Called when the database has been opened. The implementation should check SupportSQLiteDatabase.isReadOnly() before updating the database.

This method is called after the database connection has been configured and after the database schema has been created, upgraded or downgraded as necessary. If the database connection must be configured in some way before the schema is created, upgraded, or downgraded, do it in SupportSQLiteOpenHelper.Callback.onConfigure(SupportSQLiteDatabase) instead.

Parameters:

db: The database.

Source

/*
 * Copyright (C) 2017 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package androidx.room;

import android.database.Cursor;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RestrictTo;
import androidx.room.migration.Migration;
import androidx.sqlite.db.SimpleSQLiteQuery;
import androidx.sqlite.db.SupportSQLiteDatabase;
import androidx.sqlite.db.SupportSQLiteOpenHelper;

import java.util.List;

/**
 * An open helper that holds a reference to the configuration until the database is opened.
 *
 * @hide
 */
@SuppressWarnings("unused")
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
public class RoomOpenHelper extends SupportSQLiteOpenHelper.Callback {
    @Nullable
    private DatabaseConfiguration mConfiguration;
    @NonNull
    private final Delegate mDelegate;
    @NonNull
    private final String mIdentityHash;
    /**
     * Room v1 had a bug where the hash was not consistent if fields are reordered.
     * The new has fixes it but we still need to accept the legacy hash.
     */
    @NonNull // b/64290754
    private final String mLegacyHash;

    public RoomOpenHelper(@NonNull DatabaseConfiguration configuration, @NonNull Delegate delegate,
            @NonNull String identityHash, @NonNull String legacyHash) {
        super(delegate.version);
        mConfiguration = configuration;
        mDelegate = delegate;
        mIdentityHash = identityHash;
        mLegacyHash = legacyHash;
    }

    public RoomOpenHelper(@NonNull DatabaseConfiguration configuration, @NonNull Delegate delegate,
            @NonNull String legacyHash) {
        this(configuration, delegate, "", legacyHash);
    }

    @Override
    public void onConfigure(SupportSQLiteDatabase db) {
        super.onConfigure(db);
    }

    @Override
    public void onCreate(SupportSQLiteDatabase db) {
        boolean isEmptyDatabase = hasEmptySchema(db);
        mDelegate.createAllTables(db);
        if (!isEmptyDatabase) {
            // A 0 version pre-populated database goes through the create path because the
            // framework's SQLiteOpenHelper thinks the database was just created from scratch. If we
            // find the database not to be empty, then it is a pre-populated, we must validate it to
            // see if its suitable for usage.
            ValidationResult result = mDelegate.onValidateSchema(db);
            if (!result.isValid) {
                throw new IllegalStateException("Pre-packaged database has an invalid schema: "
                        + result.expectedFoundMsg);
            }
        }
        updateIdentity(db);
        mDelegate.onCreate(db);
    }

    @Override
    public void onUpgrade(SupportSQLiteDatabase db, int oldVersion, int newVersion) {
        boolean migrated = false;
        if (mConfiguration != null) {
            List<Migration> migrations = mConfiguration.migrationContainer.findMigrationPath(
                    oldVersion, newVersion);
            if (migrations != null) {
                mDelegate.onPreMigrate(db);
                for (Migration migration : migrations) {
                    migration.migrate(db);
                }
                ValidationResult result = mDelegate.onValidateSchema(db);
                if (!result.isValid) {
                    throw new IllegalStateException("Migration didn't properly handle: "
                            + result.expectedFoundMsg);
                }
                mDelegate.onPostMigrate(db);
                updateIdentity(db);
                migrated = true;
            }
        }
        if (!migrated) {
            if (mConfiguration != null
                    && !mConfiguration.isMigrationRequired(oldVersion, newVersion)) {
                mDelegate.dropAllTables(db);
                mDelegate.createAllTables(db);
            } else {
                throw new IllegalStateException("A migration from " + oldVersion + " to "
                        + newVersion + " was required but not found. Please provide the "
                        + "necessary Migration path via "
                        + "RoomDatabase.Builder.addMigration(Migration ...) or allow for "
                        + "destructive migrations via one of the "
                        + "RoomDatabase.Builder.fallbackToDestructiveMigration* methods.");
            }
        }
    }

    @Override
    public void onDowngrade(SupportSQLiteDatabase db, int oldVersion, int newVersion) {
        onUpgrade(db, oldVersion, newVersion);
    }

    @Override
    public void onOpen(SupportSQLiteDatabase db) {
        super.onOpen(db);
        checkIdentity(db);
        mDelegate.onOpen(db);
        // there might be too many configurations etc, just clear it.
        mConfiguration = null;
    }

    private void checkIdentity(SupportSQLiteDatabase db) {
        if (hasRoomMasterTable(db)) {
            String identityHash = null;
            Cursor cursor = db.query(new SimpleSQLiteQuery(RoomMasterTable.READ_QUERY));
            //noinspection TryFinallyCanBeTryWithResources
            try {
                if (cursor.moveToFirst()) {
                    identityHash = cursor.getString(0);
                }
            } finally {
                cursor.close();
            }
            if (!mIdentityHash.equals(identityHash) && !mLegacyHash.equals(identityHash)) {
                throw new IllegalStateException("Room cannot verify the data integrity. Looks like"
                        + " you've changed schema but forgot to update the version number. You can"
                        + " simply fix this by increasing the version number. Expected identity"
                        + " hash: " + identityHash + ", found: " + mIdentityHash);
            }
        } else {
            // No room_master_table, this might an a pre-populated DB, we must validate to see if
            // its suitable for usage.
            ValidationResult result = mDelegate.onValidateSchema(db);
            if (!result.isValid) {
                throw new IllegalStateException("Pre-packaged database has an invalid schema: "
                        + result.expectedFoundMsg);
            }
            mDelegate.onPostMigrate(db);
            updateIdentity(db);
        }
    }

    private void updateIdentity(SupportSQLiteDatabase db) {
        createMasterTableIfNotExists(db);
        db.execSQL(RoomMasterTable.createInsertQuery(mIdentityHash));
    }

    private void createMasterTableIfNotExists(SupportSQLiteDatabase db) {
        db.execSQL(RoomMasterTable.CREATE_QUERY);
    }

    private static boolean hasRoomMasterTable(SupportSQLiteDatabase db) {
        Cursor cursor = db.query("SELECT 1 FROM sqlite_master WHERE type = 'table' AND name='"
                + RoomMasterTable.TABLE_NAME + "'");
        //noinspection TryFinallyCanBeTryWithResources
        try {
            return cursor.moveToFirst() && cursor.getInt(0) != 0;
        } finally {
            cursor.close();
        }
    }

    private static boolean hasEmptySchema(SupportSQLiteDatabase db) {
        Cursor cursor = db.query(
                "SELECT count(*) FROM sqlite_master WHERE name != 'android_metadata'");
        //noinspection TryFinallyCanBeTryWithResources
        try {
            return cursor.moveToFirst() && cursor.getInt(0) == 0;
        } finally {
            cursor.close();
        }
    }

    /**
     * @hide
     */
    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
    public abstract static class Delegate {
        public final int version;

        public Delegate(int version) {
            this.version = version;
        }

        protected abstract void dropAllTables(SupportSQLiteDatabase database);

        protected abstract void createAllTables(SupportSQLiteDatabase database);

        protected abstract void onOpen(SupportSQLiteDatabase database);

        protected abstract void onCreate(SupportSQLiteDatabase database);

        /**
         * Called after a migration run to validate database integrity.
         *
         * @param db The SQLite database.
         *
         * @deprecated Use {@link #onValidateSchema(SupportSQLiteDatabase)}
         */
        @Deprecated
        protected void validateMigration(SupportSQLiteDatabase db) {
            throw new UnsupportedOperationException("validateMigration is deprecated");
        }

        /**
         * Called after a migration run or pre-package database copy to validate database integrity.
         *
         * @param db The SQLite database.
         */
        @SuppressWarnings("deprecation")
        @NonNull
        protected ValidationResult onValidateSchema(@NonNull SupportSQLiteDatabase db) {
            validateMigration(db);
            return new ValidationResult(true, null);
        }

        /**
         * Called before migrations execute to perform preliminary work.
         * @param database The SQLite database.
         */
        protected void onPreMigrate(SupportSQLiteDatabase database) {

        }

        /**
         * Called after migrations execute to perform additional work.
         * @param database The SQLite database.
         */
        protected void onPostMigrate(SupportSQLiteDatabase database) {

        }
    }

    /**
     * @hide
     */
    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
    public static class ValidationResult {

        public final boolean isValid;
        @Nullable
        public final String expectedFoundMsg;

        public ValidationResult(boolean isValid, @Nullable String expectedFoundMsg) {
            this.isValid = isValid;
            this.expectedFoundMsg = expectedFoundMsg;
        }
    }
}