Я создал базу данных в комнате 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
Для 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. Итак: -
Короче говоря, поле/столбец id может делать все, что требуется от поля/столбца category_id, и будет уникальным и, следовательно, не приведет к такой проблеме. Кроме того, будет достигнута экономия места и, возможно, выигрыш в эффективности, поскольку столбец category_id не нужен и фактически является помехой, как было показано.