tinano

Tinano 是一个基于 sqflite 的 Flutter 应用本地持久化库。虽然 sqflite 是一个很棒的数据持久化库,但编写所有解析代码本身很繁琐,而且很容易出错。

使用 Tinano,您可以指定查询及其返回的数据结构,它将自动处理所有手动和繁琐的工作,为您提供一种干净且类型安全的方式来管理应用数据。

入门

设置您的项目

首先,让我们准备您的 pubspec.yaml 来添加此库和工具
用于根据数据库定义自动生成代码。

dependencies:
  tinano:
  # ...
dev_dependencies:
  tinano_generator:
  build_runner:
  # test, ...

tinano 库将为您提供一些注解来编写您的
数据库类,而 tinano_generator 则插入 build_runner
来生成实现。由于我们只在开发过程中进行代码生成(而不是在运行时),所以这两个都可以作为开发依赖项。
创建数据库

使用 Tinano,创建数据库很简单

您的数据库类必须是抽象的,并且有一个名为 createBuilder 的静态方法,该方法使用 => 语法。_$createMyDatabase() 方法稍后将自动生成。当然,您可以自由选择任何名称,但创建数据库的方法必须以 _$ 开头。

import 'package:tinano/tinano.dart';
import 'dart:async';

part 'database.g.dart'; // this is important!

@TinanoDb(name: "my_database.sqlite", schemaVersion: 1)
abstract class MyDatabase {

  static DatabaseBuilder<MyDatabase> createBuilder() => _$createMyDatabase();

}

您的数据库类必须是抽象的,并且有一个名为 createBuilder 的静态方法,该方法使用 => 语法。_$createMyDatabase() 方法稍后将自动生成。当然,您可以自由选择任何名称,但创建数据库的方法必须以 _$ 开头。
您的数据库类必须是抽象的,并且有一个名为 createBuilder 的静态方法,该方法使用 => 语法。_$createMyDatabase() 方法稍后将自动生成。当然,您可以自由选择任何名称,但创建数据库的方法必须以 _$ 开头。
您的数据库类必须是抽象的,并且有一个名为 createBuilder 的静态方法,该方法使用 => 语法。_$createMyDatabase() 方法稍后将自动生成。当然,您可以自由选择任何名称,但创建数据库的方法必须以 _$ 开头。
当然,您可以自由选择任何名称,但创建数据库的方法必须以 _$ 开头。
当然,您可以自由选择任何名称,但创建数据库的方法必须以 _$ 开头。
目前,这段代码将导致一堆错误,因为实现
尚未生成。在终端中快速运行 flutter packages pub run build_runner build
将解决此问题。如果您想在每次更改规范时自动重建数据库实现(在开发过程中可能很有用),可以使用 flutter packages pub run build_runner watch
如果您想在每次更改规范时自动重建数据库实现(在开发过程中可能很有用),可以使用 flutter packages pub run build_runner watch
如果您想在每次更改规范时自动重建数据库实现(在开发过程中可能很有用),可以使用 flutter packages pub run build_runner watch

打开数据库

要获取 MyDatabase 的实例,您只需像这样使用构建器函数
像这样

Future<MyDatabase> openMyDatabase() async {
  return await (MyDatabase
    .createBuilder()
    .doOnCreate((db, version) async {
      // This await is important, otherwise the database might be opened before
      // you're done with initializing it!
      await db.execute("""CREATE TABLE `users` ( `id` INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, `name` TEXT NOT NULL )""");
    })
    .build());
}

doOnCreate 块将在数据库首次打开时执行。db 参数将使您能够访问原始 sqflite 数据库,version 参数是您的 @TinanoDb 注解中指定的架构版本。
db 参数将使您能够访问原始 sqflite 数据库,version 参数是您的 @TinanoDb 注解中指定的架构版本。
db 参数将使您能够访问原始 sqflite 数据库,version 参数是您的 @TinanoDb 注解中指定的架构版本。
您可以使用 addMigration 方法执行架构迁移 - 有关
更多信息请参见下方。

数据库查询

当然,仅仅打开数据库是很无聊的。为了实际
执行一些数据库查询,只需创建用 @Query@Update@Delete@Insert 注解的方法即可。下面是一个符合
上面定义的 doOnCreate 方法的示例
上面定义的 doOnCreate 方法的示例

@TinanoDb(name: "my_database.sqlite", schemaVersion: 1)
abstract class MyDatabase {
  static DatabaseBuilder<MyDatabase> createBuilder() => _$createMyDatabase();

  @Query("SELECT * FROM users")
  Future<List<UserRow>> getAllUsers();

  // If we know we'll only get one user, we can skip the List<>. Note that this
  // really expects there to be one row -> if there are 0, it will throw an
  // exception.
  @Query("SELECT * FROM users WHERE id = :id")
  Future<UserRow> getUserById(int id);

  // For queries with only one column that is either a String, a num or a
  // Uint8List, we don't have to define a new class.
  @Query("SELECT COUNT(id) FROM users")
  Future<int> getAmountOfUsers();

  // Inserts defined to return an int will return the insert id. Could also 
  // return nothing (Future<Null> or Future<void>) if we wanted.
  @Insert("INSERT INTO users (name) VALUES (:name)")
  Future<int> createUserWithName(String name);

  // Inserts return values based on their return type:
  // For Future<Null> or Future<void>, it won't return any value
  // For Future<int>, returns the amount of changed rows
  // For Future<bool>, checks if the amount of changed rows is greater than zero
  @Update("UPDATE users SET name = :updatedName WHERE id = :id")
  Future<bool> changeName(int id, String updatedName);

  // The behavior of deletes is identical to those of updates.
  @Delete("DELETE FROM users WHERE id = :id")
  Future<bool> deleteUser(int id);
}

// We have to annotate composited classes as @row. They should be immutable.
@row
class UserRow {

  final int id;
  final String name;

  UserRow(this.id, this.name);

}

变量

如您所见,您可以通过在 SQL 中直接使用 :myVariable 标记来轻松地将方法参数映射到 SQL
变量。如果您想在 SQL 中使用 : 字符,那是可以的,只需用反斜杠 \ 转义即可。请注意,您必须在 Dart 字符串中使用两个反斜杠("\\:")。
您必须在 Dart 字符串中使用两个反斜杠("\\:")。
您必须在 Dart 字符串中使用两个反斜杠("\\:")。

变量不会直接插入到查询中(这很容易导致 SQL 注入漏洞),而是使用预编译语句
先发送不带数据的 SQL,然后再发送变量。这意味着您
不能将变量用于所有内容,请参阅
https://www.quora.com/What-are-the-limitations-of-PDO-prepared-statements-Can-I-define-the-table-and-row-as-arguments">此处 了解一些不能使用变量的示例。
https://www.quora.com/What-are-the-limitations-of-PDO-prepared-statements-Can-I-define-the-table-and-row-as-arguments">此处 了解一些不能使用变量的示例。

架构更新

@TinanoDb 中提高版本号后,您将必须手动执行一些
迁移。您可以通过 DatabaseBuilder 直接进行迁移,方法是
使用 addMigration

MyDatabase
  .createBuilder()
  .doOnCreate((db, version) {...})
  .addMigration(1, 2, (db) async {
	  await db.execute("ALTER TABLE ....")
  });

对于较大的迁移(例如从 1 到 5),只需为
每一步指定所有迁移。然后 Tinano 将按顺序应用它们,以确保数据库
在打开之前已准备就绪。

支持的类型

由于数据库访问是异步的,所有方法都必须返回一个 Future

对于修改语句(更新/删除)

Future<int> 将解析为更新的行数,而
Future<bool> 作为返回类型将解析为 true(如果有任何更改)
false(如果没有)。

对于插入语句

Future<int> 将解析为最后插入的 ID。Future<bool>
将始终解析为 true,因此不建议在此处使用。

对于 select 语句

如果您想获取所有结果,必须使用 List<T>,或者如果您只想接收第一个结果,则直接使用 T
请注意,无论哪种情况,整个响应最终都会加载到内存中,因此请在 SQL 中设置 LIMIT
请在 SQL 中设置 LIMIT
请在 SQL 中设置 LIMIT
现在,如果您的结果只有一列,您可以使用该类型
直接使用。

@Query("SELECT COUNT(*) FROM users")
Future<int> getAmountOfUsers();

这适用于 intnumUint8ListString。请参阅
sqflite 的文档
以检查哪些 Dart 类型与哪些 SQLite 类型兼容。
如果您的查询返回多于一列,您必须将其定义
在一个新的不可变类中,该类只能有一个无参数构造函数来设置字段。

@row
class UserResult {
  final int id;
  final String name;

  UserResult(this.id, this.name);
}

// this should be a method in your @TinanoDb class....
@Query("SELECT id, name FROM users")
Future<List<UserResult>> getBirthmonthDistribution();

每个 @row 类只能包含原始字段 intnum
Uint8ListString

访问原始数据库

如果您想使用 Tinano,但也有些用例需要直接使用
sqfliteDatabase 来发送查询,您只需在 @TinanoDb 类中定义一个名为 Database database; 的字段即可。它将在数据库打开后生成并可用。
它将在数据库打开后生成并可用。
它将在数据库打开后生成并可用。

待办事项列表

  • 如果我们能摆脱 doOnCreate,而在数据库类中直接定义这些
    方法,并添加更多注解,那就太好了。这同样适用于迁移函数。
    迁移函数。
  • 批处理和事务以提高性能和可靠性。
  • 自动更新返回 Stream 并随着底层数据变化发出新值的查询。可能类似于
    Android 上的 Room 库
    Android 上的 Room 库
  • 支持从库中直接支持 DateTime,自动生成代码将其存储
    为时间戳在数据库中。
  • 支持具有其他 @row 类型作为字段的 @row 类。
  • 支持自定义类作为变量参数,在 SQL 中指定类似
    WHERE id = :user.id,然后将 User user 作为参数。
  • 能够为 SQL 和 Dart 类型使用不同的变量/列名。
    添加一些注解,如 @FromColumn("my_column")

GitHub

https://github.com/simolus3/tinano