Flutter数据库ORM管理插件elite_orm的使用

Flutter数据库ORM管理插件elite_orm的使用

简介

elite_orm 是一个基于 sqflite 构建的简单易用的ORM(对象关系映射)库。

开始使用

在你的 Flutter 项目中添加依赖:

dependencies:
  ...
  elite_orm:

示例代码

引入库

import 'package:elite_orm/elite_orm.dart';

创建模型

模型类需要继承自 Entity 类。

class Supplement extends Entity<Supplement> {
  // 构造函数必须可以无参数调用
  Supplement([name = "", count = 0, DateTime? expire]) : super(Supplement.new) {
    // 第一个数据成员必须是唯一标识符,作为主键
    members.add(DBMember<String>("name", name));
    members.add(DBMember<int>("count", count));
    members.add(DateTimeDBMember("expire", expire ?? DateTime.now()));
  }

  // 这些不是存储到数据库所必需的,但推荐用于方便访问数据成员
  String get name => members[0].value;
  int get count => members[1].value;
  DateTime get expire => members[2].value;
}

创建数据库提供者

定义好模型后,需要创建一个数据库提供者来实际存储数据。这里以 sqflite 为例。

import 'dart:io';  
import 'package:path_provider/path_provider.dart';  
import 'package:sqflite/sqflite.dart';  
import '../model/supplement.dart';

List<String> getTableDescriptions() {  
  return [  
    Supplement().describeTable(),  
  ];  
}
  
class DatabaseProvider {
  static final Future<Database> database = _createDatabase();

  static Future<Database> _createDatabase() async {
    final Directory documentsDirectory = await getApplicationDocumentsDirectory();
    final String path = join(documentsDirectory.path, "stored.db");
    return await openDatabase(
      path,
      version: 1,
      onCreate: (database, version) async {
        for (String description in getTableDescriptions()) {
          await database.execute("CREATE TABLE $description");
        }
      },
    );
  }
}

使用BLoC

接下来,你需要为每个模型对象创建一个 BLoC 来与数据库交互。

import 'package:elite_orm/elite_orm.dart';
import '../model/supplement.dart';
import '../database/database.dart';

final supplementBloc = Bloc(Supplement(), DatabaseProvider.database);
创建对象
Supplement supplement = Supplement("Wonder Supplement", 50);
supplementBloc.create(supplement);
获取所有对象
List<Supplement> supplementList = [];  
StreamSubscription subscription = supplementBloc.all.listen((supplements) {  
  supplementList = supplements;  
});
删除对象
supplementBloc.delete(supplement.name);

DBMember 类

以下是 DBMember 及其派生类的说明:

DBMember

这是所有数据库成员的基类,是一个键值对,可以表示 intdoubleString 类型。它有一个可选布尔参数,指示是否将其用作复合主键的一部分。

EnumDBMember

该类扩展了 DBMember,期望一个 Enum 类型作为值参数,构造函数参数与 DBMember 不同。第一个参数是枚举值列表,接下来两个参数是键值对。数据库中的值是 Enum 类型的整数索引。

BoolDBMember

该类扩展了 DBMember,期望一个 bool 类型作为值参数。数据库中的值是布尔值的整数表示形式。

BinaryDBMember

该类扩展了 DBMember,期望一个 Uint8List 类型作为值参数。数据库中的值是一个 BLOB。

DateTimeDBMember

该类扩展了 DBMember,期望一个 DateTime 类型作为值参数。数据库中的值是一个字符串。

DurationDBMember

该类扩展了 DBMember,期望一个 Duration 类型作为值参数。数据库中的值是一个 BIGINT,表示持续时间(以微秒为单位)。

PrimitiveListDBMember

该类扩展了 DBMember,期望一个 List<T> 类型作为值参数,其中 T 是 intdoubleString。数据库中的值是一个 JSON 字符串。

ListDBMember

该类扩展了 PrimitiveListDBMember,构造函数参数与 DBMember 不同。第一个参数是一个可以创建类型 T 的对象的函数,接下来两个参数是键值对,其中值是一个列表。

ObjectDBMember

该类扩展了 DBMember,构造函数参数与 DBMember 不同。第一个参数是一个可以创建类型 T 的对象的函数,接下来两个参数是键值对,其中值是一个 T 类型的对象。最后一个可选参数是一个布尔值,指示是否将其用作复合主键的一部分。数据库中的值是一个 JSON 字符串。

Bloc 类

Bloc 是你与数据库交互的接口。任何底层数据库实现抛出的异常都可以通过这些类方法传播出来。

构造函数

Bloc(T instance, Future db)

创建一个 Bloc 对象。

instance 不存储在数据库中;它用于访问类方法。

属性

Stream<List<T>> all

一个包含类型 T 的所有数据库对象的 Dart 异步流实例。

方法

Future<void> get()

获取类型 T 的所有数据库对象,并将它们添加到流控制器。

Future<int> create(T obj)

将类型 T 的 obj 添加到数据库。

Future<int> update(T obj)

obj 替换具有匹配主键的数据库对象。

Future<int> delete(dynamic target)

删除匹配目标的数据库对象,其中目标可以是主键或类型 T 的对象,这在模型有复合主键时非常有用。

Future<int> deleteAll()

从数据库中删除所有类型 T 的对象。

void dispose()

关闭流控制器。

完整示例

点击 这里 查看更详细的示例。

import 'dart:typed_data';
import 'package:http/http.dart' as http;
import 'package:flutter/material.dart';
import 'package:elite_orm/elite_orm.dart';

import '../model/eighties_metal.dart';
import '../database/database.dart';

final bloc = Bloc(EightiesMetal(), DatabaseProvider.database);

/// 应用程序的入口点。
void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  /// 创建应用程序。
  const MyApp({super.key});

  /// 描述由该小部件表示的用户界面部分。
  [@override](/user/override)
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'EliteORM 示例',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const MyHomePage(title: 'EliteORM 示例'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  final String title;

  /// 创建主页小部件。
  const MyHomePage({super.key, required this.title});

  /// 创建该小部件的可变状态。
  [@override](/user/override)
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  // 这是一个填充数据库的示例方法。通常会通过用户输入或其他方法收集数据。
  void _initialData() async {
    List<Uint8List> logos = [];
    List<String> urls = [
      "https://images.squarespace-cdn.com/content/v1/55ccf522e4b0fc9c2b651a5d/1439501203849-VHE9FD1TAJKZWUJPNTBA/Slayer_Logo_1000w.png",
      "https://upload.wikimedia.org/wikipedia/commons/thumb/e/ea/Metallica_wordmark.svg/1920px-Metallica_wordmark.svg.png",
      "https://megadeth.com/wp-content/uploads/2023/03/mega-tstsatd-logo-2048x391.png",
    ];
    for (var url in urls) {
      final response = await http.get(Uri.parse(url));
      logos.add(response.bodyBytes);
    }

    EightiesMetal slayer = EightiesMetal(
      "Slayer",
      Album("Show No Mercy", DateTime(1983, 12, 1), const Duration(minutes: 35, seconds: 2)),
      MetalSubGenre.thrash,
      true,
      DateTime(1981),
      [DBDateTimeRange(DateTime(1981), DateTime(2019))],
      ["Tom Araya", "Jeff Hanneman", "Kerry King", "Dave Lombardo"],
      [1983, 1985, 1986, 1988, 1990, 1994, 1996, 1998, 2001, 2006, 2009, 2015],
      logos[0],
    );
    EightiesMetal metallica = EightiesMetal(
      "Metallica",
      Album("Kill 'Em All", DateTime(1983, 7, 25), const Duration(minutes: 51, seconds: 20)),
      MetalSubGenre.thrash,
      false,
      DateTime(1981, 10, 28),
      [DBDateTimeRange(DateTime(1981))],
      ["Cliff Burton", "Kirk Hammet", "James Hetfield", "Lars Ulrich"],
      [1983, 1984, 1986, 1988, 1991, 1996, 1997, 2003, 2008, 2016, 2023],
      logos[1],
    );
    EightiesMetal megadeth = EightiesMetal(
      "Megadeth",
      Album("Killing Is My Business... and Business Is Good!", DateTime(1985, 6, 12), const Duration(minutes: 31, seconds: 10)),
      MetalSubGenre.speed,
      false,
      DateTime(1983, 7, 1),
      [
        DBDateTimeRange(DateTime(1983), DateTime(2002)),
        DBDateTimeRange(DateTime(2004))
      ],
      ["David Ellefson", "Dave Mustaine", "Chris Poland", "Gar Samuelson"],
      [
        1985,
        1986,
        1988,
        1990,
        1992,
        1994,
        1997,
        1999,
        2001,
        2004,
        2007,
        2009,
        2011,
        2013,
        2016,
        2022
      ],
      logos[2],
    );

    bloc.create(slayer);
    bloc.create(metallica);
    bloc.create(megadeth);
  }

  Widget _renderBand(EightiesMetal band) {
    return Card(
      margin: const EdgeInsets.all(10),
      shape: RoundedRectangleBorder(
        side: BorderSide(
          color: Theme.of(context).colorScheme.primaryContainer,
          width: 0.5,
        ),
        borderRadius: BorderRadius.circular(4),
      ),
      child: Column(
        children: [
          Text(
            band.name,
            style: const TextStyle(
              fontSize: 16.5,
              fontWeight: FontWeight.w500,
            ),
          ),
          Text("${band.genre.toString().replaceAll("MetalSubGenre.", "")} metal"),
          Text("Formed ${band.formed.toString().replaceAll("00:00:00.000", "")}${band.defunct ? " (disbanded)" : ""}"),
          const Text("Active:"),
          Column(
              children: band.active
                  .map((r) => Text("${r.start.year} - ${r.end.year}"))
                  .toList()),
          const Text("Studio Album Years:"),
          Column(
              children: band.studioAlbumYears.map((y) => Text("$y")).toList()),
          Text(band.album.name),
          Text("${band.album.release.toString().replaceAll("00:00:00.000", "")} ${band.album.length}"),
          Text("${band.bandMembers}"),
          if (band.logo.isNotEmpty) Image.memory(band.logo, scale: 2),
        ],
      ),
    );
  }

  Widget _renderBands(AsyncSnapshot<List<EightiesMetal>> snapshot) {
    if (snapshot.hasData) {
      if (snapshot.data!.isEmpty) {
        return const SizedBox(width: double.infinity);
      } else {
        snapshot.data!.sort((a, b) => a.formed.compareTo(b.formed));
        return ListView.builder(
          shrinkWrap: true,
          itemCount: snapshot.data!.length,
          itemBuilder: (context, itemPosition) {
            return _renderBand(snapshot.data![itemPosition]);
          },
        );
      }
    } else {
      return Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: const <Widget>[
            CircularProgressIndicator(),
            Text(
              "Loading...",
              style: TextStyle(
                fontSize: 22,
                fontWeight: FontWeight.w500,
              ),
            )
          ],
        ),
      );
    }
  }

  Widget _renderBandsWidget() {
    return StreamBuilder(
      stream: bloc.all,
      builder: (BuildContext context, AsyncSnapshot<List<EightiesMetal>> snapshot) {
        return _renderBands(snapshot);
      },
    );
  }

  /// 当此对象插入树中时被调用。
  [@override](/user/override)
  void initState() {
    super.initState();
    bloc.get();
  }

  /// 描述由该小部件表示的用户界面部分。
  [@override](/user/override)
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: SafeArea(
        child: _renderBandsWidget(),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _initialData,
        tooltip: '添加静态数据',
        child: const Icon(Icons.add),
      ),
    );
  }

  /// 当此对象永久从树中移除时被调用。
  [@override](/user/override)
  dispose() {
    super.dispose();
    bloc.dispose();
  }
}

更多关于Flutter数据库ORM管理插件elite_orm的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter数据库ORM管理插件elite_orm的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


elite_orm 是一个用于 Flutter 的轻量级 ORM(对象关系映射)插件,用于简化数据库操作。它支持 SQLite 数据库,并提供了一种简单的方式来管理数据库表、执行查询和操作数据。

以下是如何使用 elite_orm 插件的基本步骤:

1. 添加依赖

首先,在 pubspec.yaml 文件中添加 elite_orm 依赖:

dependencies:
  flutter:
    sdk: flutter
  elite_orm: ^1.0.0  # 请使用最新版本

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

2. 创建数据模型

创建一个数据模型类,并使用 [@Entity](/user/Entity) 注解来标记它。[@Column](/user/Column) 注解用于标记表的列。

import 'package:elite_orm/elite_orm.dart';

[@Entity](/user/Entity)()
class User {
  [@Column](/user/Column)(primaryKey: true, autoIncrement: true)
  int? id;

  [@Column](/user/Column)()
  String? name;

  [@Column](/user/Column)()
  int? age;

  User({this.id, this.name, this.age});
}

3. 初始化数据库

在应用程序启动时初始化数据库,并指定数据库名称和版本。

import 'package:elite_orm/elite_orm.dart';

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  
  // 初始化数据库
  await EliteOrm.init(
    databaseName: 'my_database.db',
    version: 1,
    entities: [User],
  );

  runApp(MyApp());
}

4. 使用 ORM 进行数据库操作

你可以使用 EliteOrm 提供的 API 来执行数据库操作。

插入数据

User user = User(name: 'John Doe', age: 30);
await EliteOrm.insert(user);

查询数据

List<User> users = await EliteOrm.findAll<User>();

更新数据

User user = users.first;
user.name = 'Jane Doe';
await EliteOrm.update(user);

删除数据

await EliteOrm.delete<User>(user.id);

5. 关闭数据库

在应用程序退出时关闭数据库连接。

void dispose() async {
  await EliteOrm.close();
}

示例代码

以下是一个完整的示例,展示了如何使用 elite_orm 进行基本的数据库操作:

import 'package:flutter/material.dart';
import 'package:elite_orm/elite_orm.dart';

[@Entity](/user/Entity)()
class User {
  [@Column](/user/Column)(primaryKey: true, autoIncrement: true)
  int? id;

  [@Column](/user/Column)()
  String? name;

  [@Column](/user/Column)()
  int? age;

  User({this.id, this.name, this.age});
}

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  
  // 初始化数据库
  await EliteOrm.init(
    databaseName: 'my_database.db',
    version: 1,
    entities: [User],
  );

  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  [@override](/user/override)
  Widget build(BuildContext context) {
    return MaterialApp(
      home: HomeScreen(),
    );
  }
}

class HomeScreen extends StatefulWidget {
  [@override](/user/override)
  _HomeScreenState createState() => _HomeScreenState();
}

class _HomeScreenState extends State<HomeScreen> {
  List<User> users = [];

  [@override](/user/override)
  void initState() {
    super.initState();
    _loadUsers();
  }

  Future<void> _loadUsers() async {
    List<User> userList = await EliteOrm.findAll<User>();
    setState(() {
      users = userList;
    });
  }

  Future<void> _addUser() async {
    User user = User(name: 'New User', age: 25);
    await EliteOrm.insert(user);
    _loadUsers();
  }

  Future<void> _updateUser(User user) async {
    user.name = 'Updated User';
    await EliteOrm.update(user);
    _loadUsers();
  }

  Future<void> _deleteUser(int? id) async {
    await EliteOrm.delete<User>(id);
    _loadUsers();
  }

  [@override](/user/override)
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Elite ORM Example'),
      ),
      body: ListView.builder(
        itemCount: users.length,
        itemBuilder: (context, index) {
          User user = users[index];
          return ListTile(
            title: Text(user.name ?? 'No Name'),
            subtitle: Text('Age: ${user.age}'),
            onTap: () => _updateUser(user),
            trailing: IconButton(
              icon: Icon(Icons.delete),
              onPressed: () => _deleteUser(user.id),
            ),
          );
        },
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _addUser,
        child: Icon(Icons.add),
      ),
    );
  }
}
回到顶部