安装 Exposed 框架完成之後,再来我们要和资料库进行串接。
首先我们将原本的 main(){}
改成
fun main() {
Database.connect("jdbc:h2:mem:test", driver = "org.h2.Driver")
}
这里对接的是 Java 的 H2 资料库,在这边我们利用这个资料库做一个简单的连接测试。
不过因为我们还没有安装 H2 Driver 的缘故,执行 main
时,我们会看到以下错误讯息
Exception in thread "main" java.lang.ClassNotFoundException: org.h2.Driver
要修正这个问题,我们就要安装 H2 Driver
跟安装 Exposed 框架时类似,我们在 build.gradle.kts
的 dependencies {}
段落加上
implementation("com.h2database:h2:1.4.200")
重新 Load Gradle Change
套件同步完成之後,我们的程序就可以顺利执行了。
不过因为我们还没开始撰写与资料库连线相关的程序,我们的程序仅仅是顺利的执行完成,并没有任何和资料库的互动。
首先,我们先来尝试建立一个新的资料表,先在程序最开头加入
import org.jetbrains.exposed.dao.id.IntIdTable
这里引用了 Exposed 框架所定义的 IntIdTable
。
在 main
的外面,我们定义一个 Cities
的object
object Cities: IntIdTable() {
val name = varchar("name", 50)
}
然後,我们在 Database.connect()
後面,加入一段与资料库的互动交易
transaction {
// 加上 StdOutSqlLogger 将 SQL 印出结果
addLogger(StdOutSqlLogger)
// 建立 CITIES 资料表
SchemaUtils.create (Cities)
}
执行这段程序後,我们会看到以下结果
SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
SLF4J: Defaulting to no-operation (NOP) logger implementation
SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.
SQL: SELECT VALUE FROM INFORMATION_SCHEMA.SETTINGS WHERE NAME = 'MODE'
SQL: CREATE TABLE IF NOT EXISTS CITIES (ID INT AUTO_INCREMENT PRIMARY KEY, "NAME" VARCHAR(50) NOT NULL)
Process finished with exit code 0
到这里,我们可以看到我们成功的印出了和资料库互动的 SQL 语法。
也证明我们成功的利用 Exposed 框架和资料库进行互动了。
错误讯息里面提到的 SLF4J,是Simple Logging Facade for Java 的简称。
这是 Java Logging 的一个套件,在这边因为设置不完善,所以抛出错误。
虽然并不影响程序的运行,不过看到 SLF4J 抛出的错误,总是有点影响後面开发的顺畅感。
这边,我们将设置处理正常,以便於我们後面进行开发。
首先我们安装新的 SLF4J,在 build.gradle.kts
的 dependencies {}
段落内加上
implementation("org.slf4j:slf4j-api:1.7.32")
implementation("org.slf4j:slf4j-log4j12:1.7.32")
别忘记要重新 Load Gradle Change,这已经是第三次了
套件同步完毕之後,再次执行程序,应该会看到
log4j:WARN No appenders could be found for logger (Exposed).
log4j:WARN Please initialize the log4j system properly.
log4j:WARN See http://logging.apache.org/log4j/1.2/faq.html#noconfig for more info.
的错误讯息。
这是因为,我们虽然成功安装了 SLF4J 套件,
但是我们并没有撰写相关的设置,导致 SLF4J 无法成功的找到该出现的 logger。
要修正这个问题,我们要在 src/main/resources/
资料夹里面,
加入 log4j.properties
这个档案,并在档案里面加入以下内容
log4j.rootLogger=ERROR
这样设置後,我们重新执行程序,就不会看到 SLF4J 的错误讯息了。
下面我们来说明
object Cities: IntIdTable() {
val name = varchar("name", 50)
}
fun main() {
Database.connect("jdbc:h2:mem:test", driver = "org.h2.Driver")
transaction {
addLogger(StdOutSqlLogger)
SchemaUtils.create (Cities)
}
}
这段程序和资料库的互动,具体来说是怎麽一回事
object
首先我们注意到的,可能是这个在其他语言比较少见的关键字:object
。
这个关键字的用途是什麽呢?在 kotlin 的官方文件里,针对 Object declarations 是这麽说的:
The Singleton pattern can be useful in several cases, and Kotlin makes it easy to declare singletons:
这边我们假设读者应该对单例模式(Singleton pattern)略有概念,如果有疑惑的话,可以去看看设计模式学习笔记的文章,这边就不多做说明。
object
这个关键字简单的说,也就是原本在其他语言里所使用的 Singleton pattern,在 kotlin 内不仅一样可以使用,甚至还帮这个 pattern 建立一个关键字,让它使用起来更方便。
与 Java 的 Singleton pattern 写法比较:
public class ThisIsASingleton {
private static ThisIsASingleton instance = new ThisIsASingleton();
private ThisIsASingleton(){}
public static ThisIsASingleton getInstance(){
return instance;
}
}
Kotlin 只要这麽写就可以建立一个 Singleton:
object ThisIsASingleton {
}
如何,是不是简单很多呢?
transction{}
函式不需要小括号?再来,我们看到 transction()
这个函式,为什麽在我们的程序码里面,transction()
没有小括号就可以直接宣告了呢?
首先,我们要知道,Kotlin 跟传统的 OOP 语言不太一样,除了传统语言的多形、继承⋯⋯等语言特性,Kotlin 还支援了许多函数式导向程序语言(Functional Programming)的特性,比方说可以将函数作为参数传递。
理解这件事情之後,我们再看 Kotlin 官方文件针对 Passing trailing lambdas 的说明:
According to Kotlin convention, if the last parameter of a function is a function, then a lambda expression passed as the corresponding argument can be placed outside the parentheses:
也就是说,当一个函数的最後参数也是一个函数,那我们可以直接将这个函数写在 {}
里面。乍看说明文件时,或许会觉得这是一个很神奇的设计,但是实际和 transction()
实作的程序码比对一下:
fun <T> transaction(db: Database? = null, statement: Transaction.() -> T): T
如果不是透过 Passing trailing lambdas 的写法,那麽就会变成我们得先宣告一个函数,包含建立资料库的逻辑,最後再将这个函数放在 transaction()
的参数内。与这个写法相比,直接放在大括弧内,看起来其实更加直观,语法也更加简洁。
理解了Passing trailing lambdas 的写法之後,transaction()
函数的动作就比较容易了解了,其实就是根据前面 Database.connect("jdbc:h2:mem:test", driver = "org.h2.Driver")
建立好的连线内容,执行一段资料库互动的交易(transaction),内容根据最後所提供的函数逻辑即可。
在这段程序里面,我们提供的逻辑为
addLogger(StdOutSqlLogger)
SchemaUtils.create (Cities)
这两个函数都很直观,addLogger(StdOutSqlLogger)
是加上 SQL Logger 协助印出 Query 内容,SchemaUtils.create (Cities)
则是根据 Cities
单例的结构建立资料表。
到这边,我们就成功的和资料库进行了互动,并且也详细说明了互动所用到的 Kotlin 专属语法以及建立资料表的方式。
<<: Amazon Linux 2 上解决跨来源资源共用 (CORS) 与开机自动启动 uwsgi - Day 09
本篇是 30 天铁人赛的最後一篇,本篇做个小节与心得 课程内容与代码会放在 Github 上: ht...
前言 在学程序之前当然就是要先选择好适合自己的编译器啦~ 有许许多多的网页开发工具中如何选择呢? 我...
Day 27: 暴力破解 WPA/WPA2 加密 wifi 密码 tags: Others 自我挑战...
Hello, 各位 iT邦帮忙 的粉丝们大家好~~~ 本篇是 Re: 从零开始用 Xamarin 技...
如标题,这篇想和大家聊聊「列表推导式」是什麽东西 我们先看看范例再说明,这样大家会比较好理解 Ex ...