From 2f89943b32983129bbd44cb1f06bfb9fe5467f0f Mon Sep 17 00:00:00 2001 From: Rexios Date: Tue, 12 Nov 2024 20:29:51 -0500 Subject: [PATCH 1/3] GenerateAdapters annotation changes --- hive/CHANGELOG.md | 4 + hive/README.md | 87 ++++++++++++++----- hive/example/lib/freezed.dart | 9 +- hive/example/lib/freezed.freezed.dart | 29 ++----- hive/example/lib/hive/hive_adapters.dart | 13 +++ .../hive_adapters.g.dart} | 46 +++++++++- hive/example/lib/hive/hive_adapters.g.yaml | 25 ++++++ .../lib/{ => hive}/hive_registrar.g.dart | 3 +- hive/example/lib/main.dart | 10 +-- hive/example/lib/main.g.dart | 47 ---------- hive/lib/hive.dart | 1 + .../src/annotations/generate_adapters.dart | 21 +++++ hive/pubspec.yaml | 2 +- hive/readme/add_fields/person_1.dart | 4 - hive/readme/add_fields/person_2.dart | 6 -- hive/readme/store_objects/hive_adapters.dart | 9 ++ .../readme/store_objects/hive_adapters.g.dart | 1 + hive/readme/store_objects/person.dart | 4 - 18 files changed, 192 insertions(+), 129 deletions(-) create mode 100644 hive/example/lib/hive/hive_adapters.dart rename hive/example/lib/{freezed.g.dart => hive/hive_adapters.g.dart} (53%) create mode 100644 hive/example/lib/hive/hive_adapters.g.yaml rename hive/example/lib/{ => hive}/hive_registrar.g.dart (78%) delete mode 100644 hive/example/lib/main.g.dart create mode 100644 hive/lib/src/annotations/generate_adapters.dart create mode 100644 hive/readme/store_objects/hive_adapters.dart create mode 100644 hive/readme/store_objects/hive_adapters.g.dart diff --git a/hive/CHANGELOG.md b/hive/CHANGELOG.md index 7c98f2a3..331457e5 100644 --- a/hive/CHANGELOG.md +++ b/hive/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.8.0 + +- Adds `GenerateAdapters` annotation and relevant documentation + ## 2.7.0+1 - Adds a storage benchmark to compare Hive CE with Hive v4 diff --git a/hive/README.md b/hive/README.md index 882cca31..13e27082 100644 --- a/hive/README.md +++ b/hive/README.md @@ -27,6 +27,9 @@ Hive CE is a spiritual continuation of Hive v2 with the following new features: - Support for constructor parameter defaults - Freezed support - Support for generating adapters with classes that use named imports +- Automatic type adapter generation using the `GenerateAdapters` annotation + - No more manually adding annotations to every type and field + - Generate adapters for classes outside the current package ## Hive CE (v2) vs Hive v4 (Isar) @@ -187,19 +190,34 @@ Hive not only supports primitives, lists, and maps but also any Dart object you ```dart import 'package:hive_ce/hive.dart'; -@HiveType(typeId: 0) class Person extends HiveObject { Person({required this.name, required this.age}); - @HiveField(0) String name; - - @HiveField(1) int age; } ``` +### Create a `GenerateAdapters` annotation + +Usually this is placed in `lib/hive/hive_adapters.dart` + + + +```dart +import 'package:hive_ce/hive.dart'; +import 'person.dart'; + +part 'hive_adapters.g.dart'; + +@GenerateAdapters([AdapterSpec()]) +// Annotations must be on some element +// ignore: unused_element +void _() {} + +``` + ### Update `pubspec.yaml` ```yaml @@ -214,7 +232,15 @@ dev_dependencies: dart pub run build_runner build --delete-conflicting-outputs ``` -This will generate all of your `TypeAdapter`s as well as a Hive extension to register them all in one go +This will generate the following: + +- TypeAdapters for the specified AdapterSpecs +- TypeAdapters for all explicitly defined HiveTypes +- A `hive_adapters.g.dart` file containing all adapters generated from the `GenerateAdapters` annotation +- A `hive_adapters.g.yaml` file +- A `hive_registrar.g.dart` file containing an extension method to register all generated adapters + +All of the generated files should be checked into version control. These files are explained in more detail below. ### Use the Hive registrar @@ -256,6 +282,38 @@ void example() async { ``` +### About `hive_adapters.g.yaml` + +The Hive schema is a generated yaml file that contains the information necessary to incrementally update the generated TypeAdapters as your model classes evolve. + +Some migrations might require manual modifications to the Hive schema file. One example is field renaming. Without manual intervention, the generator will see both an added and removed field. To resolve this, manually rename the field in the schema. + +### Migrating to `GenerateAdapters` + +If you already have model classes with `HiveType` and `HiveField` annotations, you can take the following steps to migrate to the new `GenerateAdapters` annotation: + +1. Convert all default values to constructor parameter defaults +2. Add the following to your `build.yaml` file: + +```yaml +targets: + $default: + builders: + hive_ce_generator|hive_schema_migrator: + enabled: true +``` + +3. Run the `build_runner`. This will generate `lib/hive/hive_adapters.dart` and `lib/hive/hive_adapters.g.yaml`. +4. Revert the `build.yaml` changes +5. Remove all explicit `HiveType` and `HiveField` annotations from your model classes +6. Run the `build_runner` again + +### Explicitly defining HiveTypes + +The old method of defining HiveTypes is still supported, but should be unnecessary now that Hive CE supports constructor parameter defaults. If you have a use-case that `GenerateAdapters` does not support, please [create an issue on GitHub](https://github.com/IO-Design-Team/hive_ce/issues/new). + +Unfortunately it is not possible for `GenerateAdapters` to handle private fields. You can use `@protected` instead if necessary. + ## Add fields to objects When adding a new non-nullable field to an existing object, you need to specify a default value to ensure compatibility with existing data. @@ -267,14 +325,10 @@ For example, consider an existing database with a `Person` object: ```dart import 'package:hive_ce/hive.dart'; -@HiveType(typeId: 0) class Person extends HiveObject { Person({required this.name, required this.age}); - @HiveField(0) String name; - - @HiveField(1) int age; } @@ -287,31 +341,16 @@ If you want to add a `balance` field, you must specify a default value or else r ```dart import 'package:hive_ce/hive.dart'; -@HiveType(typeId: 0) class Person extends HiveObject { Person({required this.name, required this.age, this.balance = 0}); - @HiveField(0) String name; - - @HiveField(1) int age; - - @HiveField(2) double balance; } ``` -Or specify it in the `HiveField` annotation: - -```dart -@HiveField(2, defaultValue: 0) -int balance; -``` - -Alternatively, you can write custom migration code to handle the transition. - After modifying the model, remember to run `build_runner` to regenerate the TypeAdapters ## Hive ❤️ Flutter diff --git a/hive/example/lib/freezed.dart b/hive/example/lib/freezed.dart index 8c730fd4..08e24fd2 100644 --- a/hive/example/lib/freezed.dart +++ b/hive/example/lib/freezed.dart @@ -1,15 +1,12 @@ import 'package:freezed_annotation/freezed_annotation.dart'; -import 'package:hive_ce/hive.dart'; part 'freezed.freezed.dart'; -part 'freezed.g.dart'; @freezed -@HiveType(typeId: 100) class FreezedPerson with _$FreezedPerson { const factory FreezedPerson({ - @HiveField(0) required String firstName, - @HiveField(1) required String lastName, - @HiveField(2) required int age, + required String firstName, + required String lastName, + required int age, }) = _FreezedPerson; } diff --git a/hive/example/lib/freezed.freezed.dart b/hive/example/lib/freezed.freezed.dart index 4e50080b..3f7e9aae 100644 --- a/hive/example/lib/freezed.freezed.dart +++ b/hive/example/lib/freezed.freezed.dart @@ -16,11 +16,8 @@ final _privateConstructorUsedError = UnsupportedError( /// @nodoc mixin _$FreezedPerson { - @HiveField(0) String get firstName => throw _privateConstructorUsedError; - @HiveField(1) String get lastName => throw _privateConstructorUsedError; - @HiveField(2) int get age => throw _privateConstructorUsedError; /// Create a copy of FreezedPerson @@ -36,10 +33,7 @@ abstract class $FreezedPersonCopyWith<$Res> { FreezedPerson value, $Res Function(FreezedPerson) then) = _$FreezedPersonCopyWithImpl<$Res, FreezedPerson>; @useResult - $Res call( - {@HiveField(0) String firstName, - @HiveField(1) String lastName, - @HiveField(2) int age}); + $Res call({String firstName, String lastName, int age}); } /// @nodoc @@ -86,10 +80,7 @@ abstract class _$$FreezedPersonImplCopyWith<$Res> __$$FreezedPersonImplCopyWithImpl<$Res>; @override @useResult - $Res call( - {@HiveField(0) String firstName, - @HiveField(1) String lastName, - @HiveField(2) int age}); + $Res call({String firstName, String lastName, int age}); } /// @nodoc @@ -130,18 +121,13 @@ class __$$FreezedPersonImplCopyWithImpl<$Res> class _$FreezedPersonImpl implements _FreezedPerson { const _$FreezedPersonImpl( - {@HiveField(0) required this.firstName, - @HiveField(1) required this.lastName, - @HiveField(2) required this.age}); + {required this.firstName, required this.lastName, required this.age}); @override - @HiveField(0) final String firstName; @override - @HiveField(1) final String lastName; @override - @HiveField(2) final int age; @override @@ -175,18 +161,15 @@ class _$FreezedPersonImpl implements _FreezedPerson { abstract class _FreezedPerson implements FreezedPerson { const factory _FreezedPerson( - {@HiveField(0) required final String firstName, - @HiveField(1) required final String lastName, - @HiveField(2) required final int age}) = _$FreezedPersonImpl; + {required final String firstName, + required final String lastName, + required final int age}) = _$FreezedPersonImpl; @override - @HiveField(0) String get firstName; @override - @HiveField(1) String get lastName; @override - @HiveField(2) int get age; /// Create a copy of FreezedPerson diff --git a/hive/example/lib/hive/hive_adapters.dart b/hive/example/lib/hive/hive_adapters.dart new file mode 100644 index 00000000..a4390873 --- /dev/null +++ b/hive/example/lib/hive/hive_adapters.dart @@ -0,0 +1,13 @@ +import 'package:example/freezed.dart'; +import 'package:example/main.dart'; +import 'package:hive_ce/hive.dart'; + +part 'hive_adapters.g.dart'; + +@GenerateAdapters([ + AdapterSpec(), + AdapterSpec(), +]) +// This is for code generation +// ignore: unused_element +void _() {} diff --git a/hive/example/lib/freezed.g.dart b/hive/example/lib/hive/hive_adapters.g.dart similarity index 53% rename from hive/example/lib/freezed.g.dart rename to hive/example/lib/hive/hive_adapters.g.dart index 354a1cee..52d14c03 100644 --- a/hive/example/lib/freezed.g.dart +++ b/hive/example/lib/hive/hive_adapters.g.dart @@ -1,14 +1,54 @@ // GENERATED CODE - DO NOT MODIFY BY HAND -part of 'freezed.dart'; +part of 'hive_adapters.dart'; // ************************************************************************** -// TypeAdapterGenerator +// AdaptersGenerator // ************************************************************************** +class PersonAdapter extends TypeAdapter { + @override + final int typeId = 0; + + @override + Person read(BinaryReader reader) { + final numOfFields = reader.readByte(); + final fields = { + for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(), + }; + return Person( + name: fields[0] as String, + age: (fields[1] as num).toInt(), + friends: (fields[2] as List).cast(), + ); + } + + @override + void write(BinaryWriter writer, Person obj) { + writer + ..writeByte(3) + ..writeByte(0) + ..write(obj.name) + ..writeByte(1) + ..write(obj.age) + ..writeByte(2) + ..write(obj.friends); + } + + @override + int get hashCode => typeId.hashCode; + + @override + bool operator ==(Object other) => + identical(this, other) || + other is PersonAdapter && + runtimeType == other.runtimeType && + typeId == other.typeId; +} + class FreezedPersonAdapter extends TypeAdapter { @override - final int typeId = 100; + final int typeId = 1; @override FreezedPerson read(BinaryReader reader) { diff --git a/hive/example/lib/hive/hive_adapters.g.yaml b/hive/example/lib/hive/hive_adapters.g.yaml new file mode 100644 index 00000000..71cfb74c --- /dev/null +++ b/hive/example/lib/hive/hive_adapters.g.yaml @@ -0,0 +1,25 @@ +# Generated by Hive CE +# Manual modifications may be necessary for certain migrations +# Check in to version control +nextTypeId: 2 +types: + Person: + typeId: 0 + nextIndex: 3 + fields: + name: + index: 0 + age: + index: 1 + friends: + index: 2 + FreezedPerson: + typeId: 1 + nextIndex: 3 + fields: + firstName: + index: 0 + lastName: + index: 1 + age: + index: 2 diff --git a/hive/example/lib/hive_registrar.g.dart b/hive/example/lib/hive/hive_registrar.g.dart similarity index 78% rename from hive/example/lib/hive_registrar.g.dart rename to hive/example/lib/hive/hive_registrar.g.dart index 0efb9d27..37cdbcb2 100644 --- a/hive/example/lib/hive_registrar.g.dart +++ b/hive/example/lib/hive/hive_registrar.g.dart @@ -3,8 +3,7 @@ // Check in to version control import 'package:hive_ce/hive.dart'; -import 'package:example/freezed.dart'; -import 'package:example/main.dart'; +import 'package:example/hive/hive_adapters.dart'; extension HiveRegistrar on HiveInterface { void registerAdapters() { diff --git a/hive/example/lib/main.dart b/hive/example/lib/main.dart index 42498a72..c8783f20 100644 --- a/hive/example/lib/main.dart +++ b/hive/example/lib/main.dart @@ -1,21 +1,13 @@ import 'dart:io'; -import 'package:example/hive_registrar.g.dart'; +import 'package:example/hive/hive_registrar.g.dart'; import 'package:hive_ce/hive.dart'; -part 'main.g.dart'; - -@HiveType(typeId: 1) class Person { Person({required this.name, required this.age, required this.friends}); - @HiveField(0) String name; - - @HiveField(1) int age; - - @HiveField(2) List friends; @override diff --git a/hive/example/lib/main.g.dart b/hive/example/lib/main.g.dart deleted file mode 100644 index 73933e58..00000000 --- a/hive/example/lib/main.g.dart +++ /dev/null @@ -1,47 +0,0 @@ -// GENERATED CODE - DO NOT MODIFY BY HAND - -part of 'main.dart'; - -// ************************************************************************** -// TypeAdapterGenerator -// ************************************************************************** - -class PersonAdapter extends TypeAdapter { - @override - final int typeId = 1; - - @override - Person read(BinaryReader reader) { - final numOfFields = reader.readByte(); - final fields = { - for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(), - }; - return Person( - name: fields[0] as String, - age: (fields[1] as num).toInt(), - friends: (fields[2] as List).cast(), - ); - } - - @override - void write(BinaryWriter writer, Person obj) { - writer - ..writeByte(3) - ..writeByte(0) - ..write(obj.name) - ..writeByte(1) - ..write(obj.age) - ..writeByte(2) - ..write(obj.friends); - } - - @override - int get hashCode => typeId.hashCode; - - @override - bool operator ==(Object other) => - identical(this, other) || - other is PersonAdapter && - runtimeType == other.runtimeType && - typeId == other.typeId; -} diff --git a/hive/lib/hive.dart b/hive/lib/hive.dart index f88a6883..47036746 100644 --- a/hive/lib/hive.dart +++ b/hive/lib/hive.dart @@ -10,6 +10,7 @@ export 'src/box_collection/box_collection_stub.dart' if (dart.library.io) 'package:hive_ce/src/box_collection/box_collection.dart'; export 'src/object/hive_object.dart' show HiveObject, HiveObjectMixin; +export 'src/annotations/generate_adapters.dart'; export 'src/annotations/hive_field.dart'; export 'src/annotations/hive_type.dart'; export 'src/binary/binary_reader.dart'; diff --git a/hive/lib/src/annotations/generate_adapters.dart b/hive/lib/src/annotations/generate_adapters.dart new file mode 100644 index 00000000..e797ef41 --- /dev/null +++ b/hive/lib/src/annotations/generate_adapters.dart @@ -0,0 +1,21 @@ +/// Annotation to generate TypeAdapters for the given [specs] +class GenerateAdapters { + /// Constructor + // coverage:ignore-start + const GenerateAdapters(this.specs, {this.firstTypeId = 0}); + // coverage:ignore-end + + /// The classes to generate TypeAdapters for + final List specs; + + /// The first typeId to use + final int firstTypeId; +} + +/// Configuration that specifies the generation of a TypeAdapter +class AdapterSpec { + /// Constructor + // coverage:ignore-start + const AdapterSpec(); + // coverage:ignore-end +} diff --git a/hive/pubspec.yaml b/hive/pubspec.yaml index 2e3236ef..a2de9102 100644 --- a/hive/pubspec.yaml +++ b/hive/pubspec.yaml @@ -1,6 +1,6 @@ name: hive_ce description: Hive Community Edition - A spiritual continuation of Hive v2 -version: 2.7.0+1 +version: 2.8.0 homepage: https://github.com/IO-Design-Team/hive_ce/tree/main/hive documentation: https://docs.hivedb.dev/ diff --git a/hive/readme/add_fields/person_1.dart b/hive/readme/add_fields/person_1.dart index 7a0f0e4d..90dd4798 100644 --- a/hive/readme/add_fields/person_1.dart +++ b/hive/readme/add_fields/person_1.dart @@ -1,12 +1,8 @@ import 'package:hive_ce/hive.dart'; -@HiveType(typeId: 0) class Person extends HiveObject { Person({required this.name, required this.age}); - @HiveField(0) String name; - - @HiveField(1) int age; } diff --git a/hive/readme/add_fields/person_2.dart b/hive/readme/add_fields/person_2.dart index 3257017c..59707a00 100644 --- a/hive/readme/add_fields/person_2.dart +++ b/hive/readme/add_fields/person_2.dart @@ -1,15 +1,9 @@ import 'package:hive_ce/hive.dart'; -@HiveType(typeId: 0) class Person extends HiveObject { Person({required this.name, required this.age, this.balance = 0}); - @HiveField(0) String name; - - @HiveField(1) int age; - - @HiveField(2) double balance; } diff --git a/hive/readme/store_objects/hive_adapters.dart b/hive/readme/store_objects/hive_adapters.dart new file mode 100644 index 00000000..473a5086 --- /dev/null +++ b/hive/readme/store_objects/hive_adapters.dart @@ -0,0 +1,9 @@ +import 'package:hive_ce/hive.dart'; +import 'person.dart'; + +part 'hive_adapters.g.dart'; + +@GenerateAdapters([AdapterSpec()]) +// Annotations must be on some element +// ignore: unused_element +void _() {} diff --git a/hive/readme/store_objects/hive_adapters.g.dart b/hive/readme/store_objects/hive_adapters.g.dart new file mode 100644 index 00000000..eb4cd482 --- /dev/null +++ b/hive/readme/store_objects/hive_adapters.g.dart @@ -0,0 +1 @@ +part of 'hive_adapters.dart'; diff --git a/hive/readme/store_objects/person.dart b/hive/readme/store_objects/person.dart index 7a0f0e4d..90dd4798 100644 --- a/hive/readme/store_objects/person.dart +++ b/hive/readme/store_objects/person.dart @@ -1,12 +1,8 @@ import 'package:hive_ce/hive.dart'; -@HiveType(typeId: 0) class Person extends HiveObject { Person({required this.name, required this.age}); - @HiveField(0) String name; - - @HiveField(1) int age; } From 62339bcfa19b793097f4f9bd799f189dacefe719 Mon Sep 17 00:00:00 2001 From: Rexios Date: Tue, 12 Nov 2024 21:01:11 -0500 Subject: [PATCH 2/3] Use hive_ce_generator from git in example --- hive/example/pubspec.yaml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/hive/example/pubspec.yaml b/hive/example/pubspec.yaml index c1d1ba25..c1a709bc 100644 --- a/hive/example/pubspec.yaml +++ b/hive/example/pubspec.yaml @@ -17,4 +17,9 @@ dependency_overrides: hive_ce: path: ../ hive_ce_generator: - path: ../../hive_generator + # TODO: Revert + # path: ../../hive_generator + git: + url: https://github.com/IO-Design-Team/hive_ce + ref: 4f06756e5eee0b027f2e9d623e8136620309afcb + path: hive_generator From af9410dc0dc144ad183b6e3ea51d3c1bcfd0fc83 Mon Sep 17 00:00:00 2001 From: Rexios Date: Tue, 19 Nov 2024 13:22:32 -0500 Subject: [PATCH 3/3] Updating readme --- hive/README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/hive/README.md b/hive/README.md index 13e27082..624faad9 100644 --- a/hive/README.md +++ b/hive/README.md @@ -244,6 +244,8 @@ All of the generated files should be checked into version control. These files a ### Use the Hive registrar +The Hive Registrar allows you to register all generated TypeAdapters in one call + ```dart import 'dart:io'; import 'package:hive_ce/hive.dart'; @@ -286,7 +288,7 @@ void example() async { The Hive schema is a generated yaml file that contains the information necessary to incrementally update the generated TypeAdapters as your model classes evolve. -Some migrations might require manual modifications to the Hive schema file. One example is field renaming. Without manual intervention, the generator will see both an added and removed field. To resolve this, manually rename the field in the schema. +Some migrations may require manual modifications to the Hive schema file. One example is class/field renaming. Without manual intervention, the generator will see both an added and removed class/field. To resolve this, manually rename the class/field in the schema. ### Migrating to `GenerateAdapters`