Связи с базой данных Android Room

Я создал базу данных в комнате Android с тремя объектами

    @Entity(tableName = "categories")
data class Category(

    @PrimaryKey(autoGenerate = true)
    @ColumnInfo(name = "id")
    val id: Int,

    @ColumnInfo(name = "category_name")
    val categoryName: String,

    @ColumnInfo(name = "category_id")
    val categoryId: Int
)

@Entity(tableName = "quotes")
data class Quote(

    @PrimaryKey(autoGenerate = true)
    @ColumnInfo(name = "id")
    val id: Int,

    @ColumnInfo(name = "quote")
    val quote: String,

    @ColumnInfo(name = "category_id")
    val categoryId: Int
)

@Entity(tableName = "favourites")
data class Favourite(

    @PrimaryKey(autoGenerate = true)
    @ColumnInfo(name = "id")
    val id: Int,

    @ColumnInfo(name = "quote")
    val quote: String,

    @ColumnInfo(name = "category_name")
    val categoryName: String,

//    @ColumnInfo(name = "date")
//    val date: Long
)

Теперь мой вопрос: как мне создать связь между двумя таблицами в этом примере, я хотел бы создать связь между Category и Quote, чтобы я мог упорядочивать котировки по categoryId. Как этот оператор sql

SELECT * FROM quotes INNER JOIN categories ON quotes.category_id = categories.id WHERE categories.id = :categoryId

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

P.s Я впервые с базой данных Room

0
0
76
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Для 1 ко многим (одна категория может иметь много кавычек) у вас есть POJO, где single — это поле, встроенное с использованием аннотации @Embedded, а many — это поле, представляющее собой список/массив с использованием аннотации @Relation.

Room генерирует эквивалент запроса JOIN (эквивалент) и подходит, если вам нужны ВСЕ связанные дочерние элементы.

Итак, вы хотите что-то вроде: -

data class CategoryWithItsQuotes(
    @Embedded
    val category: Category,
    @Relation(
        entity = Quote::class,
        parentColumn = "category_id",
        entityColumn = "category_id"
    )
    val quoteList: List<Quote>
)

Наряду с функцией в аннотированном интерфейсе @Dao, например: -

@Transaction
@Query("SELECT * FROM categories WHERE category_id=:category_id")
fun getCategoryWithItsQuotes(category_id: Int): List<CategoryWithItsQuotes>

т. е. нет необходимости в JOIN, поскольку Room создает метод для получения связанных котировок для категорий (категорий) с помощью запросов, которые он создает и выполняет на основе аннотации @Relation и, следовательно, почему @Transaction.

Если отношения многие-многие (как вы, вероятно, хотите для категории/избранного), у вас есть таблица (ассоциативная/ссылочная/сопоставление и таблица других имен), которая имеет два столбца, один для связывания/ссылки/сопоставления уникальной строки в одном таблиц и другой столбец для связывания/ссылки/сопоставления уникальной строки в другой таблице. Оба столбца составляют первичный ключ (составной первичный ключ). С помощью Room вы затем @Embed одну таблицу и @Relation другую таблицу, используя параметр associateBy для указания Junction (таблицы и соответствующих столбцов).

Комната (опять же с ограничением на получение ВСЕХ детей) позволяет иерархически использовать такие POJO, например. у вас может быть POJO: -

data class CategoryWithFavourites(
    @Embedded
    val category: Category,
    @Relation(
        entity = Favourite::class,
        parentColumn = "category_name",
        entityColumn = "category_name"
    )
    val favouriteList: List<Favourite>
)

а потом :-

data class QuoteWithCategoryAndFavourite(
    @Embedded
    val quote: Quote,
    @Relation(
        entity = Category::class,
        parentColumn = "category_id",
        entityColumn = "category_id"
    )
    val category: CategoryWithFavourites
)

И тогда есть что-то вроде: -

@Transaction
@Query("SELECT * FROM quotes WHERE id=:id")
fun getSingleQuoteWithCategoryAndFavourites(id: Int): List<QuoteWithCategoryAndFavourite>
  • Обратите внимание, что это не предлагаемое много-много (что вам, вероятно, нужно, чтобы у категории могло быть много избранных, а у избранного могло быть много категорий).
    • как таковой, это справится с фаворитом, когда-либо связанным только с одной категорией.

ОДНАКО

В ваших сущностях есть недостаток, который может привести к неожиданным результатам, то есть вы допускаете несколько одинаковых идентификаторов category_id (столбец id — единственный столбец, который однозначно идентифицирует категорию, см. демонстрацию)

ДЕМО

Чтобы продемонстрировать вышеизложенное (POJO и функции), рассмотрим дополнительные функции в @Dao аннотированном интерфейсе AllDAO (один для удобства/краткости): -

@Insert(onConflict = OnConflictStrategy.IGNORE)
fun insert(category: Category): Long
@Insert(onConflict = OnConflictStrategy.IGNORE)
fun insert(quote: Quote): Long
@Insert(onConflict = OnConflictStrategy.IGNORE)
fun insert(favourite: Favourite): Long

Следующий @Database аннотированный абстрактный класс: -

@Database(entities = [Category::class,Quote::class,Favourite::class], exportSchema = false, version = 1)
abstract class TheDatabase: RoomDatabase() {
    abstract fun getAllDAOs(): AllDAOs

    companion object {
        private var instance: TheDatabase?=null
        fun getInstance(context: Context): TheDatabase {
            if (instance==null) {
                instance = Room.databaseBuilder(context, TheDatabase::class.java, "the_database.db")
                    .allowMainThreadQueries() /* for brevity of demo */
                    .build()
            }
            return instance as TheDatabase
        }
    }
}

И следующий код активности: -

class MainActivity : AppCompatActivity() {

    lateinit var db: TheDatabase
    lateinit var dao: AllDAOs
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        db = TheDatabase.getInstance(this)
        dao = db.getAllDAOs()

        /* Add some categories */
        val c1CId = 100;
        val c1Id = dao.insert(Category(categoryName = "CAT1", categoryId = c1CId,id =0))
        val c2CId = 900
        val c2Id = dao.insert(Category(id=0, categoryName = "CAT2", categoryId = c2CId))
        val c3CId=900 /* ouch!!!!!!!!!  same category_id to demonstrate flaw */
        val c3Id = dao.insert(Category(id=0, categoryName = "CAT3", categoryId = c3CId))

        dao.insert(Quote(id=0, quote = "Quote001",c1CId))
        dao.insert(Quote(id=0, quote = "Quote002",c2CId))
        dao.insert(Quote(id=0, quote = "Quote003",c3CId))
        dao.insert(Quote(id=0, quote = "Quote004", categoryId = c1CId))
        dao.insert(Quote(id=0, quote = "Quote005", categoryId = c1CId))
        dao.insert(Quote(id=0, quote = "Quote006", categoryId = c1CId))
        dao.insert(Quote(id=0, quote = "Quote007", categoryId = c2CId))
        dao.insert(Quote(id=0, quote = "Quote008", categoryId = c2CId))
        dao.insert(Quote(id=0, quote = "Quote009", categoryId = c3CId))

        logCatWithQuotes(c1CId)
        logCatWithQuotes(c2CId)
        logCatWithQuotes(c3CId)

    }

    private fun logCatWithQuotes(catid: Int) {
        for(cwq in dao.getCategoryWithItsQuotes(catid)) {
            val sb = StringBuilder()
            for (q in cwq.quoteList) {
                sb.append("\n\tQuote is ${q.quote} ID is ${q.id} CATID is ${q.categoryId}")
            }
            Log.d(
                "DBINFO_CAT${catid}",
                "CAT is ${cwq.category.categoryName} ID is ${cwq.category.id} CATID is ${cwq.category.categoryId}. It has ${cwq.quoteList.size} quotes. They are:-${sb}")
        }
    }
}

При запуске вывод результата в журнал: -

2023-04-03 11:16:43.016 D/DBINFO_CAT100: CAT is CAT1 ID is 1 CATID is 100. It has 4 quotes. They are:-
        Quote is Quote001 ID is 1 CATID is 100
        Quote is Quote004 ID is 4 CATID is 100
        Quote is Quote005 ID is 5 CATID is 100
        Quote is Quote006 ID is 6 CATID is 100
2023-04-03 11:16:43.020 D/DBINFO_CAT900: CAT is CAT2 ID is 2 CATID is 900. It has 5 quotes. They are:-
        Quote is Quote002 ID is 2 CATID is 900
        Quote is Quote003 ID is 3 CATID is 900
        Quote is Quote007 ID is 7 CATID is 900
        Quote is Quote008 ID is 8 CATID is 900
        Quote is Quote009 ID is 9 CATID is 900
2023-04-03 11:16:43.020 D/DBINFO_CAT900: CAT is CAT3 ID is 3 CATID is 900. It has 5 quotes. They are:-
        Quote is Quote002 ID is 2 CATID is 900
        Quote is Quote003 ID is 3 CATID is 900
        Quote is Quote007 ID is 7 CATID is 900
        Quote is Quote008 ID is 8 CATID is 900
        Quote is Quote009 ID is 9 CATID is 900
2023-04-03 11:16:43.025 D/DBINFO_CAT900: CAT is CAT2 ID is 2 CATID is 900. It has 5 quotes. They are:-
        Quote is Quote002 ID is 2 CATID is 900
        Quote is Quote003 ID is 3 CATID is 900
        Quote is Quote007 ID is 7 CATID is 900
        Quote is Quote008 ID is 8 CATID is 900
        Quote is Quote009 ID is 9 CATID is 900
2023-04-03 11:16:43.025 D/DBINFO_CAT900: CAT is CAT3 ID is 3 CATID is 900. It has 5 quotes. They are:-
        Quote is Quote002 ID is 2 CATID is 900
        Quote is Quote003 ID is 3 CATID is 900
        Quote is Quote007 ID is 7 CATID is 900
        Quote is Quote008 ID is 8 CATID is 900
        Quote is Quote009 ID is 9 CATID is 900

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

то есть CAT1, как и ожидалось, имеет 4 назначенных кавычки.

Однако CAT2 и CAT3 имеют слишком много кавычек и также повторяются, то есть каждая из них представляет собой сумму обоих из-за того, что они имеют один и тот же неуникальный идентификатор категории 900. Итак: -

  • поиск CAT2 с помощью category_id находит CAT2 и CAT3 и наоборот, и, конечно, кавычки для каждого из них будут иметь одинаковую ссылку и, таким образом, ссылаться на оба.

Короче говоря, поле/столбец id может делать все, что требуется от поля/столбца category_id, и будет уникальным и, следовательно, не приведет к такой проблеме. Кроме того, будет достигнута экономия места и, возможно, выигрыш в эффективности, поскольку столбец category_id не нужен и фактически является помехой, как было показано.

Другие вопросы по теме