没有关系的房间中的一对多关系
Posted
技术标签:
【中文标题】没有关系的房间中的一对多关系【英文标题】:One to many relationship in Room without Relation 【发布时间】:2021-12-19 21:20:52 【问题描述】:这是我想要实现的目标:
User
实体(应用可以有多个用户)
University
对象
每所大学都有多个 Semester
对象
每个学期都有一个Course
对象列表
一个事件实体(一个用户可以有多个事件)
我希望能够:
插入用户 用户插入大学 用户插入学期、课程等 用户可以在需要时访问上述所有内容的列表(只有他自己的数据,而不是其他用户的数据)我如何实现上述目标?我从具有University
的@Embedded
字段的用户对象开始,而该对象又具有@Embedded
字段Semester
,但是如何将用户数据彼此分开?所有这些类都需要一个单独的 Dao 还是一个 UserDao 可以为所有事情工作(因为它们都是嵌套的并使用 @Embedded
?
【问题讨论】:
“Room without Relation 中的一对多关系”——您所描述的是为什么像 SQLite 这样的关系数据库有关系。如果您不想使用它们……那您为什么要使用 Room?例如,为什么不将每个用户存储为 JSON 文件? @CommonsWare 我的意思是不使用 Room 的关系,而不是一般的关系。 Room 的@Relation
是它支持关系数据库的关系方面的方式。
【参考方案1】:
如果您的意思是没有@Relation
而不是没有关系,那么也许考虑以下允许:-
data class UserEvents(
@Embedded
val user: User,
@Embedded
val university: University,
@Embedded
val semester: Semester,
@Embedded
val course: Course
)
并且从下面的演示/示例中将产生类似于以下内容的内容:-
2021-11-06 13:25:28.960 D/EVENTS_1: Event is English part1: Semester is Semester 1 2022, from 2022-01-17 to 2022-03-10:Univeristy is Cambridge:User is Jane
2021-11-06 13:25:28.960 D/EVENTS_1: Event is English part2: Semester is Semester 2 2022, from 2022-03-24 to 2022-06-14:Univeristy is Cambridge:User is Jane
2021-11-06 13:25:28.960 D/EVENTS_1: Event is English part 1: Semester is Semester 1, from 2022-01-15 to 2022-03-31:Univeristy is Oxford:User is Jane
2021-11-06 13:25:28.965 D/EVENTS_1: Event is Mathematcis part2: Semester is Semester 3 2022, from 2022-06-24 to 2022-09-15:Univeristy is Cambridge:User is Fred
2021-11-06 13:25:28.965 D/EVENTS_1: Event is Mathematcis part2: Semester is Semester 4 2022, from 2022-10-03 to 2022-12-15:Univeristy is Cambridge:User is Fred
注意到 Jane 登录时提取了 Jane 的 UserEvents,而 Fred 登录时提取了 Fred 的用户事件。
所以除了 UserEvents POJO 还有@Entityclasses :-
用户:-
@Entity
data class User (
@PrimaryKey
val userId: Long? = null,
val userName: String,
val userPassword: String,
val userOtherData: String
)
大学
@Entity(
indices = [
Index(value = ["universityName"], unique = true)
]
)
data class University(
@PrimaryKey
val universityId: Long?=null,
val universityName: String,
val universityOtherData: String
)
学期
@Entity
data class Semester(
@PrimaryKey
val semesterId: Long?=null,
val semesterName: String,
val semesterStartDate: String,
val semesterEndDate: String,
val semesterUniversityMap: Long
)
课程
@Entity
data class Course(
@PrimaryKey
val courseId: Long?=null,
val courseName: String,
val courseSemesterMap: Long
)
UserCourseMap 注意到提供的关系是多对多的,但这可以促进一对多。
@Entity(
primaryKeys = ["userCourseMapUserId","userCourseMapCourseId"],
indices = [
Index(value = ["userCourseMapCourseId"]
)
])
data class UserCourseMap(
val userCourseMapUserId: Long,
val userCourseMapCourseId: Long
)
上述设计并未将大学、学期或课程指定给特定用户。它们是共享的,例如所以用户 1 添加牛津大学,然后用户 2 尝试它不重复但已经存在等等。
只有用户在课程中的注册,因此学期和大学是特定于用户的。
所有这些类都需要一个单独的 Dao 还是一个 UserDao
AllDao :-
@Dao
abstract class AllDao
@Insert(onConflict = IGNORE)
abstract fun insert(user: User): Long
@Insert(onConflict = IGNORE)
abstract fun insert(university: University): Long
@Insert(onConflict = IGNORE)
abstract fun insert(semester: Semester): Long
@Insert(onConflict = IGNORE)
abstract fun insert(course: Course): Long
@Insert(onConflict = IGNORE)
abstract fun insert(userCourseMap: UserCourseMap): Long
@Query("SELECT universityId FROM University WHERE universityName=:universityName")
abstract fun getUniversityIdByName(universityName: String): Long
@Query("SELECT semesterId FROM semester WHERE semesterName=:semesterName AND semesterUniversityMap=:universityId")
abstract fun getSemesterByNameAndUniversityId(semesterName: String, universityId: Long): Long
@Query("SELECT courseId FROM course WHERE courseName=:courseName AND courseSemesterMap=:semesterId")
abstract fun getCourseByCourseNameAndSemesterId(courseName: String, semesterId: Long): Long
@Query("SELECT coalesce(max(userid),-1) FROM user WHERE userName=:userName AND userPassword =:userPassword")
abstract fun userLogin(userName: String, userPassword: String): Long
@Query("SELECT * FROM usercoursemap " +
"JOIN User on userCourseMapUserId = userId " +
"JOIN course on userCourseMapCourseId = courseId " +
"JOIN semester ON courseSemesterMap = semesterId " +
"JOIN university ON semesterUniversityMap = universityId " +
"WHERE userId=:userId")
abstract fun getUserEvents(userId: Long): List<UserEvents>
如果您想拥有多个 @Daos,由您决定
如何将用户数据彼此分开?
参见上面关于 UserCourseMap 和 getUserEvents dao
如果对上述内容感到满意,我建议考虑定义外键约束以强制执行参照完整性,但为了简洁和降低理解的复杂性而省略了它们。所以用一个非常典型的@Database TheDatabase :-
@Database(entities = [
User::class,University::class,Semester::class,Course::class,UserCourseMap::class,
version = 1)
@TypeConverters(DateTimeConverter::class)
abstract class TheDatabase: RoomDatabase()
abstract fun getAllDao(): AllDao
companion object
@Volatile
private var instance: TheDatabase? = null
fun getInstance(context: Context): TheDatabase
if (instance == null)
instance = Room.databaseBuilder(context,TheDatabase::class.java,"thedatabase.db")
.allowMainThreadQueries()
.build()
return instance as TheDatabase
一个典型的例外是为简洁起见使用了.allowMainThreadQueries
。
最后把它们放在一个演示中(产生上面的输出)MainActivity:-
class MainActivity : AppCompatActivity()
lateinit var db: TheDatabase
lateinit var dao: AllDao
override fun onCreate(savedInstanceState: Bundle?)
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
db = TheDatabase.getInstance(this)
dao = db.getAllDao()
var currentUserId: Long = -1 /* no user not logged in */
/* Add a couple of users */
dao.insert(User(userName = "Fred",userPassword = "passwordforfred", userOtherData = "blah"))
dao.insert(User(userName = "Jane", userPassword = "passwordforjane", userOtherData = "blah"))
/* add some universities, semesters and courses all 3 are globally accessible */
val yaleid = dao.insert(University(universityName = "Yale", universityOtherData = "blah"))
val cambridgeid = dao.insert(University(universityName = "Cambridge", universityOtherData = "blah"))
val semester1yale = dao.insert(Semester(semesterName = "Semester 1 2022", semesterStartDate = "2022-01-23", semesterEndDate = "2022-04-07", semesterUniversityMap = yaleid))
val semester2yale = dao.insert(Semester(semesterName = "Semester 2 2022", semesterStartDate = "2022-04-17", semesterEndDate = "2022-07-01", semesterUniversityMap = yaleid))
val semester3yale = dao.insert(Semester(semesterName = "Semester 3 2022", semesterStartDate = "2022-07-28", semesterEndDate = "2022-10-01", semesterUniversityMap = yaleid))
val semester4yale = dao.insert(Semester(semesterName = "Semester 4 2022", semesterStartDate = "2022-10-25", semesterEndDate = "2022-12-18", semesterUniversityMap = yaleid))
val semester1camb = dao.insert(Semester(semesterName = "Semester 1 2022", semesterStartDate = "2022-01-17", semesterEndDate = "2022-03-10", semesterUniversityMap = cambridgeid))
val semester2camb = dao.insert(Semester(semesterName = "Semester 2 2022", semesterStartDate = "2022-03-24", semesterEndDate = "2022-06-14", semesterUniversityMap = cambridgeid))
val semester3camb = dao.insert(Semester(semesterName = "Semester 3 2022", semesterStartDate = "2022-06-24", semesterEndDate = "2022-09-15", semesterUniversityMap = cambridgeid))
val semester4camb = dao.insert(Semester(semesterName = "Semester 4 2022", semesterStartDate = "2022-10-03", semesterEndDate = "2022-12-15", semesterUniversityMap = cambridgeid))
val coursecambengp1 = dao.insert(Course(courseName = "English part1",courseSemesterMap = semester1camb))
val coursecambengp2 = dao.insert(Course(courseName = "English part2",courseSemesterMap = semester2camb))
val coursecambengp3 = dao.insert(Course(courseName = "English part2",courseSemesterMap = semester3camb))
val coursecambengp4 = dao.insert(Course(courseName = "English part2",courseSemesterMap = semester4camb))
val coursecambmthp1 = dao.insert(Course(courseName = "Mathematics part1",courseSemesterMap = semester1camb))
val coursecambmthp2 = dao.insert(Course(courseName = "Mathematics part2",courseSemesterMap = semester2camb))
val coursecambmthp3 = dao.insert(Course(courseName = "Mathematcis part2",courseSemesterMap = semester3camb))
val coursecambmthp4 = dao.insert(Course(courseName = "Mathematcis part2",courseSemesterMap = semester4camb))
/* Logon in eventually to Jane, after 2 failed login attempts */
currentUserId = dao.userLogin("Not a known user","obviously not a valid password")
if (currentUserId < 1)
/* try again */
currentUserId = dao.userLogin("Fred","wrongpassword")
if (currentUserId < 1)
currentUserId = dao.userLogin("Jane","passwordforjane")
if (currentUserId > 0)
/* all in one add of English part 1 - semster 1 at Oxford (i.e. bar the user all are added in one go) */
dao.insert(
UserCourseMap(
userCourseMapUserId = currentUserId,
userCourseMapCourseId =
dao.insert(
Course(
courseName = "English part 1",
courseSemesterMap = dao.insert(
Semester(
semesterName = "Semester 1",
semesterStartDate = "2022-01-15",
semesterEndDate = "2022-03-31",
semesterUniversityMap = dao.insert(
University(
universityName = "Oxford",
universityOtherData = "blah"
)
)
)
)
)
)
)
)
/* add event (mapping course to user and this implicitly adding semester and uni) to pre-existing */
dao.insert(UserCourseMap(userCourseMapUserId = currentUserId,userCourseMapCourseId = coursecambengp1))
dao.insert(UserCourseMap(userCourseMapCourseId = currentUserId,userCourseMapUserId = coursecambengp2))
/* get the events for Jane */
for(ue: UserEvents in dao.getUserEvents(currentUserId))
Log.d("EVENTS_1",
"Event is $ue.course.courseName: " +
"Semester is $ue.semester.semesterName, from $ue.semester.semesterStartDate to $ue.semester.semesterEndDate:" +
"Univeristy is $ue.university.universityName:" +
"User is $ue.user.userName")
/* SWITCH TO USER FRED */
currentUserId = dao.userLogin("Fred","passwordforfred")
if (currentUserId > 0)
dao.insert(UserCourseMap(userCourseMapUserId = currentUserId,userCourseMapCourseId = coursecambmthp3))
dao.insert(UserCourseMap(userCourseMapUserId = currentUserId,userCourseMapCourseId = coursecambmthp4))
/* Get the events for Fred */
for(ue: UserEvents in dao.getUserEvents(currentUserId))
Log.d("EVENTS_1",
"Event is $ue.course.courseName: " +
"Semester is $ue.semester.semesterName, from $ue.semester.semesterStartDate to $ue.semester.semesterEndDate:" +
"Univeristy is $ue.university.universityName:" +
"User is $ue.user.userName")
【讨论】:
几个问题。忘记事件部分。让我们说它是一个用户,有一个大学列表,每个大学都有自己的学期和课程。这是一对多的关系吧?我是否使用上述逻辑对其进行建模?还是我给课程一个学期的 FK,学期一个大学的 FK? 没有用户拥有(拥有)任何可供所有人使用的大学、学期和课程。仅仅因为 Bert 添加了 Durham Uni 并不意味着 Anne 不能使用 Durham(或任何人添加的任何学期或任何课程)。是的,Uni-Semester 是 1-many,Yes Semseter-Course 是 1-many,所以是的,这就是逻辑。添加 Uni 无需其他任何操作即可完成。学期应该有它的父母大学。课程应该有它的父学期(因此它是祖父大学)。以上是关于没有关系的房间中的一对多关系的主要内容,如果未能解决你的问题,请参考以下文章