前面讲了很多 Kotlin Exposed 框架使用的方式。
今天来讲点观念性的东西,谈谈 Exposed 框架内 DAO 和 DSL 的差异。
如果读者跟着这系列文章看到这边,相信会很明显看到,范例程序码里面基本上都是用 DAO 的方式建立资料。
这也是笔者个人比较偏好的存取方式,个人原因是比较习惯这样的维护方式。
以比较具象徵性的多对多关系为例。假设资料表如下
object Users : IntIdTable() {
val name = varchar("name", 50)
}
object UserTag : IntIdTable() {
val user = reference("user", Users)
val tag = reference("tag", Tags)
}
object Tags : IntIdTable() {
val name = varchar("name", 50)
}
用 DAO 的存取方式是
class Tag(id: EntityID<Int>) : IntEntity(id) {
companion object : IntEntityClass<Tag>(Tags)
var name by Tags.name
var users by User via UsersTags
}
class User(id: EntityID<Int>) : IntEntity(id) {
companion object : IntEntityClass<User>(Users)
var name by Users.name
var tags by Tag via UsersTags
}
宣告了这些关联之後,框架会透过 via
去自动找到资料表之间的关联性,并组合出需要的 Query。
如果用 DSL 的话,就得自己去构思里面的 join 逻辑。
假设我们现在资料设置如下:
val carol = User.new {
name = "Carol"
}
val admin = Tag.new {
name = "admin"
}
carol.tags = SizedCollection(listOf(admin))
要从叫做 Carol
的 user
取出所有对应的 user.tags
用 DAO 的方式,并加上 StdOutSqlLogger
协助我们看到 Query 内容
addLogger(StdOutSqlLogger)
val carol = User.find { Users.name eq "Carol" }.first()
carol.tags.forEach{ println(it.name) }
会看到以下的结果
SQL: SELECT USERS.ID, USERS."NAME" FROM USERS WHERE USERS."NAME" = 'Carol'
SQL: SELECT TAGS.ID, TAGS."NAME", USERSTAGS.ID, USERSTAGS.TAG, USERSTAGS."USER" FROM TAGS INNER JOIN USERSTAGS ON TAGS.ID = USERSTAGS.TAG WHERE USERSTAGS."USER" = 1
admin
要用DSL 做出一样的效果,你需要自己组合出这段 Query
addLogger(StdOutSqlLogger)
val carol = Users.select { Users.name eq "Carol" } .first()
val tags = (Tags innerJoin UsersTags).select{ UsersTags.user eq carol[Users.id]}
tags.forEach { println(it[Tags.name]) }
来得到以下的结果
SQL: SELECT USERS.ID, USERS."NAME" FROM USERS WHERE USERS."NAME" = 'Carol'
SQL: SELECT TAGS.ID, TAGS."NAME", USERSTAGS.ID, USERSTAGS."USER", USERSTAGS.TAG FROM TAGS INNER JOIN USERSTAGS ON TAGS.ID = USERSTAGS.TAG WHERE USERSTAGS."USER" = 1
admin
笔者的角度来看,显然 DAO 的程序码更加直观,也更容易进行後续的开发与维护。不过,当资料关联越来越复杂时,用 DAO 的方式进行存取,如果没有对底层的 Query 语法有所认识,很容易会在撰写时不小心误用,导致出现了很复杂的存取语法而不自知。
相对来说,DSL 的存取方式,由於需要在撰写时就理解底层的 Query 语法,相较之下比较不容易在毫无发觉的状况下,建立出很复杂的存取语法。
另外,随着专案开始复杂化,存取资料库的逻辑越发复杂,甚至包含到一些 Stored Procedure 时,为了满足某些 DAO 可能尚未支援的存取方式,要实现这些存取逻辑,DSL 也是不可或缺的。
总结来说,两种资料库的存取方式,随着专案本身的大小以及属性,随着共同维护人员的经验,甚至包含到资料库目前的状况,可能最适用的做法会有所不同。
以作为後端工程的我们来说,应该要想办法根据实际情况,挑选最适合目前团队与专案的做法,来让程序码变得更好维护。
今天开始的主题有关於MYSQL的crash-safe能力(二阶段提交),如何保证服务在任何时间发生...
这篇要介绍的是 Redux DevTools,是一个可以纪录及操作存在 Redux store 内的...
有时候想要刻一些小物件,或是需要分享程序码给人以便请教问题 我都会使用codepen (真的是初学者...
-安全和隐私控制系列(来源:NIST SP 800-53 R5) .安全和隐私控制有效性解决了正确...
可是我的心,比整个宇宙,还要大了那麽一点点。 -- 费尔南多‧佩索亚, 诗选:A Little L...