[Day 26] review 一下我们的程序,谈谈 DSL 和 DAO 的差异

前面讲了很多 Kotlin Exposed 框架使用的方式。

今天来讲点观念性的东西,谈谈 Exposed 框架内 DAO 和 DSL 的差异。

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))

要从叫做 Caroluser 取出所有对应的 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 也是不可或缺的。

总结来说,两种资料库的存取方式,随着专案本身的大小以及属性,随着共同维护人员的经验,甚至包含到资料库目前的状况,可能最适用的做法会有所不同。

以作为後端工程的我们来说,应该要想办法根据实际情况,挑选最适合目前团队与专案的做法,来让程序码变得更好维护。


<<:  JavaScript Day16 - 箭头函式

>>:  #17 Automation (5)

Day.13 Crash Recovery - InnoDB 架构 -> MYSQL 二阶段提交(2PC) _1

今天开始的主题有关於MYSQL的crash-safe能力(二阶段提交),如何保证服务在任何时间发生...

Day28-介绍 Redux DevTools

这篇要介绍的是 Redux DevTools,是一个可以纪录及操作存在 Redux store 内的...

【踩坑】codepen没有出现fontawesome的icon!

有时候想要刻一些小物件,或是需要分享程序码给人以便请教问题 我都会使用codepen (真的是初学者...

NIST SP 800-53 R5的摘要

-安全和隐私控制系列(来源:NIST SP 800-53 R5) .安全和隐私控制有效性解决了正确...

mostly:functional 终章:Monad 的实体

可是我的心,比整个宇宙,还要大了那麽一点点。 -- 费尔南多‧佩索亚, 诗选:A Little L...