选型

数据库ORM框架可以提高开发效率,不用写一大堆的sql

网上搜了下,资料不多,官方也没有提供,第三方框架有但是在GitHub上的star数量都不高,以下是几个star数量比较靠前的:

  • flutter_orm_plugin:GitHub上的demo及文档

  • jaguar_query_sqflite

  • sqfentity

  • floor

对比了下,最后㐊选择了floor,因为它的API调用看起来比较舒服,和Android官方的Room框架比较类似。

使用方法

使用方法比较简单,直接看floor或者GitHub上的的文档写得很清晰,如果之前有用过Android的其他ORM框架的话,入手很快,直接看官方文档里的示例代码基本知道怎么用了。

这里直接copy官网上的文档,方便自己也方便大家查阅,基于版本0.9.0,如果有最新版本,可以到pub.dev或者GitHub查看

Table of contents #

  1. How to use this library
  2. Architecture
  3. Querying
  4. Persisting Data Changes
  5. Streams
  6. Transactions
  7. Entities
  8. Foreign Keys
  9. Primary Keys
  10. Indices
  11. Migrations
  12. In-Memory Database
  13. Callback
  14. Examples
  15. Naming
  16. Bugs and Feedback
  17. License

How to use this library #

  1. Add the runtime dependency floor as well as the generator floor_generator to your pubspec.yaml. The third dependency is build_runner which has to be included as a dev dependency just like the generator.

    • floor holds all the code you are going to use in your application.

    • floor_generator includes the code for generating the database classes.

    • build_runner enables a concrete way of generating source code files.

     dependencies:flutter:sdk: flutterfloor: ^0.9.0dev_dependencies:floor_generator: ^0.9.0build_runner: ^1.7.1
    
  2. Creating an Entity

    It will represent a database table as well as the scaffold of your business object. @entity marks the class as a persistent class. It's required to add a primary key to your table. You can do so by adding the @primaryKey annotation to an int property. There is no restriction on where you put the file containing the entity.

     // entity/person.dartimport 'package:floor/floor.dart';@entityclass Person {@primaryKeyfinal int id;final String name;Person(this.id, this.name);}
    
  3. Creating a DAO

    This component is responsible for managing access to the underlying SQLite database. The abstract class contains the method signatures for querying the database which have to return a Future.

    • You can define queries by adding the @Query annotation to a method. The SQL statement has to get added in parenthesis. The method must return a Future of the Entity you're querying for.

    • @insert marks a method as an insertion method.

     // dao/person_dao.dart   import 'package:floor/floor.dart';@daoabstract class PersonDao {@Query('SELECT * FROM Person')Future<List<Person>> findAllPersons();@Query('SELECT * FROM Person WHERE id = :id')Future<Person> findPersonById(int id);@insertFuture<void> insertPerson(Person person);}
    
  4. Creating the Database

    It has to be an abstract class which extends FloorDatabase. Furthermore, it's required to add @Database() to the signature of the class. Make sure to add the created entity to the entities attribute of the @Database annotation.

    In order to make the generated code work, it's required to also add the listed imports.

     // database.dart// required package importsimport 'dart:async';import 'package:floor/floor.dart';import 'package:path/path.dart';import 'package:sqflite/sqflite.dart' as sqflite;   import 'dao/person_dao.dart';import 'model/person.dart';   part 'database.g.dart'; // the generated code will be there@Database(version: 1, entities: [Person])abstract class AppDatabase extends FloorDatabase {PersonDao get personDao;}
    
  5. Make sure to add part 'database.g.dart'; beneath the imports of this file. It's important to note, that 'database' has to get exchanged with the name of the file the entity and database is defined in. In this case, the file is named database.dart.

  6. Run the generator with flutter packages pub run build_runner build. To automatically run it, whenever a file changes, use flutter packages pub run build_runner watch.

  7. Use the generated code. For obtaining an instance of the database, use the generated $FloorAppDatabase class, which allows access to a database builder. The name is composited from $Floor and the database class name. The string passed to databaseBuilder() will be the database file name. For initializing the database, call build().

     final database = await $FloorAppDatabase.databaseBuilder('app_database.db').build();final person = await database.findPersonById(1);await database.insertPerson(person);
    

For further examples take a look at the example and floor_test directories.

Architecture #

The components for storing and accessing data are EntityData Access Object (DAO) and Database.

The first, Entity, represents a persistent class and thus a database table. DAOs manage the access to Entities and take care of the mapping between in-memory objects and table rows. Lastly, Database, is the central access point to the underlying SQLite database. It holds the DAOs and, beyond that, takes care of initializing the database and its schema. Room serves as the source of inspiration for this composition, because it allows creating a clean separation of the component's responsibilities.

The figure shows the relationship between EntityDAO and Database.

Querying #

Method signatures turn into query methods by adding the @Query() annotation with the query in parenthesis to them. Be patient about the correctness of your SQL statements. They are only partly validated while generating the code. These queries have to return either a Future or a Stream of an entity or void. Returning Future<void> comes in handy whenever you want to delete the full content of a table, for instance. Some query method examples can be seen in the following.

@Query('SELECT * FROM Person WHERE id = :id')
Future<Person> findPersonById(int id);@Query('SELECT * FROM Person WHERE id = :id AND name = :name')
Future<Person> findPersonByIdAndName(int id, String name);@Query('SELECT * FROM Person')
Future<List<Person>> findAllPersons(); // select multiple items@Query('SELECT * FROM Person')
Stream<List<Person>> findAllPersonsAsStream(); // stream return@Query('DELETE FROM Person')
Future<void> deleteAllPersons(); // query without returning an entity@Query('SELECT * FROM Person WHERE id IN (:ids)')
Future<List<Person>> findPersonsWithIds(List<int> ids); // query with IN clause

Query arguments, when using SQLite's LIKE operator, have to be supplied by the input of a method. It's not possible to define a pattern matching argument like %foo% in the query itself.

// dao
@Query('SELECT * FROM Person WHERE name LIKE :name')
Future<List<City>> findPersonsWithNamesLike(String name);// usage
final name = '%foo%';
await dao.findPersonsWithNamesLike(name);

Persisting Data Changes #

Use the @insert@update and @delete annotations for inserting and changing persistent data. All these methods accept single or multiple entity instances.

  • Insert

    @insert marks a method as an insertion method. When using the capitalized @Insert you can specify a conflict strategy. Else it just defaults to aborting the insert. These methods can return a Future of either voidint or List<int>.

    • void return nothing
    • int return primary key of inserted item
    • List<int> return primary keys of inserted items
  • Update

    @update marks a method as an update method. When using the capitalized @Update you can specify a conflict strategy. Else it just defaults to aborting the update. These methods can return a Future of either void or int.

    • void return nothing
    • int return number of changed rows
  • Delete

    @delete marks a method as a deletion method. These methods can return a Future of either void or int.

    • void return nothing
    • int return number of deleted rows
// examples of changing multiple items with return @insert
Future<List<int>> insertPersons(List<Person> person);@update
Future<int> updatePersons(List<Person> person);@delete
Future<int> deletePersons(List<Person> person);

Streams #

As already mentioned, queries can not only return a value once when called but also a continuous stream of query results. The returned stream keeps you in sync with the changes happening to the database table. This feature plays really well with the StreamBuilder widget.

These methods return a broadcast stream. Thus, it can have multiple listeners.

// definition
@Query('SELECT * FROM Person')
Stream<List<Person>> findAllPersonsAsStream();// usage
StreamBuilder<List<Person>>(stream: dao.findAllPersonsAsStream(),builder: (BuildContext context, AsyncSnapshot<List<Person>> snapshot) {// do something with the values here},
);

Transactions #

Whenever you want to perform some operations in a transaction you have to add the @transaction annotation to the method. It's also required to add the async modifier. These methods can only return Future<void>.

@transaction
Future<void> replacePersons(List<Person> persons) async {await deleteAllPersons();await insertPersons(persons);
}

Entities #

An entity is a persistent class. Floor automatically creates the mappings between the in-memory objects and database table rows. It's possible to supply custom metadata to Floor by adding optional values to the Entity annotation. It has the additional attribute of tableName which opens up the possibility to use a custom name for that specific entity instead of using the class name. foreignKeys allows adding foreign keys to the entity. More information on how to use these can be found in the Foreign Keys section. Indices are supported as well. They can be used by adding an Index to the indices value of the entity. For further information of these, please refer to the Indices section.

@PrimaryKey marks property of a class as the primary key column. This property has to be of type int. The value can be automatically generated by SQLite when autoGenerate is enabled. For more information about primary keys and especially compound primary keys, refer to the Primary Keys section.

@ColumnInfo enables custom mapping of single table columns. With the annotation, it's possible to give columns a custom name and define if the column is able to store null.

@Entity(tableName: 'person')
class Person {@PrimaryKey(autoGenerate: true)final int id;@ColumnInfo(name: 'custom_name', nullable: false)final String name;Person(this.id, this.name);
}

Primary Keys #

Whenever a compound primary key is required (e.g. n-m relationships), the syntax for setting the keys differs from the previously mentioned way of setting primary keys. Instead of annotating a field with @PrimaryKey, the @Entity annotation's primaryKey attribute is used. It accepts a list of column names that make up the compound primary key.

@Entity(primaryKeys: ['id', 'name'])
class Person {final int id;final String name;Person(this.id, this.name);
}

Foreign Keys #

Add a list of ForeignKeys to the Entity annotation of the referencing entity. childColumns define the columns of the current entity, whereas parentColumns define the columns of the parent entity. Foreign key actions can get triggered after defining them for the onUpdate and onDelete properties.

@Entity(tableName: 'dog',foreignKeys: [ForeignKey(childColumns: ['owner_id'],parentColumns: ['id'],entity: Person,)],
)
class Dog {@PrimaryKey()final int id;final String name;@ColumnInfo(name: 'owner_id')final int ownerId;Dog(this.id, this.name, this.ownerId);
}

Indices #

Indices help speeding up query, join and grouping operations. For more information on SQLite indices please refer to the official documentation. To create an index with floor, add a list of indices to the @Entity annotation. The example below shows how to create an index on the custom_name column of the entity.

The index, moreover, can be named by using its name attribute. To set an index to be unique, use the unique attribute.

@Entity(tableName: 'person', indices: [Index(value: ['custom_name'])])
class Person {@primaryKeyfinal int id;@ColumnInfo(name: 'custom_name', nullable: false)final String name;Person(this.id, this.name);
}

Migrations #

Whenever are doing changes to your entities, you're required to also migrate the old data.

First, update your entity. Next, Increase the database version. Define a Migration which specifies a startVersion, an endVersion and a function that executes SQL to migrate the data. At last, use addMigrations() on the obtained database builder to add migrations. Don't forget to trigger the code generator again, to create the code for handling the new entity.

// update entity with new 'nickname' field
@Entity(tableName: 'person')
class Person {@PrimaryKey(autoGenerate: true)final int id;@ColumnInfo(name: 'custom_name', nullable: false)final String name;final String nickname;Person(this.id, this.name, this.nickname);
}// bump up database version
@Database(version: 2)
abstract class AppDatabase extends FloorDatabase {PersonDao get personDao;
}// create migration
final migration1to2 = Migration(1, 2, (database) {database.execute('ALTER TABLE person ADD COLUMN nickname TEXT');
});final database = await $FloorAppDatabase.databaseBuilder('app_database.db').addMigrations([migration1to2]).build();

In-Memory Database #

To instantiate an in-memory database, use the static inMemoryDatabaseBuilder() method of the generated $FloorAppDatabase class instead of databaseBuilder().

final database = await $FloorAppDatabase.inMemoryDatabaseBuilder('app_database.db').build();

Callback #

In order to hook into Floor's database initialization process, Callback should be used. It allows the invocation of three separate callbacks which are triggered when the database has been

  • initialized for the first time (onCreate).
  • opened (onOpen).
  • upgraded (onUpgrade).

Each callback is optional.

Their usage can be seen in the following snippet.

final callback = Callback(onCreate: (database, version) { /* database has been created */ },onOpen: (database) { /* database has been opened */},onUpgrade: (database, startVersion, endVersion) { /* database has been upgraded */ },
);final database = await $FloorAppDatabase.databaseBuilder('app_database.db').addCallback(callback).build();

Examples #

For further examples take a look at the example and floor_test directories.

Naming #

Floor - the bottom layer of a Room.

Flutter 数据库ORM框架floor使用详解相关推荐

  1. Flutter 网络请求框架dio使用详解

    前言 dio是一款Flutter 网络请求框架,在GitHub上目前有超过5.9k个star.由国人(Flutter中文网)开发,所以中文文档非常完善. 这里copy了dio官方的文档,便于自己开发时 ...

  2. 定时任务框架APScheduler学习详解

    定时任务框架APScheduler学习详解 APScheduler简介 在平常的工作中几乎有一半的功能模块都需要定时任务来推动,例如项目中有一个定时统计程序,定时爬出网站的URL程序,定时检测钓鱼网站 ...

  3. python安装robotframework报错_Python3+RIDE+RobotFramework自动化测试框架搭建过程详解

    Python2.7已于2020年1月1日开始停用,之前RF做自动化都是基于Python2的版本. 没办法,跟随时代的脚步,我们也不得不升级以应用新的控件与功能. 升级麻烦,直接全新安装. 一.Pyth ...

  4. spring框架 AOP核心详解

    AOP称为面向切面编程,在程序开发中主要用来解决一些系统层面上的问题,比如日志,事务,权限等待,Struts2的拦截器设计就是基于AOP的思想,是个比较经典的例子. 一 AOP的基本概念 (1)Asp ...

  5. pythonmessage用法_django 消息框架 message使用详解

    前言 在网页应用中,我们经常需要在处理完表单或其它类型的用户输入后,显示一个通知信息给用户. 对于这个需求,Django提供了基于Cookie或者会话的消息框架messages,无论是匿名用户还是认证 ...

  6. mysql数据库定点任务_MySQL数据库Event定时执行任务详解

    一.背景 由于项目的业务是不断往前跑的,所以难免数据库的表的量会越来越庞大,不断的挤占硬盘空间.即使再大的空间也支撑不起业务的增长,所以定期删除不必要的数据是很有必要的.在我们项目中由于不清理数据,一 ...

  7. SQL Server中通用数据库角色权限的处理详解

    SQL Server中通用数据库角色权限的处理详解 前言 安全性是所有数据库管理系统的一个重要特征.理解安全性问题是理解数据库管理系统安全性机制的前提. 最近和同事在做数据库权限清理的事情,主要是删除 ...

  8. java集合框架的结构_集合框架(Collections Framework)详解及代码示例

    简介 集合和数组的区别: 数组存储基础数据类型,且每一个数组都只能存储一种数据类型的数据,空间不可变. 集合存储对象,一个集合中可以存储多种类型的对象.空间可变. 严格地说,集合是存储对象的引用,每个 ...

  9. Android系统(96)---Android 数据交换解析框架Gson使用详解

    Android 数据交换解析框架Gson使用详解 Json 是一种文本形式的数据交换格式,比 xml 更为轻量.Json 的解析和生成的方式很多,在 Android 平台上最常用的类库有 Gson 和 ...

  10. mysql in从数据库取数_MySQL数据库中 where in 用法详解

    本文主要向大家介绍了MySQL数据库中 where in 用法详解,通过具体的内容向大家展现,希望对大家学习MySQL数据库有所帮助. 这里分两种情况来介绍 WHERE column IN (valu ...

最新文章

  1. centos7安装redis3.2.5
  2. UItraIso 制作ubentu 系统失败
  3. java addfirst()_Java addFirst(),addLast() 在链表(LinkedList)的开头和结尾添加元素
  4. scikit keras_Scikit学习,TensorFlow,PyTorch,Keras…但是天秤座呢?
  5. java jni调用dll_浅谈JNI的使用--java调用dll(原创)
  6. 基于JAVA+SpringMVC+Mybatis+MYSQL的宠物商城管理系统
  7. 让块元素在同一行显示的方法: float 和inline-block
  8. SpringBoot法律知识分享问答论坛 lawbbs.liuyanzhao.com
  9. vue3.0项目引入高德地图
  10. IT管理员喜欢OpManager的十大原因
  11. 特征值和奇异值的关系
  12. 神经网络训练样本太少,神经网络常用训练方法
  13. CCF CSP 201609-2 火车购票(C++语言100分)[简单模拟题]
  14. Docker从入门到放弃-----Dockerfile常用命令解析与实战(使用docker制作一个开箱即用的consul镜像)
  15. 一个 SPI 转串口驱动的优化
  16. Haydn解决方案数字化平台助力架构师1小时完成架构设计(实操篇)
  17. 问答 | 35岁以后怎么混
  18. 显卡发展史:从百花齐放到双雄争霸,显卡为何如此「迷人」?
  19. 【附源码】计算机毕业设计SSM网上拍卖系统
  20. iOS加载网页【全解】

热门文章

  1. 【Autoware】之ndt_mapping理论公式及代码对比
  2. QoS实现交换机带宽控制
  3. 大学计算机课英语心得体会,【大学计算机课程总结12篇】_大学计算机课程总结范文大全_2021年大学计算机课程总结_东城教研...
  4. python自动登录qq邮箱_python和selenium实现163邮箱自动登陆
  5. 植物大战僵尸 - 修改关卡和商店金钱
  6. 同位素的研究方法和技术
  7. Leetcode每日一题——T32. 最长有效括号(难)——栈
  8. FPGA Vivado AXI _DMA IP介绍
  9. 宋智孝那个机器人_陈柏霖机器人竟然冷落宋智孝
  10. 恶心的极速输入法windows10 删除