Сделал скрин как на картинке.
Как видите, Set information items
находятся в red box
.
Я хочу сохранить эти данные в Room DB
.
red box
состоит из Workout Class
, а blue box
(WorkoutSetInfo class
) входит в него.
Я просто хочу получить список set information
из комнаты.
Но я написал запрос, чтобы получить список set information
,
Я получил следующую ошибку:
error: The columns returned by the query does not have the fields [id,set,weight,reps] in com.example.lightweight.data.db.entity.WorkoutSetInfo even though they are annotated as non-null or primitive. Columns returned by the query: [sets]
public abstract androidx.lifecycle.LiveData<java.util.List<com.example.lightweight.data.db.entity.WorkoutSetInfo>> getWorkoutSetInfoList()
Было несколько подобных вопросов, но я до сих пор не смог их решить. Как я могу получить только список установленной информации?
Информация о тренировках
data class WorkoutSetInfo(
val id: String = UUID.randomUUID().toString(), // For comparison in DiffUtil.
val set: Int,
val weight: String = "",
val reps: String = ""
)
Тренировка
@Entity
data class Workout(
@PrimaryKey(autoGenerate = true)
val id: Int,
val title: String = "",
val unit: String = "kg",
val memo: String = "",
var sets: List<WorkoutSetInfo> = emptyList()
)
ДАО
@Dao
interface WorkoutDao {
@Query("SELECT sets FROM Workout")
fun getWorkoutSetInfoList() : LiveData<List<WorkoutSetInfo>>
@Insert
fun insert(workout: Workout)
}
Репозиторий
class WorkoutRepository(private val workoutDao : WorkoutDao, title: String) {
val workout = Workout(0, title)
var setInfoList : ArrayList<WorkoutSetInfo> = arrayListOf()
val _items: LiveData<List<WorkoutSetInfo>> = workoutDao.getWorkoutSetInfoList()
fun add(item: WorkoutSetInfo) {
setInfoList.add(item)
workout.sets = setInfoList
workoutDao.insert(workout)
}
}
ViewModel
class DetailViewModel(application: Application, title: String) : ViewModel() {
private val repository: WorkoutRepository
private lateinit var _items: LiveData<List<WorkoutSetInfo>>
val items = _items
private val list: List<WorkoutSetInfo>
get() = _items.value ?: emptyList()
init {
val workoutDao = DetailDatabase.getDatabase(application)!!.workoutDao()
repository = WorkoutRepository(workoutDao, title)
_items = repository._items
}
fun addDetail() {
viewModelScope.launch(Dispatchers.IO){
val item = WorkoutSetInfo(set = list.size+1)
repository.add(item)
}
}
}
ОБНОВЛЕНО
class Converter {
@TypeConverter
fun listToJson(value: List<WorkoutSetInfo>) : String {
return Gson().toJson(value)
}
@TypeConverter
fun jsonToList(value: String) : List<WorkoutSetInfo> {
return Gson().fromJson(value, Array<WorkoutSetInfo>::class.java).toList()
}
}
Вы пытаетесь сохранить список сложных объектов в столбце. Комната не знает, как это сохранить.
var sets: List<WorkoutSetInfo> = emptyList()
<-- это сложно
Помните, что комната — это просто sqlite внизу, а ваши объекты представляют собой таблицы. Sqlite распознает базовые типы, но по умолчанию не может хранить список сложных объектов. Вы можете изучить ТипПреобразователи в качестве решения, если хотите сохранить этот объект в той же таблице.
См. ниже реляционное решение, в котором используются отдельные таблицы:
Здесь вы ищете отношение один ко многим. Один Workout
может иметь один или несколько WorkoutSetInfo
В мире реляционных баз данных вы можете представить это, создав отдельную таблицу для WorkoutSetInfo
и используя идентификатор из id
работы для создания отношения.
@Entity
data class WorkoutSetInfo(
val id: String = UUID.randomUUID().toString(), // For comparison in DiffUtil.
val set: Int,
val weight: String = "",
val reps: String = "",
val workoutId: String // <-- this is the id of the workout that this set is associated with
)
После этого вы можете либо написать запрос на соединение, либо использовать объект данных для запроса, как определено здесь.
У вас может быть что-то вроде этого:
data class WorkoutWithSets(
@Embedded val workout: Workout,
@Relation(
parentColumn = "id",
entityColumn = "workoutId"
)
val sets: List<WorkoutSetInfo>
)
И используйте что-то вроде этого, чтобы запросить его
@Transaction
@Query("SELECT * FROM Workout WHERE id = :workoutId")
fun getWorkoutWithSets(workoutId: String) : LiveData<WorkoutWithSets>
Это даст тренировку для данного идентификатора вместе со всеми наборами, которые соответствуют этому идентификатору тренировки в таблице наборов.
Да, WorkoutSetInfo
должна быть отдельной таблицей и содержать столбец для идентификатора тренировки. Затем в своем запросе вы должны использовать столбец trainingId, чтобы получить все WorkoutSetInfo
, связанные с данной тренировкой.
Тогда должны существовать две таблицы, Workout
и WorkoutSetInfo
, WorkoutSetInfo
должна содержать Workout Id
, а к WorkoutSetInfo Table
нужно обращаться с помощью Workout Id
, верно?
Да, если вы посмотрите на мой пример запроса выше, он даст вам то, что вам нужно. Кроме того, убедитесь, что WorkoutSetInfo
является сущностью (также показанной в ответе), которая сообщит Room создать для нее таблицу.
здорово. Но у меня есть вопрос. Ссылка, на которую вы ответили, показывала one-to-many relationship
. Но поскольку это WorkoutSetInfo
для Workout
, разве это не должны быть one-to-one
отношения? (one Workout table
и one WorkoutSetInfo table
)
Это одна WorkoutSetInfo
таблица, но эта таблица может содержать несколько записей для одной тренировки. Одна вещь, о которой не упоминалось, это то, что вы должны убедиться, что всякий раз, когда вы вставляете в свою таблицу тренировок, вы также вставляете соответствующие сеансы тренировок в свою таблицу WorkoutSetInfo
.
Были ли отношения основаны на tuple
, а не на table-to-table
? И я не правильно понял, что каждый раз, когда я вставляю его в workout table
, мне нужно вставлять workout session
и в WorkoutSetInfo table
. Вы хотите сказать, что при вставке кортежа в Workout
как минимум one tuple
также должен быть вставлен в WorkoutSetInfo
?
Давайте продолжить обсуждение в чате.
ок..я вышел из дополнительного чата
Хорошо.. Но я уже использую TypeConverter и получаю сообщение об ошибке. не знаю где проблема..
Я бы порекомендовал задать еще один вопрос о TypeConverter и перечислить конкретно, так как у меня может не быть времени, чтобы следить за этим.
в настоящее время ваша опубликованная ошибка говорит о том, что набор не найден в выборе
data class WorkoutSetInfo(
val id: String = UUID.randomUUID().toString(), // For comparison in DiffUtil.
val set: Int,//Add Any value or make it nullable here for get temp rid of this error
val weight: String = "",
val reps: String = ""
)
но как ответ Навида, вы должны использовать typeConvertor или решить эту проблему с типом реляционной базы данных без реализации этого преобразователя типов или комнаты реляционной структуры вы получите ошибку
Я уже реализовал TypeConverter
(см. update
) Я получил эту ошибку при создании TypeConverter
. Кстати, вы говорите, что можете создать table
, не используя TypeConverter
?
TypeConverter Or Two Table — варианты для вашего случая. Могу ли я узнать, есть ли у вас такая же ошибка после внедрения TypeConverter? Вы пытались сделать set обнуляемым?
Обновлен класс TypeConverter
до исходного плаката. Я использовал TypeConverter и получил ошибку. И я не собираюсь устанавливать set
на null
.
Вы говорите, что для этого должно быть два
tables
изWorkout
иWorkoutSetInfo
, а не просто использовать одну таблицу под названиемWorkout
?