在Ktor的服务端项目中植入数据库,实现对数据的增删改查

在上一篇文章从服务端到客户端,一次Ktor的跨端实践中我们已经知道了如何去使用Ktor创建一个简单的服务端项目,开发接口,并在自己的demo中去调用接口去展示数据,但是美中不足的是所使用的数据是我们自己造的假数据,真正的研发过程中,服务端往往是会通过数据库与数据打交道,所以今天我们就来一步步学习下如何在一个ktor的服务端项目中植入一个数据库,并实现基本的增删改查操作,以及优化下上一篇文章中做的客户端demo,让它这次可以跟服务端有个数据的交互

第一步:引入依赖

首先在gradle.properties文件中加入两个版本号,分别是Exposed和H2两个库

然后在build.gradle.kts文件中添加Exposed与H2两个库的依赖

第二步:创建数据模型与数据库表

新建一个PhotoTable.kt文件,在里面加入数据类Photo,另外创建一个objectPhotos作为我们的数据表,Photos继承自Exposed库里面的Table

在表中id作为主键自增长,title保存着图片的名字,imgUrl保存着图片的链接

第三步:连接数据库

连接数据库需要用到Exposed库里面的Database类,它会调用connect函数来连接数据库,connect函数里面需要传入两个参数,一个是jdbc链接,另一个是驱动名字,所以首先创建一个object类DatabaseFactory,建立一个init函数,在里面去连接数据库

注意这里的driverClassNamejdbcURL是写死的两个值,除非是自己去自定义数据库,建立好连接以后,在connect函数底下调用transaction函数,在函数体内部我们就可以去创建表,创建的方式是调用SchemaUtils.create函数,里面传入我们之前声明好的Photos类,这样表就创建好了,考虑到每次访问数据库都需要开启一个事务,我们在DatabaseFactory中新增一个挂起函数dbQuery,这个函数接受一个挂起的函数类型参数,在这个函数类型参数里面我们去执行每一次对数据库的操作

最后在程序启动的时候,在Application.module函数中调用一下Databasefactory.init函数,我们数据库就完成了所有初始化的工作

第四步:访问数据

完成了对数据库的初始化工作以后,就该对里面的数据进行操作了,对数据库最基本的操作无非就是增删改查,所以接下来我们也添加上增删改查的api,第一步先创建个interface,命名为PhotoDao.kt,在里面定义我们需要的操作,有这么几个

既然都是数据库操作,所以它们都属于比较耗时的,所以将它们都定义为挂起函数,然后就是去实现这些接口了,这里有个快捷方式可以一键生成impl类,那就是点击左上角的那个灯泡,在下来菜单中点击implement interface

接下去就会询问你是否创建PhotoDaoImpl.kt类,一直点ok就可以了,最终得到了我们的实现类

现在就可以在实现类里面写数据库操作的代码了,不过在这之前我们需要增加一个函数,这个函数用来将从数据库里获得的对象也就是ResultRow,转换成我们需要用的数据类也就是Photo

查询所有数据

函数allPhotos的功能就是将表中的所有数据都读出来,使用selectAll函数来实现,然后使用map操作符将数据通过resultRowToPhoto函数转换成List<Photo>

查询单条数据

查询单个数据使用select函数进行,与selectAll不同的是,select函数接受一个SqlExpressionBuilder为接收者的lambda表达式,我们在表达式里面添加查询的条件,比如我们这里的条件就是与传入的id相同的数据,那么就可以这样写

eq就表示相等的意思,除此之外还可以使用其他操作符比如less, greater, lessEq, greaterEq 等来表示不同的条件,singleOrNull表示如果返回结果数组大小为1,那么直接返回,否就返回空

新增数据

需要插入数据的时候我们使用insert函数,insert也接受一个lambda表达式,表达式里面有个InsertStatement对象,我们调用这个对象的set操作符将对应字段插入到对应Column里面去

更新数据

更新一条数据使用update函数,该函数接收三个参数,第一个where是一个SqlExpressionBuilder为接收者的lambda表达式,用来填写条件来查找需要更新的数据,第二个参数是limit,表示最大可更新的数据量,第三个参数是body,是调用UpdateStatementset操作符来执行更新数据的操作,update函数会返回一个int结果,当结果大于0的时候表示更新成功

删除数据

删除数据使用的是deleteWhere函数,这个函数接收三个参数,第一个参数为limit,表示删除的最大数量,第二个参数为offset,表示删除的偏移量,第三个参数删除的条件语句,也是一个SqlExpressionBuilder为接收者的lambda表达式,最终是否删除成功也是根据deleteWhere的返回值是否大于0来判断

就这样增删改查的所有操作的写完了,最后在PhotoDaoImpl.kt文件里面添加一个PhotoDao的顶层属性,用来提供给外界来访问对数据库进行操作

第五步:实战

展示数据

数据库部分写完了,我们现在可以来实际演练一下,先更改一下上一篇文章中写好的queryPhotos接口,原本这个接口返回的是造出来的假数据,现在我们可以让这个接口直接去数据库里面查询,将所有数据都读出来,代码更改如下

现在如果重新运行一下之前写好的图片列表demo,访问的接口就是从我们的数据库里面拿数据了,不过在运行之前我们先给数据库里面默认添加一条,因为现在整个表没数据,就算运行了客户端代码,页面也是空的

当创建PhotoDao的时候,就判断如果表中没有数据,就默认添加一条进去,现在我们运行一下服务端项目,然后打开我们的客户端,看看效果

我们看到界面上展示的数据与ktor打印出来的日志相吻合,说明我们已经将数据库中的数据展示出来了。

添加数据

现在让我们扩展一下,增加一个添加图片的功能,首先服务端那边需要添加一个addPhoto的接口,当客户端调用addPhoto接口的时候,服务端获取到请求,判断传过来的数据是否不为空,如果不是空的话就往数据库中插入一条数据,所以首先我们在服务端项目中打开Routing.kt文件,在原有的queryPhotos接口下面添加上下面这段代码

一般这种添加数据的操作都是post的请求,所以这里先调用了post函数,里面传入我们接口的路径addPhoto,在函数体里面我们调用call.receive函数用来接受客户端传过来的参数body,AddPhotoRequest是接收过来的参数的数据模型

当接口从参数中获取title字符串后,判断是否为空,如果不为空就插入到数据库,图片链接我们就随机获取的一个链接,最终再将数据库中的所有数据再返还给客户端,在客户端项目中,我们也添加上addPhoto接口的post请求,代码如下

AddImageRequest为客户端调用addPhoto接口的请求体,接口加好之后,我们在界面上做一下修改,由于是要添加图片,所以要有一个编辑框去编辑图片的title,然后还需要一个按钮去触发addPhoto接口,更改后的界面代码如下所示

然后在ButtononClick事件中调用添加图片的接口

当接口成功返回数据之后,我们将编辑框的内容清空,然后把返回的新的图片数据赋值给feedList,界面就刷新了,分别运行下服务端与客户端项目,看看效果

我们看到当点击添加按钮之后,控制台那边返回了两条数据,我们界面上也对应着展示出了两条数据,说明我们这个添加图片的功能完成了

删除数据

最后我们再来一个删除数据的操作,删除数据就是把id传给服务端,服务端接收到id之后,再用这个id去数据库中执行删除操作,删除成功后再返回一个状态给客户端,客户端刷新列表,首先我们在服务端这里新增一个接收删除数据传参的数据类DeletePhotoRequest.kt

然后在Routing.kt文件里面新增一个接口叫deletePhoto,代码如下

当删除数据成功之后,服务端返给客户端一个status:ok的json数据,如果删除失败,那么返给客户端一个status:fail的json数据,然后运行一遍服务端代码重启下服务,我们回到客户端的代码中,首先在LazyColumn的每个item底下新增一个删除按钮

clickable函数里面就是调用删除图片的接口,现在我们添加上调用接口的代码,在之前添加图片的接口底下新增deletePhoto接口

DeleteImageResponseDeleteImageRequest分别是调用deletePhoto接口的返回数据和请求参数数据类

然后在删除按钮的clickable函数中调用删除图片的接口

我们看到在接口请求成功的回调那里,我们将被删除的id赋值给了一个变量deleteId,deleteIdremember出来的一个Int变量,目的是当deleteId值发生改变之后可以刷新列表数据,所以我们还需要将请求列表数据的接口包在LaunchedEffect函数体内,deleteId作为参数传给LaunchedEffect,这样当deleteId发生改变的时候就又会请求一遍列表数据

现在来跑一遍客户端代码看看效果

总结

头一次写demo有种既当爹又当妈的感觉,自己要啥接口自己写,写完接口自己调,虽说目前这些服务端代码写的还比较不规范,不能跟真正的大项目代码做比较,但是也是托Ktor的福,让我也接触到了一点服务端开发的皮毛,后面也会继续调研这方面的技术,有收获了就会拿出来分享给大家~

相关推荐
Yaml41 小时前
智能化健身房管理:Spring Boot与Vue的创新解决方案
前端·spring boot·后端·mysql·vue·健身房管理
小码编匠2 小时前
一款 C# 编写的神经网络计算图框架
后端·神经网络·c#
AskHarries2 小时前
Java字节码增强库ByteBuddy
java·后端
佳佳_2 小时前
Spring Boot 应用启动时打印配置类信息
spring boot·后端
许野平4 小时前
Rust: 利用 chrono 库实现日期和字符串互相转换
开发语言·后端·rust·字符串·转换·日期·chrono
BiteCode_咬一口代码5 小时前
信息泄露!默认密码的危害,记一次网络安全研究
后端
萌面小侠Plus5 小时前
Android笔记(三十三):封装设备性能级别判断工具——低端机还是高端机
android·性能优化·kotlin·工具类·低端机
齐 飞5 小时前
MongoDB笔记01-概念与安装
前端·数据库·笔记·后端·mongodb