Как создать классы Entity и данных по ROOM в Android?
У меня есть структура JSON:
data class ListResponse(val item: ListItem)
data class ListItem(
@SerializedName("id")
val id: List<CheckUnCheckItem>
)
data class CheckUnCheckItem(
@SerializedName("check")
val check: CheckItem,
@SerializedName("unCheck")
val UnCheck: UnCheckItem
)
data class CheckItem(
@SerializedName("url")
val url: String,
@SerializedName("text")
val text: String,
@SerializedName("color")
val color: String
)
data class UnCheckItem(
@SerializedName("url")
val urlUnCheck: String,
@SerializedName("text")
val textUnCheck: String,
@SerializedName("color")
val colorUnCheck: String
)
Но как я могу создать такую сущность ROOM?
Нужно ли использовать @TypeConverter?
@Entity(tableName = TABLE_NAME)
data class ListEntity(
@PrimaryKey @SerializedName("id")
val id: CheckUnCheckItem,
@SerializedName("check")
val check: CheckItem,
@SerializedName("unCheck")
val unCheck: UnCheckItem,
@SerializedName("url")
val url: String,
@SerializedName("text")
val text: String,
@SerializedName("size")
val size: String
){
companion object{
const val TABLE_NAME = "db_table"
}
class RoomTypeConverters{
@TypeConverter
fun convertCheckItemListToJSONString(checkList: CheckItem): String = Gson().toJson(checkList)
@TypeConverter
fun convertJSONStringToCheckItemList(jsonString: String): CheckItem = Gson().fromJson(jsonString,CheckItem::class.java)
}
}
верны ли мои данные и классы сущностей? Нужен ли класс ведьмы, расширяющий RoomDatabase? Или лучше мне нужно отделить БД и создать для проверки и снять отметку с другой БД?
Или лучше мне нужно отделить БД и создать для проверки и снять отметку с другой БД?
Как подразумевает база данных, она способна хранить данные не по одному, а по многим. Таким образом, единая база данных — это все, что потребуется. SQLite — это реляционная база данных, предназначенная для хранения связанных данных. Связанные данные обычно хранятся в нескольких таблицах. Так что опять же одной базы данных, скорее всего, будет достаточно.
Нужно ли использовать @TypeConverter? На самом деле вам никогда не понадобятся преобразователи типов. Однако для любого объекта, кроме тех, которые обрабатываются напрямую (например, String, Int, Long, Double, Float, ByteArray), вам нужно либо разбить их на такие обрабатываемые объекты, либо иметь преобразователь типов, который будет преобразовывать объект в и из такой объект.
Например, на основе вашего аннотированного класса ListEntity @Entity тогда: -
Комната должна знать о преобразователях типов. Итак, вам нужна аннотация @TypeConverters. Его размещение определяет масштаб. Использование аннотации, предшествующей аннотации @Database, имеет наиболее далеко идущие возможности.
Нужен ли класс ведьмы, расширяющий RoomDatabase? Да. Однако он должен быть абстрактным классом и должен иметь абстрактную функцию для извлечения экземпляра каждого аннотированного интерфейса
@Dao(или абстрактного класса, и в этом случае функции должны быть абстрактными, нет необходимости в абстрактном классе с Kotlin в качестве функций в интерфейсе может быть тел)).
Этот класс должен быть аннотирован аннотацией @Database, параметр entities аннотации должен включать список классов для каждой таблицы (@Entity аннотированный класс). например
@TypeConverters(value = [ListEntity.RoomTypeConverters::class])
@Database(entities = [ListEntity::class], exportSchema = false, version = 1)
abstract class TheDatabase: RoomDatabase(){
}
Однако использование вышеизложенного вместе с вашими классами приводит к ошибке сборки в соответствии с: -
ListEntity.java:11: error: Cannot figure out how to save this field into database. You can consider adding a type converter for it. private final a.a.so74708202kotlinroomentitydesign.CheckUnCheckItem id = null;
Изменение класса RoomTypeConverters следующим образом: -
class RoomTypeConverters{
@TypeConverter
fun convertItemListToJSONString(invoiceList: Item): String = Gson().toJson(invoiceList)
@TypeConverter
fun convertJSONStringToItemList(jsonString: String): Item = Gson().fromJson(jsonString,Item::class.java)
@TypeConverter
fun convertCheckUnCheckItemToJSONString(cuc: CheckUnCheckItem): String = Gson().toJson(cuc)
@TypeConverter
fun convertJSONStringToCheckUnCheckItem(jsonString: String): CheckUnCheckItem = Gson().fromJson(jsonString,CheckUnCheckItem::class.java)
}
Решает проблему сборки, и теоретически у вас есть потенциально пригодная для использования база данных.
Однако вам, очевидно, нужен код для доступа к базе данных. Таким образом, вы, скорее всего, хотели бы иметь. как упоминалось ранее, аннотированный интерфейс @Dao, например
@Dao
interface TheDAOs {
@Insert
fun insert(listEntity: ListEntity): Long
@Query("SELECT * FROM ${TABLE_NAME}")
fun getAll(): List<ListEntity>
}
Этого будет достаточно для вставки строк в базу данных и для извлечения всех строк из базы данных в файл List<ListEntity).
Из базы данных built вам нужно получить экземпляр TheDAO, и, таким образом, аннотированный класс @Database может быть
:-
@TypeConverters(value = [ListEntity.RoomTypeConverters::class])
@Database(entities = [ListEntity::class], exportSchema = false, version = 1)
abstract class TheDatabase: RoomDatabase(){
abstract fun getTheDAOsInstance(): TheDAOs
}
Чтобы продемонстрировать фактическое использование вышеизложенного, рассмотрите следующий код в действии: -
class MainActivity : AppCompatActivity() {
lateinit var roomDBInstance: TheDatabase
lateinit var theDAOs: TheDAOs
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
roomDBInstance = Room.databaseBuilder(this,TheDatabase::class.java,"The_database_name.db")
.allowMainThreadQueries() /* NOTE ADDED FOR CONVENIENCE AND BREVITY */
.build()
/* Note the database itself does not yet exist, it's creation is delayed until an attempt is made to access it. So:- */
theDAOs = roomDBInstance.getTheDAOsInstance() /* Still the database is not created/accessed */
showData(theDAOs.getAll()) /* No data has been added BUT the database will now exist */
theDAOs.insert(
ListEntity(
id = CheckUnCheckItem(
check = Item (
url = "URL001",
text = "TEXT001",
color = "RED"
),
unCheck = Item(
url = "URL002",
text = "TEXT002",
color = "BLUE"
)
),
check = Item(url = "URL003", text = "TEXT003", color = "WHITE"),
unCheck = Item(url = "URL004", text = "TEXT004", color = "BLACK"),
url = "URL005", text = "TEXT005", size = "BIG"
)
)
showData(theDAOs.getAll())
}
fun showData(listEntities: List<ListEntity>) {
for (li in listEntities) {
Log.d(
"DBINFO",
"id is $li.id.check.url${li.id.check.text}.... " +
"\n\tcheck is ${li.check.url} .... " +
"\n\tuncheck is ${li.unCheck.url} ...." +
"\n\turl is ${li.url} text is ${li.text} size is ${li.size}"
)
}
}
}
Вывод в журнал: -
D/DBINFO: id is ListEntity(id=CheckUnCheckItem(check=Item(url=URL001, text=TEXT001, color=RED), unCheck=Item(url=URL002, text=TEXT002, color=BLUE)), check=Item(url=URL003, text=TEXT003, color=WHITE), unCheck=Item(url=URL004, text=TEXT004, color=BLACK), url=URL005, text=TEXT005, size=BIG).id.check.urlTEXT001....
check is URL003 ....
uncheck is URL004 ....
url is URL005 text is TEXT005 size is BIG
База данных через проверку приложений "-
Итак, наконец
верны ли мои данные и классы сущностей?
С точки зрения базы данных да, они работают после нескольких поправок. Однако я подозреваю, что ваши классы, вероятно, не такие, как вы предполагали.
Если бы к этому подходили с точки зрения базы данных и нормализовали, без раздувания и без необходимости преобразователей типов, то рассмотрите следующее:
Встроенные элементы (снять отметку и проверить) в основном повторяются, поэтому, вероятно, это может быть таблица (связанная с db_table). Отсюда 2 таблицы. Один для ListEntity (альтернативный), а другой для элементов (AlternativeItem), поэтому 2 аннотированных класса @Entity могут быть: -
/* Alternative Approach */
@Entity(
/* Foreign Keys NOT REQUIRED, they enforce Referential Integrity */
foreignKeys = [
ForeignKey(
entity = AlternativeItem::class,
parentColumns = ["alternativeItemId"],
childColumns = ["unCheckIdMap"]
/* OPTIONAL within a Foreign Key, they help automatically maintain Referential Integrity*/,
onDelete = ForeignKey.CASCADE,
onUpdate = ForeignKey.CASCADE
),
ForeignKey(
entity = AlternativeItem::class,
parentColumns = ["alternativeItemId"],
childColumns = ["checkIdMap"],
onDelete = ForeignKey.CASCADE,
onUpdate = ForeignKey.CASCADE
)
]
)
data class Alternative(
@PrimaryKey
val id: Long?=null,
@ColumnInfo(index = true)
val unCheckIdMap: Long, /* map to the id of the related Item (AlternativeItem) for the uncheck */
@ColumnInfo(index = true)
val checkIdMap: Long, /* map to the id of the related Item (AlternativeItem) for the uncheck */
val url: String,
val text: String,
val size: String
)
@Entity
data class AlternativeItem(
@PrimaryKey
val alternativeItemId: Long?=null,
val alternativeItemUrl: String,
val alternativeItemText: String,
val alternativeItemColor: String
)
Поскольку вам обычно нужна альтернатива вместе со связанными с ней AlternativeItems, тогда POJO, который обеспечивает единство: -
data class AlternativeWithUncheckAndCheck(
@Embedded
val alternative: Alternative,
@Relation(entity = AlternativeItem::class, parentColumn = "unCheckIdMap", entityColumn = "alternativeItemId")
val unCheck: AlternativeItem,
@Relation(entity = AlternativeItem::class, parentColumn = "checkIdMap", entityColumn = "alternativeItemId")
val check: AlternativeItem
)
В аннотированном интерфейсе @Dao потребуются некоторые дополнительные функции, поэтому: -
@Insert
fun insert(alternative: Alternative): Long
@Insert
fun insert(alternativeItem: AlternativeItem): Long
@Transaction
@Query("")
fun insertAlternativeAndUncheckAndCheck(alternative: Alternative, uncheck: AlternativeItem, check: AlternativeItem): Long {
var uncheckId = insert(uncheck)
var checkId = insert(check)
return insert(Alternative(null,url = alternative.url, text = alternative.text, size = alternative.size, unCheckIdMap = uncheckId, checkIdMap = checkId ))
}
@Transaction
@Query("SELECT * FROM alternative")
fun getAllAlternativesWithRelatedUnCheckAndCheck(): List<AlternativeWithUncheckAndCheck>
insertAlternativeAndUncheckAndCheck делает то, что говорит (обратите внимание, что он слишком прост и может потребовать некоторых улучшений, чтобы расширить принцип)Чтобы продемонстрировать это, все, что требуется, — это добавить новые объекты в entities parameter, а затем добавить некоторый код в действие.
Исправленная аннотация @Database: -
@Database(entities = [ListEntity::class, /* for the alternative approach */ Alternative::class, AlternativeItem::class], exportSchema = false, version = 1)
Код действия (который обслуживает оба подхода в аналогичном/эквивалентном способе хранения и извлечения данных): -
class MainActivity : AppCompatActivity() {
lateinit var roomDBInstance: TheDatabase
lateinit var theDAOs: TheDAOs
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
roomDBInstance = Room.databaseBuilder(this,TheDatabase::class.java,"The_database_name.db")
.allowMainThreadQueries() /* NOTE ADDED FOR CONVENIENCE AND BREVITY */
.build()
/* Note the database itself does not yet exist, it's creation is delayed until an attempt is made to access it. So:- */
theDAOs = roomDBInstance.getTheDAOsInstance() /* Still the database is not created/accessed */
showData(theDAOs.getAll()) /* No data has been added BUT the database will now exist */
theDAOs.insert(
ListEntity(
id = CheckUnCheckItem(
check = Item (
url = "URL001",
text = "TEXT001",
color = "RED"
),
unCheck = Item(
url = "URL002",
text = "TEXT002",
color = "BLUE"
)
),
check = Item(url = "URL003", text = "TEXT003", color = "WHITE"),
unCheck = Item(url = "URL004", text = "TEXT004", color = "BLACK"),
url = "URL005", text = "TEXT005", size = "BIG"
)
)
showData(theDAOs.getAll())
/* Alternative equivalent */
theDAOs.insertAlternativeAndUncheckAndCheck(
Alternative(url = "URL005", size = "BIG", text = "TEXT005", checkIdMap = -1, unCheckIdMap = -1),
check = AlternativeItem(alternativeItemUrl = "URL001", alternativeItemText = "TEXT001", alternativeItemColor = "RED"),
uncheck = AlternativeItem(alternativeItemUrl = "URL002", alternativeItemText = "TEXT002", alternativeItemColor = "BLUE" )
)
showAlternativeData(theDAOs.getAllAlternativesWithRelatedUnCheckAndCheck())
}
fun showData(listEntities: List<ListEntity>) {
for (li in listEntities) {
Log.d(
"DBINFO",
"id is $li.id.check.url${li.id.check.text}.... " +
"\n\tcheck is ${li.check.url} .... " +
"\n\tuncheck is ${li.unCheck.url} ...." +
"\n\turl is ${li.url} text is ${li.text} size is ${li.size}"
)
}
}
fun showAlternativeData(listAlternatives: List<AlternativeWithUncheckAndCheck>) {
for (la in listAlternatives) {
Log.d("DBALTINFO",
"id is ${la.alternative.id} URL is ${la.alternative.url} TEXT is ${la.alternative.text} SIZE is ${la.alternative.size} " +
"\n\t UNCHECK id is ${la.unCheck.alternativeItemId} url is ${la.unCheck.alternativeItemUrl} text is ${la.unCheck.alternativeItemText} color is ${la.unCheck.alternativeItemColor}" +
"\n\t CHECK id is ${la.check.alternativeItemId} url is ${la.check.alternativeItemUrl} text is ${la.check.alternativeItemText} color is ${la.check.alternativeItemColor}")
}
}
}
При запуске результат теперь: -
D/DBINFO: id is ListEntity(id=CheckUnCheckItem(check=Item(url=URL001, text=TEXT001, color=RED), unCheck=Item(url=URL002, text=TEXT002, color=BLUE)), check=Item(url=URL003, text=TEXT003, color=WHITE), unCheck=Item(url=URL004, text=TEXT004, color=BLACK), url=URL005, text=TEXT005, size=BIG).id.check.urlTEXT001....
check is URL003 ....
uncheck is URL004 ....
url is URL005 text is TEXT005 size is BIG
D/DBALTINFO: id is 1 URL is URL005 TEXT is TEXT005 SIZE is BIG
UNCHECK id is 1 url is URL002 text is TEXT002 color is BLUE
CHECK id is 2 url is URL001 text is TEXT001 color is RED
База данных через App Inspection (в отношении альтернативного подхода): -
и :-
то есть хранятся только фактические данные, BLOAT (описания полей/типов, разделители, вложенные данные) не сохраняется, таким образом
Однако отрицательным является то, что требуется больше кода и размышлений.
Обратите внимание, что этот ответ предназначен для рассмотрения основных принципов и, безусловно, не является полностью исчерпывающим.