#android #android-room
#Android #android-комната
Вопрос:
Данный ответ изменил встроенный тип на тип внешнего ключа, я этого не ищу.
У меня есть таблица student и адрес встроенного типа.
Схема учащегося
---- ------ ------------
| id | name | address_id |
---- ------ ------------
| 1 | John | 1 |
| 2 | Jane | 2 |
---- ------ ------------
Схема адресов
---- ------------------
| id | addressLine |
---- ------------------
| 1 | 123 Acme Street |
| 2 | 456 Beach Street |
---- ------------------
Объект Student
@Entity(tableName = "student")
data class Student(
@PrimaryKey var id: Long,
var name: String?,
@Embedded var address: Address // can't change, given answer changed it
)
Адресная сущность
@Entity(tableName = "address")
data class Address(
@PrimaryKey var id: Long,
val addressLine: String
)
Интерфейс DAO
@Dao
interface StudentDao {
@Query("SELECT * FROM student WHERE id = :id INNER JOIN address ON student.address_id = address.id")
fun findById(id: Long): Student
}
Проблема: в настоящее время невозможно заполнить встроенный адрес учащегося.
Ответ №1:
Обычным способом для вашего варианта использования является использование дополнительного класса POJO (не объекта) для получения объединенного результата из обеих таблиц.
Итак, сущности:
@Entity(tableName = "student")
data class Student(
@PrimaryKey var id: Long,
var name: String?,
var address_id: Long // <- you can make it foreign key in addition
)
@Entity(tableName = "address")
data class Address(
@PrimaryKey var id: Long,
val addressLine: String
)
и вспомогательный класс POJO:
data class StudentWithAdress( // <-- you can change the class name to more descriptive one
@Embedded var student: Student
@Embedded var address: Address
)
Затем ваш dao:
@Dao
interface StudentDao {
@Query("SELECT * FROM student WHERE id = :id INNER JOIN address ON student.address_id = address.id")
fun findById(id: Long): StudentWithAdress
}
ОБНОВЛЕНО
Второй способ — использовать взаимно однозначное отношение @вместо этого, используя соединения SQLite
Этот метод использует одни и те же сущности, но запрос и вспомогательный класс немного отличаются:
data class StudentWithAdress(
@Embedded var student: Student
@Relation(
parentColumn = "addressId",
entityColumn = "id"
)
var address: Address
)
и запрос был бы проще:
@Transaction
@Query("SELECT * FROM student WHERE id = :id")
fun findById(id: Long): StudentWithAdress
}
Заключение
Student
класс должен включать ТОЛЬКОaddressId
(не весьAddress
объект), и вы используете отдельный классStudentWithAdress
, который имеет обаStudent
иAddress
. Итак, чтобы вставить значениеStudent
, вам следует вставить егоaddressId
из соответствующейAddress
таблицы. Это обычный и правильный способ.- Оба описанных способа похожи, вы можете выбрать любой. Лично я предпочитаю способ отношений.
- Технически можно использовать только две сущности без вспомогательного класса (и хранить все
Address
содержимое внутриStident
сущности), но я думаю, что это нарушает принцип нормализации реляционной таблицы, и я не хочу включать это в свой ответ, поскольку это противоречит шаблону.
ОБНОВЛЕНО 2 (анти-шаблон)
Я не рекомендую это, но если вы настаиваете, вы можете использовать следующую схему:
@Entity(tableName = "address")
data class Address(
@PrimaryKey var idAddress: Long, // <-- changed
val addressLine: String
)
@Entity(tableName = "student")
data class Student(
@PrimaryKey var id: Long,
var name: String?,
@Embedded var address: Address
)
Как это работает? @Embedded означает, что на самом деле таблица Sqlite Student
включает в себя все поля Address
. В вашем случае в реальной таблице Sqlite будет 4 столбца — id, name, idAddress, AddressLine (именно поэтому необходимо изменить Address
имя первичного ключа, поскольку не может быть полей с одинаковым именем).
Комната немного скрывает это, и вы работаете с Student
объектом через address
поле. Почему это плохо? Давайте посмотрим на сценарий:
- Вы сохраняете
Address
с id = 1, AddressLine = «Некоторый адрес # 1». - Вы устанавливаете этот адрес в каком
Student
-либо объекте и сохраняете его. Под капотом Sqlite вStudent
таблице будет содержать значенияid = 1
,addressLine = Some address #1
. - Затем по какой-то причине вы меняете
addressLine
наAddress
«Некоторый адрес # 2». - Но в
Student
таблице все еще есть старое значениеaddressLine
. Это ошибка. Или вы должны сохранить это изменение вStudent
таблице, и это тоже плохо.
Комментарии:
1. Привет, спасибо за ответ, не могли бы вы также отредактировать и добавить версию «один к одному», это то, что я искал
2. это должен быть объект всего адреса, а не этот ->
var address_id: Long // <- you can make it foreign key in addition
3. Обновил мой ответ. Что касается
this has to be whole Address object, not this
, я думаю, что это неправильный путь, я попытался объяснить это в разделе заключения ответа.4. Спасибо за повторную попытку, я ценю. На самом деле я ищу решение без вспомогательного класса, сглаживаю данные учащихся на корневом уровне и адрес как встроенный. Не могли бы вы сделать еще одну попытку, и вы можете пометить ее как анти-шаблон.
5. Для повторения у меня здесь только два класса: Student и Address.
data class Student( @PrimaryKey var id: Long, var name: String?, @Embedded var address: Address )