Flutter对象关系映射插件flora_orm的使用

Flutter对象关系映射插件flora_orm的使用

flora_orm

pub package

为Flutter提供的数据库ORM(对象关系映射)。

该ORM支持:

  • shared_preferences - 所有平台
  • sqflite - iOS/Android/MacOS
  • sqflite_common_ffi - 磁盘上 - iOS/Android/MacOS/Linux/Windows
  • sqflite_common_ffi - 内存中 - iOS/Android/MacOS/Linux/Windows

获取开始

要开始使用,你需要在项目中添加 flora_orm。请遵循以下步骤:

  1. 在项目的根目录打开终端。你可以在Android Studio中通过按 Alt+F12 或在VS Code中按 Ctrl+ 来做到这一点。
  2. 运行以下命令:
flutter pub add flora_orm

这条命令会在你的包的 pubspec.yaml 文件中添加一行,并运行一个隐式的 flutter pub get。添加的行将如下所示:

dependencies:
  flora_orm: 

使用示例

导入 flora_orm.dart

import 'package:flora_orm/flora_orm.dart';

初始化

要使用 flora_orm,你需要创建实体类。

对于VS Code用户,我们为你提供了一个代码片段,这样你就不用再手动输入样板代码了。 有关如何添加和使用代码片段的更多信息,请参阅此处

你的实体类必须满足以下条件:

  • 命名约定是 {entity_name}.entity.dart。例如 user.entity.dart (推荐)。
  • 你必须在实体文件的顶部添加两个 part{entity_name}.entity.g.dart{entity_name}.entity.migrations.dart
  • 你必须用 @entity(或 @OrmEntity() 用于细粒度控制)来注解类。
  • 你的实体类必须扩展 Entity<{YourEntityName}, {YourEntityName}Meta> with _{YourEntityName}Mixin, {YourEntityName}Migrations

示例实体

import 'package:flora_orm/flora_orm.dart';

part 'user.entity.g.dart';
part 'user.entity.migrations.dart';

@OrmEntity(tableName: 'user')
class UserEntity extends Entity<UserEntity, UserEntityMeta>
    with _UserEntityMixin, UserEntityMigrations {

  const UserEntity({
    super.id,
    super.collectionId,
    super.createdAt,
    super.updatedAt,
    this.claims,
    this.uid,
    this.email,
    this.phoneNumber,
    this.displayName,
    this.photoURL,
    this.provider,
  });

  [@override](/user/override)
  @column
  final List<String>? claims;

  [@override](/user/override)
  @column
  final String? uid;

  [@override](/user/override)
  @column
  final String? email;
  [@override](/user/override)
  @column
  final String? phoneNumber;
  
  [@override](/user/override)
  @column
  final String? displayName;
  
  [@override](/user/override)
  @OrmColumn(isEnum: true)
  final OAuthProvider? provider;

  [@override](/user/override)
  @column
  final String? photoURL;
}

enum OAuthProvider { google, apple, facebook }

一旦你创建或更新了实体文件,打开终端并从项目的根目录运行以下命令:

dart run build_runner build

OrmManager

你需要一个 OrmManager 的实例来与存储进行交互。

尽早创建 OrmManager 的实例。

我们建议在应用程序启动时使用 get_it 或任何你喜欢的DI框架将其注册为单例。

例如,在 void main() 函数中,在 runApp() 之前,你可以有以下内容:

final ormManager = OrmManager(
     /// 每次添加或更新实体时(如添加新属性/字段),请更新此版本号
      dbVersion: 1,
      /// 如果未指定,dbEngine 默认为 DbEngine.sqflite
      dbEngine: DbEngine.sqflite,
      dbName: 'your_db_name_here.db',
      tables: <Entity>[
        /// 实例化所有你希望保存在数据库中的实体
        const UserEntity(),
      ],
    );
GetIt.I.registerSingleton(ormManager);

为了保持代码整洁,我们建议你将上述代码放在单独的文件中。例如在 src/orm.init.dart 中。

重要提示:在添加实体类(并更新现有实体类)后,请不要忘记:

  1. 从终端运行:
dart run build_runner build
  1. 更新 OrmManager 中的 dbVersion - 如果你更改了列或添加了新的实体类。
  2. 注册任何新的实体到 OrmManagertables: [] 中。

dbEngine 的值默认为 DbEngine.sqflite,可以是以下之一:

  inMemory,
  sqfliteCommon,
  sqflite,
  sharedPreferences,

然而,并不是所有的引擎都适用于所有的平台。下面是每个平台及其支持的引擎的详细情况: 如果你提供了平台不支持的 dbEngine 值,则会使用该平台的默认值。

Android: all (我们推荐 sqflite)
iOS: all (我们推荐 sqflite)
macOS: all (我们推荐 sqflite)
Linux: inMemory, sqfliteCommon, sharedPreferences (默认为 sqfliteCommon)
Windows: inMemory, sqfliteCommon, sharedPreferences (默认为 sqfliteCommon)
web: sharedPreferences (默认为 sharedPreferences)

一旦你的 OrmManager 设置好,你就可以在代码的任何地方使用它。如果你使用的是 get_it,你可以像这样获取你的 storage 实例:

final orm = GetIt.I<OrmManager>();
final {EntityType}Orm storage = orm.getStorage(/* 实体实例 */);

例如,要获取 UserEntitystorage

final orm = GetIt.I<OrmManager>();
final UserEntityOrm storage = orm.getStorage(const UserEntity())

重要提示:你 需要 指定类型(如上面的 UserEntityOrm)以便稍后在 Filter 中获取 ColumnDefition。类型类是在你运行 dart run build_runner build 时自动生成的。

CRUD操作

CRUD - 创建

如果记录具有相同的 id,将会抛出错误:

final entity = await storage.insert(
                                UserEntity(id: 'user1',   
                                displayName: 'Test User',
                                ));

我们建议使用 uuid 生成器来生成 id

你可以使用 insertOrUpdate 代替,这将在记录存在时更新记录:

final entity = await storage.insertOrUpdate(
                                UserEntity(id: 'user1',   
                                displayName: 'Test User',
                                ));

你可以一次插入多条记录:

final entities = await storage.insertList([
                                UserEntity(id: 'user1',   
                                displayName: 'Test User'), 
                                ...,
                                ]);

有一个等效于 insertOrUpdate 的方法可以处理多条记录:

final entities = await storage.insertOrUpdateList([
                                UserEntity(id: 'user1',   
                                displayName: 'Test User'), 
                                ...,
                                ]);

CRUD - 读取

获取单个记录:

final entity = await storage.firstWhereOrNull(...);

获取多个记录:

final entities = await storage.where(...);

CRUD - 更新

你可以使用前面解释过的 insertOrUpdate 选项,这将在记录不存在时插入记录。但是,如果你只想严格更新现有的记录,那么:

final updatedCount = await storage.update(where: ...);

CRUD - 删除

final deletedCount = await storage.delete(where: ...);

Filter 函数

大多数查询都需要一个 where 参数,这是一个必须返回 Filter 的函数。 该函数有一个参数 t,它是你的属性作为 ColumnDefinition 的元描述。

以下是几个示例:

获取 UserEntity,其 id'user1'

final user = await storage.firstWhereOrNull(
      where: (t) => Filter(
        t.id,
        value: 'user1',
      ),
    );

删除所有 UserEntity,其 uid 不为 null

await storage.delete(
      where: (t) => Filter(
        t.uid,
        condition: OrmCondition.notNull,
      ),
    );

获取所有 UserEntity,其 rating 大于等于 20

final users = await storage.where(
      where: (t) => Filter(
        t.rating,
        condition: OrmCondition.greaterThanOrEqual,
        value: 20,
      ),
    );

获取所有 UserEntity,其 rating 在 10 到 100 之间

final users = await storage.where(
      where: (t) => Filter(
        t.rating,
        condition: OrmCondition.between,
        value: 10,
        secondaryValue: 100,
      ),
    );

链接和分组过滤器

你可以拥有复杂的过滤器以满足你的需求。 使用诸如 startGroup()endGroup()filter()and()or() 等实用函数。

filter()and()or() 也有 openGroupcloseGroup 参数,以简化分组,因此你可能不需要 startGroup()endGroup()。然而,我们建议使用 startGroup()endGroup(),因为它们易于阅读和理解其效果。

想想分组就像打开和关闭括号,并将操作放在 <openGroup><closeGroup> 之间的括号内。

在下面的例子中,最后一个 or()and() 过滤器将被分组到 (...) 中。

final users = await storage.where(
      where: (t) =&gt; Filter.startGroup()
          .filter(
            t.displayName,
            condition: OrmCondition.like,
            value: '%flu%',
          )
          .and(
            t.rating,
            value: 10,
          )
          .endGroup()
          .or(
            openGroup: true,
            t.displayName,
            value: 'Loveable',
          )
          .and(
            t.rating,
            value: 11002,
            closeGroup: true,
          ),
    );

startGroup() 通常需要跟随 filter() 之后才能链接其他过滤器。记得 endGroup()/closeGroup

迁移 - 对实体类和数据库更新的更改

如果你更新了任何 Entity 类,你需要再次运行 dart run build_runner build

如果你在 Entity 类中添加/删除了 @column 或任何注解项,那么 增加 OrmManagerdbVersion注册 新的 Entity 类到 OrmManagertables: [] 中,并在相应的 {entity_name}.entity.migrations.dart 文件中添加迁移。

最简单的迁移方式是删除并重新创建实体表(丢失该表中的所有数据),或者指定新增的列:

示例 UserEntity 迁移(第一次运行 dart run build_runner build 时自动生成该文件)

mixin UserEntityMigrations on Entity&lt;UserEntity, UserEntityMeta&gt; {
  [@override](/user/override)
  bool recreateTableAt(int newVersion) {
    return switch (newVersion) {
        /// 当 dbVersion = 3 时,删除并重新创建表
        3 =&gt; true,
      _ =&gt; false,
    };
  }

  [@override](/user/override)
  List&lt;ColumnDefinition&gt; addColumnsAt(int newVersion) {
    return switch (newVersion) {
        /// 这里我们说我们在设置 dbVersion = 2 时添加了名为 provider 的属性。
        /// 在你的实体类中的所有 [@column] 属性都可以作为 [ColumnDefinition] 在 [meta] 对象中访问
      2 =&gt; [meta.provider],
      _ =&gt; [],
    };
  }
}

{entity_name}.entity.migrations.dart 中,你还可以覆盖 downgradeTable()additionalUpgradeQueries(),返回在该操作期间必须执行的查询。

你还可以覆盖 onUpgradeCompleteonDowngradeComplete 以返回在升级/降级完成后的自定义查询。

还有一个 onCreateComplete,你可以在其中返回首次创建数据库时必须执行的查询。

重要提示:作为提醒,在添加实体类(并编辑现有实体类)后,请不要忘记:

  1. 从终端运行:
dart run build_runner build
  1. 更新 OrmManager 中的 dbVersion - 如果你更改了列或添加了新的实体类。
  2. 注册新的实体到 OrmManagertables: [] 中。

支持的数据类型

  • String
  • bool
  • int
  • double
  • DateTime
  • 枚举(需要指定 @OrmColumn(isEnum: true)
  • 自定义类(对象) - 它们需要有工厂构造函数 fromMap(map) 和函数 toMap()
  • 上述类型的列表(例如 List&lt;String&gt;

所有实体类已经实现了 toMap。如果你想让类作为另一个实体的列,你需要定义工厂构造函数 fromMap(map)。 为了方便起见,你可以调用 load() 函数,它会处理其余部分。

UserEntity 的示例工厂构造函数:

@OrmEntity(tableName: 'user')
class UserEntity extends Entity&lt;UserEntity, UserEntityMeta&gt; {
  
  /// 默认构造函数在这里

  factory UserEntity.fromMap(map) {
    return const UserEntity().load(map);
  }

  /// 其余类在这里
}

完整示例代码

import 'package:example/user.entity.dart';
import 'package:flora_orm/flora_orm.dart';
import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  [@override](/user/override)
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
        useMaterial3: true,
      ),
      home: const MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key, required this.title});

  final String title;

  [@override](/user/override)
  State&lt;MyHomePage&gt; createState() =&gt; _MyHomePageState();
}

class _MyHomePageState extends State&lt;MyHomePage&gt; {
  /// 我们推荐你实例化并注册 OrmManager 为单例
  final orm = OrmManager(
    dbVersion: 1,
    dbName: 'orm_db_test.db',
    tables: &lt;Entity&gt;[
      /// 你必须在此处注册所有你要保存的实体
      UserEntity(),
    ],
  );

  /// 获取你想要使用的实体类型的存储实例。
  /// **重要** 记得指定类型 ([UserEntityOrm] 用于此示例)
  /// 以便在使用存储对象时使生活更轻松
  late final UserEntityOrm storage = orm.getStorage(UserEntity());

  Future&lt;UserEntity&gt; _insertUser() async {
    await storage.insertOrUpdate(
      UserEntity(
        id: 'user1',
        firstName: 'John',
        lastName: 'Doe',
      ),
    );

    final user = await storage.firstWhereOrNull(
      (t) =&gt; Filter(t.lastName, value: 'Doe'),
    );
    return user!;
  }

  [@override](/user/override)
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        backgroundColor: Theme.of(context).colorScheme.inversePrimary,
        title: Text(widget.title),
      ),
      body: Center(
        child: FutureBuilder(
          future: _insertUser(),
          builder: (context, snapshot) {
            if (!snapshot.hasData) {
              return const Text('Loading...');
            }
            final user = snapshot.data!;
            return Text('Hello, ${user.firstName} ${user.lastName}');
          },
        ),
      ),
    );
  }
}

更多关于Flutter对象关系映射插件flora_orm的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter对象关系映射插件flora_orm的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


当然,以下是一个关于如何在Flutter项目中使用flora_orm对象关系映射(ORM)插件的示例代码。flora_orm插件允许你以声明性的方式定义数据模型,并轻松地在Flutter应用中执行数据库操作。

1. 添加依赖

首先,在你的pubspec.yaml文件中添加flora_orm依赖:

dependencies:
  flutter:
    sdk: flutter
  flora_orm: ^最新版本号  # 请替换为最新版本号

然后运行flutter pub get来安装依赖。

2. 定义数据模型

使用@Entity注解来定义你的数据模型。每个模型字段可以用@Column注解来标记。

import 'package:flora_orm/flora_orm.dart';

@Entity(tableName: 'users')
class User {
  @PrimaryKey(autoGenerate: true)
  int? id;

  @Column(name: 'name')
  String name;

  @Column(name: 'email')
  String email;

  // 默认构造函数是必需的,因为ORM库需要它来实例化对象
  User({required this.name, required this.email});

  // 工厂构造函数可以从Map创建User对象
  factory User.fromJson(Map<String, dynamic> json) {
    return User(
      name: json['name'] as String,
      email: json['email'] as String,
    );
  }

  // 将User对象转换为Map
  Map<String, dynamic> toJson() {
    return {
      'name': name,
      'email': email,
    };
  }
}

3. 配置数据库

在你的应用中配置数据库连接。

import 'package:flutter/material.dart';
import 'package:flora_orm/flora_orm.dart';
import 'user_model.dart';  // 假设你的User模型定义在这个文件中

void main() async {
  WidgetsFlutterBinding.ensureInitialized();

  // 配置数据库
  final database = await FloraDatabase.connect(
    databasesPath: 'path_to_your_databases_directory',  // 可以是getDatabasesPath()返回的路径
    databaseName: 'my_database.db',
  );

  // 注册实体
  database.registerEntities(<Type>[User]);

  // 创建表(如果尚不存在)
  await database.open();

  runApp(MyApp(database: database));
}

class MyApp extends StatelessWidget {
  final FloraDatabase database;

  MyApp({required this.database});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('Flora ORM Example'),
        ),
        body: Center(
          child: MyHomePage(database: database),
        ),
      ),
    );
  }
}

4. 执行数据库操作

在你的主页面(或任何其他组件)中执行数据库操作,如插入、查询、更新和删除。

import 'package:flutter/material.dart';
import 'package:flora_orm/flora_orm.dart';
import 'user_model.dart';

class MyHomePage extends StatefulWidget {
  final FloraDatabase database;

  MyHomePage({required this.database});

  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  @override
  void initState() {
    super.initState();
    _insertUser();
  }

  Future<void> _insertUser() async {
    final user = User(name: 'John Doe', email: 'john.doe@example.com');
    await widget.database.insert(user);
    print('User inserted');
  }

  Future<void> _fetchUsers() async {
    final users = await widget.database.query<User>();
    print('Fetched users: $users');
  }

  @override
  Widget build(BuildContext context) {
    return ElevatedButton(
      onPressed: () async {
        // 执行查询操作
        await _fetchUsers();
      },
      child: Text('Fetch Users'),
    );
  }
}

在这个示例中,我们演示了如何定义一个简单的User模型,配置数据库连接,并在初始化时插入一个用户,以及如何通过按钮点击事件来查询所有用户。

注意:在实际应用中,你可能会希望将数据库操作封装在更具体的服务类中,并在UI层调用这些服务。此外,错误处理和用户反馈也是实际应用中需要考虑的重要因素。

回到顶部