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

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

本工程是基于sqflite实现的简易版本的ORM,优点是支持在不更新数据库版本的情况下新增表字段,解决表结构变更时需要升级数据库版本的问题。

接入方法

1. 设置依赖

pubspec.yaml文件中添加以下依赖:

dependencies:
  flutter:
    sdk: flutter
  simple_orm:
    path: ../simple_orm
  simple_orm_annotation:
    path: ../annotation
dev_dependencies:
  flutter_test:
    sdk: flutter
  simple_orm_generator:
    path: ../generator
  build_runner: ^2.4.8
  analyzer: ^6.2.0

2. 创建entity

需要把entity放在lib/db/entity/目录下面。

import 'package:simple_orm_annotation/annotation/column_info.dart';
import 'package:simple_orm_annotation/annotation/table.dart';

@Table(name: 'user_info', index: {'sex_index': ['sex']})
class UserInfo {
  @ColumnInfo(name: 'id', primaryKey: true, autoIncrement: true)
  int? id;
  @ColumnInfo(name: 'username')
  String? username;
  @ColumnInfo(name: 'nickname')
  String? nickname;
  @ColumnInfo(name: 'sex')
  String? sex;
  @ColumnInfo(name: 'address')
  String? address;
}

3. 执行build_runner命令

运行以下命令生成dao文件:

flutter packages pub run build_runner build

生成的dao文件将位于lib/db/dao/generated/目录下。

// GENERATED CODE - DO NOT MODIFY BY HAND

// **************************************************************************
// DbGenerator
// **************************************************************************

// NOTE: 可以直接使用该生成类,建议最好继承生成类再使用

import 'package:simple_orm/base_dao.dart';
import 'package:sqflite/sqflite.dart';

class BaseUserInfoDao extends BaseDao<UserInfo> {
  static String tableName = "user_info";
  static late Database db;

  static const String columnId = "id";
  static const String columnUsername = "username";
  static const String columnNickname = "nickname";
  static const String columnSex = "sex";
  static const String columnAddress = "address";

  static String _createTableSQL() {
    return "CREATE TABLE IF NOT EXISTS `user_info` (`id` INTEGER PRIMARY KEY autoincrement,`username` TEXT,`nickname` TEXT,`sex` TEXT,`address` TEXT)";
  }

  static Map<String, String> _columnMap() => {
        columnId: 'INTEGER',
        columnUsername: 'TEXT',
        columnNickname: 'TEXT',
        columnSex: 'TEXT',
        columnAddress: 'TEXT',
      };

  static List<String>? _indexList() {
    return const [
      'CREATE INDEX IF NOT EXISTS `sex_index` ON user_info (`sex`);'
    ];
  }

  static _createTable() async {
    await db.execute(_createTableSQL());
  }

  static _createIndex() async {
    // 创建索引
    final indexs = _indexList();
    if (indexs != null) {
      for (var indexString in indexs) {
        if (indexString.isNotEmpty) {
          await db.execute(indexString);
        }
      }
    }
  }

  static _updateCols() async {
    final oldCols = await getTableInfo();
    final newCols = _columnMap();
    List<String> updateSQLs = [];
    // 检查新增字段
    newCols.forEach((key, value) {
      if (!oldCols.containsKey(key)) {
        updateSQLs.add('ALTER TABLE $tableName ADD COLUMN $key $value;');
      }
    });

    if (updateSQLs.isNotEmpty) {
      for (var sql in updateSQLs) {
        await db.execute(sql);
      }
    }
  }

  static getTableInfo() async {
    ensureDB();
    var ret = await db.rawQuery('PRAGMA table_info("$tableName")');
    if (ret != null && ret.isNotEmpty) {
      Map<String, String> result = {};
      for (var element in ret) {
        result[element['name'] as String] = element['type'] as String;
      }
      return result;
    } else {
      return {};
    }
  }

  static ensureDB() {
    if (db == null) {
      throw Exception('should invoke initTable first');
    }
  }

  static Future initTable(Database _db) async {
    db = _db;
    // 创建表
    await _createTable();
    // 更新字段
    await _updateCols();
    // 创建索引
    await _createIndex();
  }

  @override
  Future<bool> insertOrUpdate(UserInfo entity) async {
    ensureDB();
    Map<String, Object> values = {};
    if (entity.id != null && !entity.id!.isNaN) {
      values['id'] = entity.id!;
    }
    if (entity.username != null && entity.username!.isNotEmpty) {
      values['username'] = entity.username!;
    }
    if (entity.nickname != null && entity.nickname!.isNotEmpty) {
      values['nickname'] = entity.nickname!;
    }
    if (entity.sex != null && entity.sex!.isNotEmpty) {
      values['sex'] = entity.sex!;
    }
    if (entity.address != null && entity.address!.isNotEmpty) {
      values['address'] = entity.address!;
    }

    int ret = await db.insert(tableName, values,
        conflictAlgorithm: ConflictAlgorithm.replace);
    return ret != 0;
  }

  @override
  Future<UserInfo?> getById(dynamic id) async {
    ensureDB();
    final valuesList = await db.rawQuery('SELECT * FROM user_info WHERE id = ?', [id]);
    if (valuesList.isEmpty) {
      return null;
    }
    final values = valuesList[0];
    final item = UserInfo();

    item.id = values['id'] as int?;
    item.username = values['username'] as String?;
    item.nickname = values['nickname'] as String?;
    item.sex = values['sex'] as String?;
    item.address = values['address'] as String?;

    return item;
  }

  @override
  Future<UserInfo?> getFirstOrNull(UserInfo entity) async {
    ensureDB();
    final list = await getList(entity);
    if (list.isEmpty) {
      return null;
    }
    return list[0];
  }

  @override
  Future<List<UserInfo?>> getList([UserInfo? entity]) async {
    ensureDB();
    String querySql = 'SELECT * FROM user_info';
    if (entity != null) {
      StringBuffer whereSb = StringBuffer(' WHERE 1=1');
      if (entity.id != null && !entity.id!.isNaN) {
        whereSb.write(' AND ');
        whereSb.write('id=${entity.id}');
      }
      if (entity.username != null && entity.username!.isNotEmpty) {
        whereSb.write(' AND ');
        whereSb.write('username=${entity.username}');
      }
      if (entity.nickname != null && entity.nickname!.isNotEmpty) {
        whereSb.write(' AND ');
        whereSb.write('nickname=${entity.nickname}');
      }
      if (entity.sex != null && entity.sex!.isNotEmpty) {
        whereSb.write(' AND ');
        whereSb.write('sex=${entity.sex}');
      }
      if (entity.address != null && entity.address!.isNotEmpty) {
        whereSb.write(' AND ');
        whereSb.write('address=${entity.address}');
      }

      querySql = querySql + whereSb.toString();
    }

    final valuesList = await db.rawQuery(querySql);
    List<UserInfo> ret = [];
    for (var values in valuesList) {
      final item = UserInfo();
      item.id = values['id'] as int?;
      item.username = values['username'] as String?;
      item.nickname = values['nickname'] as String?;
      item.sex = values['sex'] as String?;
      item.address = values['address'] as String?;

      ret.add(item);
    }
    return ret;
  }

  @override
  Future<bool> delete([UserInfo? entity]) async {
    ensureDB();
    if (entity == null) {
      await db.execute('DELETE FROM user_info');
      return true;
    }
    String deleteSql = 'DELETE FROM user_info WHERE 1=1 ';
    StringBuffer whereSb = StringBuffer();
    if (entity.id != null && !entity.id!.isNaN) {
      whereSb.write(' AND ');
      whereSb.write('id=${entity.id}');
    }
    if (entity.username != null && entity.username!.isNotEmpty) {
      whereSb.write(' AND ');
      whereSb.write('username=${entity.username}');
    }
    if (entity.nickname != null && entity.nickname!.isNotEmpty) {
      whereSb.write(' AND ');
      whereSb.write('nickname=${entity.nickname}');
    }
    if (entity.sex != null && entity.sex!.isNotEmpty) {
      whereSb.write(' AND ');
      whereSb.write('sex=${entity.sex}');
    }
    if (entity.address != null && entity.address!.isNotEmpty) {
      whereSb.write(' AND ');
      whereSb.write('address=${entity.address}');
    }

    deleteSql = deleteSql + whereSb.toString();
    final ret = await db.rawDelete(deleteSql);
    return ret != 0;
  }

  @override
  Future<int> updateValues(
      Map<String, Object> updateValues, Map<String, Object> whereArgs) async {
    ensureDB();
    final whereSb = StringBuffer('1 = 1');
    if (whereArgs.isNotEmpty) {
      for (var ele in whereArgs.entries) {
        whereSb.write(' AND ');
        whereSb.write('${ele.key} = ${ele.value}');
      }
      return await db.update(tableName, updateValues,
          where: whereSb.toString());
    }
    return 0;
  }
}

4. 继承dao类

import 'package:simple_orm_example/db/dao/generated/base_user_info_dao.dart';

class UserInfoDao extends BaseUserInfoDao {
  static UserInfoDao? _instance;

  factory UserInfoDao() => _instance ??= UserInfoDao._();

  UserInfoDao._();
}

5. 使用dao

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  final db = await initDB();
  await BaseUserInfoDao.initTable(db);
  runApp(const MyApp());
}

initDB() async {
  // open the database
  final databasesPath = await getDatabasesPath();

  final dbPath = "$databasesPath/simple_orm_example.db";

  return await openDatabase(
    dbPath,
    version: 1,
    onCreate: (Database db, int version) async {},
    onUpgrade: (Database db, int oldVersion, int newVersion) async {},
  );
}

getUserList() {
  UserInfoDao().getList().then((value) {
    print('user count: ${value.length}');
  });
}

_addUser() {
  UserInfoDao().insertOrUpdate(UserInfo()
    ..username = 'user_1'
    ..nickname = 'nickname_1'
    ..address = 'address_1'
    ..sex = 'male'
  ).then((value) {
    print('insert user result: $value');
  });
}

Annotation

table.dart

class Table {
  /// 表名
  final String? name;
  /// 索引,"索引名": ["字段1", "字段2"]
  final Map<String, List<String>>? index;

  const Table({this.name, this.index});
}

column_info.dart

class ColumnInfo {
  /// 字段名
  final String? name;
  /// 是否是主键
  final bool primaryKey;
  /// 是否自增——只有int类型支持自增
  final bool? autoIncrement;
  const ColumnInfo({this.name, this.primaryKey = false, this.autoIncrement});
}

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

1 回复

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


simple_orm 是一个用于 Flutter 的轻量级对象关系映射(ORM)插件,它可以帮助开发者简化数据库操作。通过 simple_orm,你可以将 Dart 对象直接映射到数据库表,从而避免手动编写 SQL 语句。

安装

首先,你需要在 pubspec.yaml 文件中添加 simple_orm 依赖:

dependencies:
  simple_orm: ^1.0.0  # 请使用最新版本

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

基本用法

1. 定义实体类

使用 simple_orm,你需要定义一个实体类,并使用注解来标记它。例如:

import 'package:simple_orm/simple_orm.dart';

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

  @ColumnInfo(name: 'user_name')
  String? userName;

  @ColumnInfo(name: 'email')
  String? email;

  User({this.id, this.userName, this.email});
}
  • @Entity(tableName: 'users'):标记该类为实体类,并指定对应的数据库表名。
  • @PrimaryKey(autoIncrement: true):标记该字段为主键,并指定是否自增。
  • @ColumnInfo(name: 'user_name'):标记该字段为数据库列,并指定列名。

2. 初始化数据库

在使用 simple_orm 之前,你需要初始化数据库:

import 'package:simple_orm/simple_orm.dart';

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

  // 初始化数据库
  await SimpleOrm.init(
    databaseName: 'my_database.db',
    version: 1,
    entities: [User],
  );

  runApp(MyApp());
}

3. 插入数据

你可以使用 SimpleOrm.insert 方法将对象插入数据库:

User user = User(userName: 'JohnDoe', email: 'johndoe@example.com');
int id = await SimpleOrm.insert(user);
print('Inserted user with id: $id');

4. 查询数据

你可以使用 SimpleOrm.query 方法查询数据:

List<User> users = await SimpleOrm.query(User);
users.forEach((user) {
  print('User: ${user.userName}, Email: ${user.email}');
});

5. 更新数据

你可以使用 SimpleOrm.update 方法更新数据:

User user = users.first;
user.email = 'newemail@example.com';
await SimpleOrm.update(user);

6. 删除数据

你可以使用 SimpleOrm.delete 方法删除数据:

await SimpleOrm.delete(user);

高级用法

条件查询

simple_orm 支持条件查询,你可以使用 where 参数来指定查询条件:

List<User> users = await SimpleOrm.query(
  User,
  where: 'user_name = ?',
  whereArgs: ['JohnDoe'],
);

自定义查询

你还可以执行自定义的 SQL 查询:

List<Map<String, dynamic>> results = await SimpleOrm.rawQuery(
  'SELECT * FROM users WHERE email = ?',
  ['johndoe@example.com'],
);

数据库迁移

如果你的数据库结构发生了变化,你可以通过增加版本号来执行数据库迁移:

await SimpleOrm.init(
  databaseName: 'my_database.db',
  version: 2,  // 增加版本号
  entities: [User],
);
回到顶部