Spring + Kotlin 实现最简 CURD 应用

在 10 小时前,我还对kotlinspring-boot以及gradle不是很了解,但现在已经知道该如何使用它们运行一个简单的CURD应用了。

于是记录下来,虽然网上已经有很多教程,不过自己写一遍能够加深印象。所以这篇笔记适合和我一样,之前没有接触过javaspring等等这些后端相关知识的人。

项目准备

  • 可以上网的电脑(最好能翻墙)
  • 配置好了java环境
  • 安装了IDEA(虽然可以不用,但最好有)
  • Docker(或者安装好了Mysql数据库并启动)

初始化项目

http://start.spring.io/ 下载脚手架,Group默认为com.exampleArtifact改成kotlinDemo,这里一致后面代码就可以直接拷贝。

初始化项目

这里 kotlinDemo 写成了 koltinDemo 导致后面都是错的,尴尬😓

点击Generate Project后会下载一个压缩包,将压缩包解压到文件夹,使用IDEA打开该文件夹,文件目录应该是这样的:

.
├── build.gradle
├── gradle
│   └── wrapper
│       ├── gradle-wrapper.jar
│       └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
└── src
    ├── main
    │   ├── kotlin
    │   │   └── com
    │   │       └── example
    │   │           └── koltinDemo
    │   │               └── KoltinDemoApplication.kt
    │   └── resources
    │       ├── application.properties
    │       ├── static
    │       └── templates
    └── test
        └── kotlin
            └── com
                └── example
                    └── koltinDemo
                        └── KoltinDemoApplicationTests.kt

打开后会出现该界面:


import Project

表示识别为一个Gradle项目,点击“OK”即可。

然后IDEA会下载依赖,等待下载即可。

如果不成功,翻墙后重试;最难下载的应该是gradle这个包;如果不使用IDEA,使用命令行进入项目根目录,运行./gradlew build

启动项目

下载成功后,先将build.gradledependencies两个依赖先暂时注释:

dependencies {
    // compile('org.springframework.boot:spring-boot-starter-data-jpa')
    compile('org.springframework.boot:spring-boot-starter-web')
    compile("org.jetbrains.kotlin:kotlin-stdlib-jre8:${kotlinVersion}")
    compile("org.jetbrains.kotlin:kotlin-reflect:${kotlinVersion}")
    // runtime('mysql:mysql-connector-java')
    testCompile('org.springframework.boot:spring-boot-starter-test')
}

右下角出现该提示时,表示IDEA识别到修改,点击“Import Changes”。

import changes

spring-boot-starter-data-jpaspring-boot-starter-data-jpa是用来连接数据库的,因为暂时还没有数据库直接运行会无法启动。

注释之后,就可以点击右上角的运行按钮来启动项目。


运行项目

当看到下方控制台打印了如下信息,并且没有报错,就表示运行成功:

成功启动服务器

不使用IDEA则在项目根目录下使用命令./gradlew bootRun运行项目。

如果出现该错误提示:

Cannot determine embedded database driver class for database type NONE
没有配置数据库

则表示需要暂时注释和数据库有关的依赖,或者在resource/application.properties中配置数据库信息。

运行成功后,打开浏览器http://127.0.0.1:8080,可以看到如下页面:

index page

虽然显示Error,但成功表示我们运行起了服务器,只是还没有配置该显示什么内容。

如果是如下内容:

not hava server

就表示根本没有服务器在运行,检查IDEA下面控制台是否和上面成功的截图一样,重新打开浏览器查看。

第一个页面

kotlinDemo文件夹下,新增Controller.kt文件,内容如下:

package com.example.koltinDemo

import org.springframework.web.bind.annotation.*

@RestController
class Controller {
    @RequestMapping("/")
    fun get() = "Hello Spring and kotlin"
}

如果使用IDEA,则更加方便

创建文件

而且只需要在新生成的文件内,添加:

@RestController
class Controller {
    @RequestMapping("/")
    fun get() = "Hello Spring and kotlin"
}

然后点击@RestController,按下键盘option + enter,如果是Windows应该是ctrl + enter?就会自动添加import org.springframework.web.bind.annotation.*这一行。

OK,到此我们第一个页面就写好了,重新运行项目(停止并启动)。刷新页面(如果还没有关闭的话,否则就再次打开127.0.0.1:8080),Wow!页面出现了“Hello Spring and kotlin”,而不是之前让人讨厌的Error字眼。

第一个页面

连接数据库

好了,令人激动的环节到了,这也是折腾了我最久的环节,虽然现在感觉So easy

初始化数据库

我们使用Docker来运行一个数据库供我们使用,很简单:

docker run --name store_db -d -e MYSQL_ROOT_PASSWORD=123 -e MYSQL_DATABASE=store -p 3306:3306 mysql:latest 

然后查看下是否启动成功:

docker ps -a

如果出现


启动 mysql

则表示创建mysql数据库成功,

同时需要注意STATUS必须为Up,且PORTS必须为0.0.0.0:3306->3306/tcp

OK,接下来就直接连接数据库并向其插入数据吧!

配置数据库信息

还记得之前注释的数据库相关依赖吗,将其取消注释(注意Import Changes):

dependencies {
    compile('org.springframework.boot:spring-boot-starter-data-jpa')
    compile('org.springframework.boot:spring-boot-starter-web')
    compile("org.jetbrains.kotlin:kotlin-stdlib-jre8:${kotlinVersion}")
    compile("org.jetbrains.kotlin:kotlin-reflect:${kotlinVersion}")
    runtime('mysql:mysql-connector-java')
    testCompile('org.springframework.boot:spring-boot-starter-test')
}

然后在resource/application.properties中添加如下内容:

spring.datasource.driverClassName=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/store?autoReconnect=true&useSSL=false
spring.datasource.username=root
spring.datasource.password=123
# Hibernate ddl auto (create, create-drop, update)
spring.jpa.hibernate.ddl-auto = create-drop

重新启动项目,刷新页面,和之前一样?哦,那真是太好了,没有错误就是好消息。

创建实体

实体是什么,用来做什么?额,大概就是类似于约定数据库中的表会有什么样的字段,以及如何操作数据库的东西?

增加Entity.kt文件,添加如下内容:

package com.example.koltinDemo

import javax.persistence.*

@Entity
@Table(name = "user")
data class Customer(
        var firstName: String = "",
        var lastName: String = "",

        @Id
        @GeneratedValue(strategy = GenerationType.AUTO)
        var id: Long = 0
)

这将表示我们会在store数据库中,有一张user表,表会有idfirstNamelastName字段。

然后再增加Interface.kt文件,内容如下:

package com.example.koltinDemo

import org.springframework.data.jpa.repository.JpaRepository


interface StoreRepository : JpaRepository<Customer, Long> {
}

插入数据库

然后将Controller.kt改写为如下内容:

package com.example.koltinDemo

import org.springframework.beans.factory.annotation.Autowired
import org.springframework.web.bind.annotation.*

@RestController
class CustomerController
    @Autowired constructor(val repository: StoreRepository) {

    @RequestMapping("/")
    fun findAll() = repository.findAll()

    @RequestMapping("/create", method = arrayOf(RequestMethod.POST))
    @ResponseBody
    fun create(@RequestBody customer: Customer): Customer = repository.save(customer)
}

OK,重启服务,再次刷新页面。页面上没有可爱的Hello Spring了,而是变成了[],但这更可爱好吗,这表示我们从数据中读取数据了!虽然数据库中还是空的。

所以我们要向数据库中插入数据,如果你有Postman那就简单多了,没有也没事,我们使用命令行来实现。

在命令行输入如下内容:

curl -i -H "Content-Type: application/json" -X POST \
-d '{"firstName": "Hello", "lastName": "Spring framework"}' \
http://localhost:8080/create

{"firstName": "Hello", "lastName": "Spring framework"}就是我们要发送的数据。

如果显示

HTTP/1.1 200
Content-Type: application/json;charset=UTF-8
Transfer-Encoding: chunked
Date: Sat, 25 Nov 2017 07:10:14 GMT

{"firstName":"Hello","lastName":"Spring framework","id":1}%

就表示成功了!再次刷新我们的浏览器页面看看,有数据了!!

万一没有,检查是否有HTTP/1.1 200字样,如果没有就用Postman试试看,如下:

使用Postman 插入数据

这样是表示成功,重点是那个200 OK

插入数据成功

查找数据

当然现在也可以算实现了查找,只是查找所有的数据而已,接下来我们实现根据lastName查找数据。

Interface.kt改写为如下:

interface StoreRepository : JpaRepository<Customer, Long> {
    fun findByLastName(name: String): List<Customer>
}

Controller变成这样:

class CustomerController
    @Autowired constructor(val repository: StoreRepository) {

    @RequestMapping("/")
    fun findAll() = repository.findAll()

    @RequestMapping("/create", method = arrayOf(RequestMethod.POST))
    @ResponseBody
    fun create(@RequestBody customer: Customer): Customer = repository.save(customer)
    // 这里是新增的
    @RequestMapping("/{name}")
    fun findByLastName(@PathVariable name: String) = repository.findByLastName(name)
}

重启服务,先随便插入几条不同的数据,访问首页看看效果。

[
  {
    "firstName": "Hello",
    "lastName": "Kotlin",
    "id": 1
  },
  {
    "firstName": "Hello",
    "lastName": "World",
    "id": 2
  },
  {
    "firstName": "Hello",
    "lastName": "ltaoo",
    "id": 3
  },
  {
    "firstName": "Hello",
    "lastName": "Spring-boot",
    "id": 4
  }
]

插入的数据,现在我们要查询lastNameltaoo的记录,看是否存在。在地址栏后面增加/ltaoo,地址变成:http://127.0.0.1:8080/ltaoo,回车,页面只显示一条记录了,而且正是lastNameltaoo的那条,表示查询成功。

更新数据

Controller.kt增加如下代码,就按照之前添加代码的方式位置一样。

@PutMapping("/update")
fun updateUser(@RequestBody user: Customer) {
    repository.save(user)
}

重启服务,同样先随便插入几条数据,然后用命令行或者Postman发送如下数据:

curl -i -H "Content-Type: application/json" -X PUT \                                                                              ltaoo@ltaoodeMacBook-Pro
-d '{"firstName": "UPdate", "lastName": "Data", "id": 1}' \
http://localhost:8080/update

表示发送的数据为{"firstName": "UPdate", "lastName": "Data", "id": 1},且这次发送的方式为PUT,之前插入数据是用POST。除了数字 1 之外,都要用双引号包裹。

这表示要将id为 1 的这条数据,firstName修改为UpdatelastName修改为Data,回车后显示200,就表示成功, 刷新页面也能看到新修改的数据。

删除数据

同样是Controller.kt,增加如下代码:

@DeleteMapping("/del/{id}")
@ResponseBody
fun deleteEmployee(@PathVariable id: Long) {
    repository.delete(id)
}

重启服务,先增加两条记录,然后使用命令行发送如下命令:

curl -i -H "Content-Type: application/json" -X DELETE \                                                                           ltaoo@ltaoodeMacBook-Pro
http://localhost:8080/del/1

表示已DELETE方式请求http://localhost:8080/del/1地址。从这里可以看到增删改查对应了四种请求方式POSTDELETEPUTGET

然后刷新浏览器页面查看所有数据,会发现原本两条数据只剩一条了,表示删除成功。

总结

到此,就完成了一个能够实现向数据库增删改查的Web应用了,全部的代码加起来可能都不超过两百行,这就是Spring-boot的强大之处吧。

当然这个应用很简陋,甚至都算不上应用,只是一个玩具,但这是作为学习Kotlin甚至说学习后端开发的一个起点,一个萌芽。

学习的过程就是不断搜索、学习、总结的过程,总体来说,从中我收获了:

  • 了解了gradle是什么,用来做什么,怎么用。
  • 了解了Spring连接数据库的方式
  • 学习如何从Post请求中接收数据
  • 知道了什么是实体
  • 知道了@RestController@RequestMapping以及@PathVariable这些装饰器?的作用
  • 知道了一个Spring项目基本结构
  • 知道了什么是包package
  • 知道了在IDEA中如何自动导包
  • kotlin语法有了一丢丢的了解

以及很多很多,收获满满,最后很感谢愿意在网上分享博客的同学。

最后附上打包的代码,虽然不知道能不能够直接运行,但能看是肯定的。
https://pan.baidu.com/s/1c2jNs3Y

遇到的问题

在过程中遇到很多问题,有自己代码写错的,也有大小写错误的,但每一个问题都值得记录下来,说不定也能给别人以启发呢。

unresolved reference xxx

复制代码后IDEA提示这个,表示这个变量找不到来源,使用option + enter也不能自动导包,我遇到的可能有两种情况

  • 该变量对应的依赖未在build.gradle中配置
  • 该变量依赖项目中的包,但是路径配置不正确

ApplicationEventMulticaster not initialized - call 'refresh' before multicasting events via the cont

可能是重复写了某个函数,记不太清了。

Error creating bean with name xxx

代码写得有问题,具体上面问题不知道。。。

Cannot determine embedded database driver class for database type NONE

没有配置数据库信息

Warning about SSL connection when connecting to MySQL database

数据库地址后面必须加上?autoReconnect=true&useSSL=falsejdbc:mysql://localhost:3306/Peoples?autoReconnect=true&useSSL=false

参考

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342

推荐阅读更多精彩内容