Cách sử dụng Hive đơn giản nhưng hiệu quả mà có thể bạn chưa biết

Hive là thư viện của Flutter, một công cụ lưu trữ dữ liệu trong bộ nhớ điện thoại. Bài viết này hướng đến những người đã biết cơ bản về Hive, nếu chưa biết Hive là gì có thể tham khảo tại đây . Hive có nhiều ưu điểm như tốc độ đọc ghi rất nhanh, thiết lập và sử dụng dễ dàng,…
Tuy nhiên, khi muốn lưu trữ một kiểu dữ liệu đặc biệt do chính bạn xác định, bạn phải đăng ký và tạo một trình cập nhật như dưới đây

mport 'package:hive/hive.dart';
part 'person.g.dart';

@HiveType(typeId: 1)
class Person {
 @HiveField(0)
 String name;

 @HiveField(1)
 int age;
}

Cách này gây ra rất nhiều bất tiện, khi chúng ta phải quản lý typeId cũng như phải viết rất nhiều code soạn sẵn mỗi khi muốn tạo một custom type. Có cách nào khác đơn giản và sạch sẽ hơn không? Câu trả lời là có.

Bài toán : Tạo cách lưu và đọc đối tượng của 2 lớp PersonUser trong cơ sở dữ liệu cục bộ. LớpPerson bao gồm nameage, lớp Person dùng bao gồm userId.

Bước 1: Tạo một lớp trừu tượng LocalEntity

abstract class LocalEntity {
 final String key;

 LocalEntity(this.key);
}

Bước 2: Đối với các lớp cần lưu trữ cục bộ, hãy triển khai lớp LocalEntity này

part 'person.g.dart';

// don't forget to add fromJson and toJson 
@JsonSerializable()
class Person implements LocalEntity{
 final String id;
  final String name;
  final int age;

  Person({
  required this.id,
    required this.name,
    required this.age,
  });

  factory Person.fromJson(Map<String, dynamic> json) => _$PersonFromJson(json);

  Map<String, dynamic> toJson() => _$PersonToJson(this);
  
 // Choose one of person field to be the key
 // this key will be used to save to local database
  @override
  String get key => id;
}

NOTE: Làm tương tự với lớp User

Bước 3: Tạo một lớp LocalEntityRepository

Lớp này chịu trách nhiệm liên lạc với Hive để đọc và ghi UserPerson . Trước hết hãy xem repo này ghi dữ liệu như thế nào (chúng ta sẽ quay lại phần ghi dữ liệu sau)

class LocalEntityRepository {

 final String boxName;

  Future<void> createOrUpdate(LocalEntity data) async {
    await Hive.box<String>(boxName)
        .put(data.key, jsonEncode(data));
  }

  Future<void> deleteData(String id) async {
    Hive.box<String>(boxName).delete(id);
  }
}

Bước 4: Chỉ cần sử dụng nó

Bạn có thể tạo và quản lý localEntityRepository ở bất cứ đâu. Tôi sẽ đưa ra một ví dụ về trường hợp sử dụng phổ biến trong bộ điều khiển. Để nội dung bài viết không bị lan truyền, mình sẽ không sử dụng Dependence Insert mà chỉ khởi tạo đối tượng một cách bình thường.

  • Đừng quên mở hộp trước khi sử dụng
class Controller {
 final personRepo = LocalEntityRepository("person");

 savePerson(Person person) {
  // add try/catch as you want
  personRepo.createOrUpdate(person);
 }
}

Rất đơn giản phải không? Như vậy là chúng ta đã ghi dữ liệu cục bộ rồi, việc tiếp theo là đọc dữ liệu từ Hive

Bước 5: Đọc dữ liệu

Hãy thêm 2 hàm sau vào LocalEntityRepository

class LocalEntityRepository {
 //...
 List<T> getAllEntities<T extends LocalEntity>() {
    final data = Hive.box<String>(boxName).values.map((e) {
      return LocalEntity.fromJson(T, Map<String, dynamic>.from(jsonDecode(e)));
    }).toList();
    return List<T>.from(data);
  }

  T? getEntity<T extends LocalEntity>(String id) {
    final dataLocal = Hive.box<String>(boxName).get(id); 
    if (dataLocal == null) return null;
    return LocalEntity.fromJson(T, Map<String, dynamic>.from(jsonDecode(dataLocal))) as T;
  }
}

Bạn sẽ gặp lỗi vì LocalEntity không có hàm fromJson tĩnh, đó là lúc chúng ta sửa lớp LocalEntity. Thêm chức năng xuất xưởng từ Json như bên dưới

abstract class LocalEntity {
  final String key;

  LocalEntity(this.key);
  
  factory LocalEntity.fromJson(Type type, Map<String, dynamic> e) {
    switch (type) {
     case Person:
        return Person.fromJson(e);
   case User:
        return User.fromJson(e);
      default:
        throw Exception();
    }
  }
}

Thế là xong, bây giờ chúng ta hãy đọc dữ liệu

class Controller {
 final personRepo = LocalEntityRepository("person");

 List<Person> getAllPerson() {
  final persons = personRepo.getAllEntities<Person>();
  return persons;
 }
}