Introduction to Flutter ORM database

Perhaps the biggest problem with Flutter development is the use of databases. Flutter now only provides sqflite, which means that the least efficient way to use a database is to manually write SQL code, build tables, build indexes, transation, and control db threads. For example, IOS platforms have frameworks like CoreData, Realm, and so on that provide easy database operations, but when Flutter goes back to bare WRITING SQL, this is a significant cost for most teams.

This article will detail an elegant way to use the ORM database in a Flutter project. The ORM framework we use is one of the features contained in the Flutter_luakit_plugin for Flutter. This paper only introduces the use and implementation principle of this ORM framework in detail. We gave a demo.

We implemented a simple function in the demo, which is to query the weather information of Beijing from a weather website, parse the returned JSON and save it in the database. The data will be displayed immediately after the next startup, and then send a request to the weather website to update the weather information. It is such a simple function. Although simple in function, 99% of our daily business logic is made up of these simple logics. Below is a working demo.

With that in mind, let’s take a look at the ORM database. The core code of ORM database is LuA, WeatherManager. Lua is the business logic code, and other LuA files are the core code of ORM database, all of which are implemented by LuA. All code files add up to about 120K, which is very lightweight.

In view of the weather information functions mentioned above, we to design the data model, from the demo show we see every day the weather information includes several information, city name, sunrise and sunset time, maximum temperature, minimum temperature, wind direction, wind, and then to distinguish which is the data of one day, we add an id attribute to each message, as the primary key. We will define the first ORM data model with the necessary information, db name, table name, and the following fields we need. We will provide IntegerField, RealField, BlobField, TextField, BooleandField. And other commonly used data types. Weather was the name of the model, and then we used the data model for the index weather. Define the model code as follows.

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", {}}},Copy the code

So once we’ve defined the model, let’s see how we can use it, let’s go with the business logic, first the network request comes back and we’re going to generate the model object and store it in the database, okay

  • Get model objects
Local = require('orm.class.table') local _weatherTable = Table("weather ")Copy the code
  • Prepare data and create data objects
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)
Copy the code
  • Save the data
weather:save()
Copy the code
  • Read the data
_weatherTable.get:all():getPureData()
Copy the code

Is it very simple, very elegant, do not have to worry about what to build a table, SQL, transation, thread safety, etc., foolishly use, a business in a few lines of code to do. For more advanced uses of select, UPDATE, and join tables, see db_test demo.

The principle of Flutter ORM database is explained

Well, has introduced how to use the above, if you only care about can use the following watch, if you want to know the implementation principle of cross-platform ORM framework, the following will introduce in detail, actually understand the principle, to specific business use is very good, although I feel you rarely understand the principle and use it every time.

The ORM framework is divided into three layers: access layer, cache layer and DB operation layer. The three layers are located in corresponding threads respectively. For details, please refer to the following figure. The access layer can be initiated from any thread, and the access layer is also the initiating point of every database operation. All operations in the demo above are in the access layer, cache layer, and DB operation layer, which are only internal division of ORM. For users, there is no need to care about the cache layer and DB operation layer. We divide all operations into two categories, db subsequent dependent, and DB subsequent independent.

Subsequent db operations are unrelated to the queue from different threads in the access layer to the cache layer. All operations are performed synchronously in this queue and then immediately returned to the access layer for db operations asynchronously. Subsequent irrelevant db operations include save, update, and delete.

Subsequent db operations depend on the results of db operations, which must wait for the actual DB operations to complete before returning to the access layer. Subsequent db operations include SELECT.

To achieve this kind of data synchronization, we must first abstract the ORM operation interface, giving only a few commonly used interfaces, all operations must be done through the specified interface. We summarize the following basic operation interfaces.

1, save

2. Select the where

3, select the PrimaryKey

4, update the where

5, the update PrimaryKey

6, delete the where

7, delete the PrimaryKey

These seven operations ensure that the memory cache is always consistent with db, so that we can use the cache layer data first in the future. The control logic for these seven operations is written in cache.lua. All the objects in cache are stored in memory in the form of primary key and orM object value.

The following details the logic of the seven basic operations.

  • The save operation synchronously modifies the memory cache, and then immediately returns to the access layer for db replace into asynchronously

  • Where select, this must be synchronized to the DB thread to obtain the query result, and then modify the cache value in the memory, and then return to the access layer

  • Select PrimaryKey, which is an ORM object with a PrimaryKey value. Check whether there are ORM pairs with a PrimaryKey value in the cache. If there are no ORM pairs with a PrimaryKey value, the query result is returned. The cache value in the memory is changed synchronously, and then returned to the access layer

  • Update WHERE: The db thread uses the WHERE condition to select the primary key that needs to be updated, synchronously updates the memory cache based on the primary key and the content that needs to be updated, and asynchronously updates the DB

  • Update PrimaryKey: The database is updated based on the PrimaryKey. The memory cache is updated synchronously and the DB is updated asynchronously

  • Delete WHERE: The db thread uses the WHERE condition to select the primary key to delete, deletes the memory cache based on the primary key, and then asynchronously deletes the DB

  • Delete PrimaryKey: Deletes the memory cache synchronously and then deletes the DB asynchronously

As long as you ensure the above seven basic operation logic, you can ensure that the contents of the cache and the final contents of the DB are consistent. This feature can improve the efficiency of database operations, and ensure that all operations of the same DB are completed in the specified cache thread and DB thread, but also to ensure thread safety.

Finally, because all of our DB operations are centralized, we can transation save periodically, which can greatly improve the performance of database operations.

conclusion

Database operations are currently the biggest pain point in the Flutter field. This article provides an elegant way to use ORM databases, greatly reducing the barrier to use databases. Hopefully this article and flutter_luakit_plugin will help you develop Flutter applications more conveniently.