手把手教你在Flutter项目优雅的使用ORM数据库

Flutter ORM数据库介绍

Flutter如今开发上最大的槽点可能就是数据库使用了,Flutter如今只提供了sqflite插件,这代表开发者手动写sql代码,建表、建索引、transation、db线程控制等等繁琐的事情必然接踵而至,这种数据库使用方式是最低效的了。例如IOS平台有coredata、realm等等的框架提供便捷的数据库操做,但来到flutter就又倒退回去裸写sql,这对大部分团队都是重大的成本。android

本文将详细介绍一种在Flutter项目中优雅的使用ORM数据库的方法,咱们使用的ORM框架是包含在一个Flutter插件flutter_luakit_plugin(如何使用可参考介绍文章)中的其中一个功能,本文只详细介绍这套ORM框架的使用和实现原理。咱们给出了一个demogit

咱们demo中实现了一个简单的功能,从一个天气网站上查询北京的天气信息,解析返回的json而后存数据库,下次启动优先从数据库查数据立刻显示,再发请求向天气网站更新天气信息,就这么简单的一个功能。虽然功能简单,可是咱们99%平常的业务逻辑也就是由这些简单的逻辑组成的了。下面是demo运行的效果图。github

image

看完运行效果,咱们开始看看ORM数据库的使用。ORM数据库的核心代码都是lua,其中WeatherManager.lua是业务逻辑代码,其余的lua文件是ORM数据库的核心代码,所有是lua实现的,全部代码文件加起来也就120k左右,很是轻量。sql

针对上面提到的天气信息的功能,咱们来设计数据模型,从demo的展现咱们看到天天天气信息包含几个信息,城市名、日出日落时间、最高温度、最低温度、风向、风力,而后为了区分是哪一天的数据,咱们给每条信息加上个id的属性,做为主键。想好咱们就开始定义第一个ORM数据模型,有几个必要的信息,db名,表名,后面的就是咱们须要的各个字段了,咱们提供IntegerField、RealField、BlobField、TextField、BooleandField。等经常使用的数据类型。weather 就是这个模型的名字,以后咱们weather为索引使用这个数据模型。定义模型代码以下。数据库

weather = {
   __dbname__ = "test.db",
    __tablename__ = "weather",
    id = {"IntegerField",{unique = true, null = false, primary_key = true}},
    wind = {"TextField",{}},
    wind_direction = {"TextField",{}},
    sun_info = {"TextField",{}},
    low = {"IntegerField",{}},
    high = {"IntegerField",{}},
    city =  {"TextField",{}},
 },
复制代码

定义好模型后,咱们看看如何使用,咱们跟着业务逻辑走,首先网络请求回来咱们要生成模型对象存到数据库,分下面几步json

  • 获取模型对象
local Table = require('orm.class.table')
local _weatherTable = Table("weather”)
复制代码
  • 准备数据,创建数据对象
local t = {}
t.wind = flDict[v.fg]
t.wind_direction = fxDict[v.ff]
t.sun_info = v.fi
t.low = tonumber(v.fd)
t.high = tonumber(v.fc)
t.id = i
t.city = city
local weather = _weatherTable(t)
复制代码
  • 保存数据
weather:save()
复制代码
  • 读取数据
_weatherTable.get:all():getPureData()
复制代码

是否是很简单,很优雅,什么建表、拼sql、transation、线程安全等等都不用考虑,傻瓜式地使用,一个业务就几行代码搞定。这里只演示了简单的存取,更多的select、update、联表等高级用法可参考db_test demo安全

Flutter ORM数据库原理详解

好了,上面已经介绍完如何使用了,若是你们仅仅关心使用下面的能够不看了,若是你们想了解这套跨平台的ORM框架的实现原理,下面就会详细介绍,其实了解了实现原理,对你们具体业务使用仍是颇有好处的,虽然我感受你们用的时候极少了解原理。网络

咱们把orm框架分为三层接入层,cache层,db操做层,三个层分别处于对应的线程,具体能够参考下图。接入层能够在任意线程发起,接入层也是每次数据库操做的发起点,上面的demo全部操做都是在接入层,cache层,db操做层仅仅是ORM内部划分,对使用者来说不须要关心cache层和db操做层。咱们把全部的操做分红两种,db后续相关的,和db后续无关的。app

image

db后续无关的操做是从接入层不一样的线程进入到cache层的队列,全部操做在这个队列里先同步完成内存操做,而后便可立刻返回接入层,异步再到db操做层进行db操做。db后续无关的操做包括 save、update、delete。框架

db后续相关的操做依赖db操做层操做的结果,这样的话就必须等真实的db操做完成了再返回接入层。db后续相关的操做包括select。

要作到这种数据同步,咱们必须先把orm操做接口抽象化,只给几个经常使用的接口,全部操做都必须经过指定的接口来完成。咱们总结了以下基本操做接口。

一、save

二、select where

三、select PrimaryKey

四、update where

五、update PrimaryKey

六、delete where

七、delete PrimaryKey

这七种操做只要在操做前返回前对内存中的cache作相应的处理,便可保证内存cache始终和db保持一致,这样之后咱们就能够优先使用cache层的数据了。这七种操做的实现逻辑,这里先说明一下,cache里面的对象都是以主键为key,orm对象为value的形式存储在内存中的,这些控制逻辑是写在cache.lua里面的。

下面详细介绍七种基本操做的逻辑。

  • save操做,同步修改内存cache,而后立刻返回接入层,再异步进行db replace into 的操做

image

  • where条件select,这个必须先同步到db线程获取查询结果,再同步修改内存里面的cache值,再返回给接入层

image

  • select PrimaryKey,就是选必定PrimaryKey值的orm对象,这个操做首先看cache里面是否有primarykey 值的orm对,若是有,直接返回,若是没有,先同步到db线程获取查询结果,再同步修改内存里面的cache值,再返回给接入层

image

  • update where,先同步到db线程经过where 条件select出须要update的主键值,根据主键值和须要update的内容,同步更新内存cache,而后异步进行db的update操做

image

  • update PrimaryKey,根据PrimaryKey进行update操做,先同步更新内存cache,而后异步进行db的update操做

image

  • delete where,先同步到db线程经过where 条件select出须要delete的主键值,根据主键值删除内存cache,而后异步进行db的delete操做

image

  • delete PrimaryKey,根据PrimaryKey进行delete操做,先同步删除内存cache,而后异步进行db的delete操做

image

只要保证上面七种基本操做逻辑,便可保证cache中的内容和db最终的内容是一致的,这种尽可能使用cache的特性能够提高数据库操做的效率,并且保证同一个db的全部操做都在指定的cache线程和db线程里面完成,也能够保证线程安全。

最后,因为咱们全部的db操做都集中起来了,咱们能够定时的transation 保存,这样能够大幅提高数据库操做的性能。

结语

目前Flutter领域最大的痛点就是数据库操做,本文提供了一种优雅使用ORM数据库的方法,大幅下降了使用数据库的门槛。但愿这篇文章和flutter_luakit_plugin能够帮到你们更方便的开发Flutter 应用。