RoomDatabase onCreate вызывается дважды - PullRequest
0 голосов
/ 29 июня 2018

Я использую RoomDatabase.Callback для заполнения базы данных при вызове onCreate. Согласно документации, этот метод должен вызываться только один раз при первом создании базы данных.

Вызывается при первом создании базы данных. Это называется после того, как все таблицы созданы.

Но по какой-то причине он вызывается дважды (иногда более двух раз). Это не будет большой проблемой, если данные заменяются при втором вызове, но поскольку каждый вызов запускает новый поток с отдельными входными вызовами, он создает дублирующиеся данные.

Это база данных, о которой идет речь.

@Database(entities = [FTSPlaceholder::class], version = 1)
abstract class DirectoryDatabase : RoomDatabase() {

    companion object {

        const val NAME = "directory_database"

        @Volatile private var INSTANCE: DirectoryDatabase? = null

        fun getInstance(context: Context): DirectoryDatabase = INSTANCE ?: synchronized(this) {
            INSTANCE ?: buildDatabase(context).also { INSTANCE = it }
        }

        private fun buildDatabase(context: Context): DirectoryDatabase =
                Room.databaseBuilder(context.applicationContext, DirectoryDatabase::class.java, NAME)
                        .addCallback(FTSCallback()).build()

    }

    fun addCallback(callback: Callback) {
        if (mCallbacks == null) mCallbacks = ArrayList()
        mCallbacks?.add(callback)
    }

    abstract fun departmentDao(): DepartmentDao

    abstract fun employeeDao(): EmployeeDao

    private class FTSCallback : Callback() {

        companion object {

            private const val CREATE_TABLE_DEPARTMENTS =
                    "CREATE VIRTUAL TABLE IF NOT EXISTS `departments` " +
                    "USING FTS4(`code` TEXT NOT NULL, `title` TEXT NOT NULL, " +
                    "`location` TEXT NOT NULL, `phone` TEXT, `fax` TEXT, PRIMARY KEY(`code`))"

            private const val CREATE_TABLE_PERSONNEL =
                    "CREATE VIRTUAL TABLE IF NOT EXISTS `personnel` " +
                    "USING FTS4(`familyName` TEXT NOT NULL, `givenName` TEXT NOT NULL, " +
                    "`middleName` TEXT, `title` TEXT NOT NULL, `location` TEXT NOT NULL, " +
                    "`room` TEXT NOT NULL, `phone1` TEXT NOT NULL, `phone2` TEXT NOT NULL, " +
                    "`email` TEXT NOT NULL, `fax` TEXT, `department` TEXT NOT NULL, " +
                    "`school` TEXT, PRIMARY KEY(`familyName`, `givenName`, `title`))"

        }

        override fun onCreate(db: SupportSQLiteDatabase) {
            db.execSQL(CREATE_TABLE_DEPARTMENTS)
            db.execSQL(CREATE_TABLE_PERSONNEL)
        }

    }

}

Я делаю некоторые странные вещи, чтобы добавить поддержку FTS, но это не должно вызывать onCreate(), чтобы вызываться дважды; особенно если учесть, что я делаю то же самое в другой базе данных, что не вызывает той же проблемы.

@Database(entities = [Area::class], version = 1)
abstract class MapDatabase : RoomDatabase() {

    companion object {

        const val NAME = "map_database"

        @Volatile private var INSTANCE: MapDatabase? = null

        fun getInstance(context: Context): MapDatabase = INSTANCE ?: synchronized(this) {
            INSTANCE ?: buildDatabase(context).also { INSTANCE = it }
        }

        private fun buildDatabase(context: Context): MapDatabase =
                Room.databaseBuilder(context.applicationContext, MapDatabase::class.java, NAME)
                        .addCallback(FTSCallback()).build()

    }

    fun addCallback(callback: Callback) {
        if (mCallbacks == null) mCallbacks = ArrayList()
        mCallbacks?.add(callback)
    }

    abstract fun placeDao(): PlaceDao

    abstract fun areaDao(): AreaDao

    private class FTSCallback : Callback() {

        companion object {

            private const val CREATE_TABLE_PLACES =
                    "CREATE VIRTUAL TABLE IF NOT EXISTS `places` " +
                            "USING FTS4(`id` INTEGER NOT NULL, `title` TEXT NOT NULL, " +
                            "`subtitle` TEXT, `description` TEXT, `latitude` REAL NOT NULL, " +
                            "`longitude` REAL NOT NULL, `type` INTEGER NOT NULL, " +
                            "`parent` INTEGER, PRIMARY KEY(`id`))"

        }

        override fun onCreate(db: SupportSQLiteDatabase) {
            db.execSQL(CREATE_TABLE_PLACES)
        }

    }

}

Я добавляю обратный вызов в базу данных в отдельном классе хранилища.

class DirectoryRepository(application: Application) {

    private val database = DirectoryDatabase.getInstance(application)

    init {
        database.addCallback(object : RoomDatabase.Callback() {

            // This method is being called twice
            override fun onCreate(db: SupportSQLiteDatabase) {
                refresh()
            }
        }
    }

    // Code omitted for brevity

}

Я не могу понять, почему это так, особенно если учесть, что это происходит только с одной из двух (очень похожих) реализаций.

Ответы [ 2 ]

0 голосов
/ 30 июня 2018

Существует вероятность, что экземпляр class DirectoryRepository был создан более одного раза, а обратный вызов добавлен при каждом вызове init.


Кроме того, вы должны добавить обратные вызовы с addCallback(), предоставленные строителем класса RoomDatabase.

В противном случае вы можете столкнуться с противоположной проблемой: обратный вызов вообще не будет срабатывать. Это может произойти, если вы вручную добавите обратный вызов после SupportSQLiteOpenHelper, созданного в методе <database-class>_Impl.createOpenHelper(...).

0 голосов
/ 29 июня 2018

Вы должны проверить instance == null даже в синхронизированном блоке.

    fun getInstance(context: Context): DirectoryDatabase {
        return INSTANCE ?: synchronized(this) {
            if(INSTANCE != null) {
                return@synchronized database
            }
            val database = Room.databaseBuilder(context.applicationContext,
                    DirectoryDatabase::class.java, NAME)
                    .addCallback(FTSCallback())
                    .build()
            INSTANCE = database
            return@synchronized database
        }
    }
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...