hive icon indicating copy to clipboard operation
hive copied to clipboard

I am afraid of Hive Code Generator doesn't see generic types

Open dsrenesanse opened this issue 5 years ago • 8 comments
trafficstars

Screenshot 2020-04-05 at 16 13 02 Screenshot 2020-04-05 at 16 13 07

dsrenesanse avatar Apr 05 '20 13:04 dsrenesanse

Sadly, it does not, but it's being worked on. For now, you can manually write a class that implements TypeAdapter<T> and has an id field. Then you can register it for multiple IDs:

Hive
  ..registerAdapter(MyAdapter<int>(id: 10)
  ..registerAdapter(MyAdapter<double>(id: 11)

MarcelGarus avatar Apr 05 '20 23:04 MarcelGarus

Hi @marcelgarus @leisim , I was working on a desktop app and I need to save slightly complex data which also includes Generics, so I'd really appreciate if you could guide me on how to save my data in Hive. Other than the error, it'd be really nice of you to give me a few pointers on what I might be doing wrong here.

Note: The object I want to save is CodeTemplate.

Error

Launching lib/main.dart on macOS in debug mode...
Building macOS application...

Compiler message:
lib/utils/models.g.dart:131:34: Error: 'T' isn't a type.
      defaultValue: fields[3] as T,
                                 ^
lib/utils/models.g.dart:135:30: Error: 'T' isn't a type.
      ..value = fields[2] as T
                             ^

My Data Structure


@HiveType(typeId: 2)
class CodeTemplate extends HiveObject {
  @HiveField(1)
  String name;
  @HiveField(2)
  String language;
  @HiveField(3)
  Map<int, TemplateSequenceOption> sequence;

  CodeTemplate({
    @required this.name,
    @required this.language,
    @required this.sequence,
});
}

@HiveType(typeId: 3)
class TemplateSequenceOption extends HiveObject {
  @HiveField(1)
  int id;
  @HiveField(2)
  String name;
  @HiveField(3)
  Color color;
  @HiveField(4)
  IconData iconData;
  @HiveField(5)
  TemplateSequenceOptionType type;
  @HiveField(6)
  Map<String, TemplateSequenceProperty> properties;

  TemplateSequenceOption(this.id, this.name, this.color, this.iconData,
      this.type, this.properties);
}

@HiveType(typeId: 4)
class TemplateSequenceProperty<T> extends HiveObject {
  @HiveField(1)
  int id;
  @HiveField(2)
  T value;
  @HiveField(3)
  T defaultValue;
  @HiveField(4)
  String hintText;
  @HiveField(5)
  Type dataType;
  @HiveField(6)
  bool optional;

  TemplateSequenceProperty(this.id,
      {this.defaultValue, this.hintText, this.optional = true})
      : this.dataType = T;
}

My Data Saving Code


      await Hive.initFlutter(); // Called once only
      Hive.registerAdapter(CodeTemplateAdapter()); // Called once only
      await Hive.openBox<CodeTemplate>(codeTemplatesBox); // Called once only

      CodeTemplate template = CodeTemplate(
        name: _nameController.text,
        language: _selectedLanguage,
        sequence: _sequence,
      );

      Box<CodeTemplate> _codeTemplatesBox = Hive.box(codeTemplatesBox);
      await _codeTemplatesBox.add(template);

Models.G.Dart

...

class TemplateSequencePropertyAdapter
    extends TypeAdapter<TemplateSequenceProperty> {
  @override
  final typeId = 4;

  @override
  TemplateSequenceProperty read(BinaryReader reader) {
    var numOfFields = reader.readByte();
    var fields = <int, dynamic>{
      for (var i = 0; i < numOfFields; i++) reader.readByte(): reader.read(),
    };
    return TemplateSequenceProperty(
      fields[1] as int,
      defaultValue: fields[3] as T, // ---------- Line 131
      hintText: fields[4] as String,
      optional: fields[6] as bool,
    )
      ..value = fields[2] as T // ---------- Line 135
      ..dataType = fields[5] as Type;
  }

  @override
  void write(BinaryWriter writer, TemplateSequenceProperty obj) {
    writer
      ..writeByte(6)
      ..writeByte(1)
      ..write(obj.id)
      ..writeByte(2)
      ..write(obj.value)
      ..writeByte(3)
      ..write(obj.defaultValue)
      ..writeByte(4)
      ..write(obj.hintText)
      ..writeByte(5)
      ..write(obj.dataType)
      ..writeByte(6)
      ..write(obj.optional);
  }
}

BasedMusa avatar Apr 11 '20 12:04 BasedMusa

Hive doesn't support generics out-of-the box. Yet. That means, generating code for generic classes doesn't work yet. Which in turn means, you need to write your own custom type adapter.

Here's a modified version of the automatically generated TemplateSequencePropertyAdapter, which supports generics. You can copy it into your project (I only changed the first 6 lines):

class TemplateSequencePropertyAdapter<T>
    extends TypeAdapter<TemplateSequenceProperty<T>> {
  TemplateSequencePropertyAdapter({@required this.typeId}) : assert(typeId != null);

  @override
  final int typeId;

  @override
  TemplateSequenceProperty read(BinaryReader reader) {
    var numOfFields = reader.readByte();
    var fields = <int, dynamic>{
      for (var i = 0; i < numOfFields; i++) reader.readByte(): reader.read(),
    };
    return TemplateSequenceProperty(
      fields[1] as int,
      defaultValue: fields[3] as T,
      hintText: fields[4] as String,
      optional: fields[6] as bool,
    )
      ..value = fields[2] as T
      ..dataType = fields[5] as Type;
  }

  @override
  void write(BinaryWriter writer, TemplateSequenceProperty obj) {
    writer
      ..writeByte(6)
      ..writeByte(1)
      ..write(obj.id)
      ..writeByte(2)
      ..write(obj.value)
      ..writeByte(3)
      ..write(obj.defaultValue)
      ..writeByte(4)
      ..write(obj.hintText)
      ..writeByte(5)
      ..write(obj.dataType)
      ..writeByte(6)
      ..write(obj.optional);
  }
}

You can then register this adapter for all the generic types you want to support:

await Hive.initFlutter();
Hive
  ..registerAdapter(CodeTemplateAdapter())
  ..registerAdapter(TemplateSequenceOptionAdapter())
  ..registerAdapter(TemplateSequencePropertyAdapter<int>(typeId: 9))
  ..registerAdapter(TemplateSequencePropertyAdapter<String>(typeId: 10))
  ..registerAdapter(TemplateSequencePropertyAdapter<double>(typeId: 11));
await Hive.openBox<CodeTemplate>(codeTemplatesBox);

Finally, you can use this hand-written adapter in your code:

CodeTemplate template = CodeTemplate(
  name: _nameController.text,
  language: _selectedLanguage,
  sequence: _sequence,
);

Box<CodeTemplate> _codeTemplatesBox = Hive.box(codeTemplatesBox);
await _codeTemplatesBox.add(template);

I know this is loads of boilerplate, but I suppose type-safety comes built-in with Hive 2.0.

MarcelGarus avatar Apr 12 '20 14:04 MarcelGarus

@marcelgarus Thanks for the response, one last thing, since we've manually written the adapter, I should remove the @HiveType and @HiveField properties from CodeTemplateProperty still extend it from a HiveObject. Right?

And can the generic be of another HiveObject type? For example, I need to use this in my code:

TemplateSequenceProperty<CodeSnippet> property = TemplateSequenceProperty<CodeSnippet>();

//Code Snippet extends from a HiveObject and has a TypeAdapter with typeId: 1

BasedMusa avatar Apr 12 '20 16:04 BasedMusa

Yes exactly, the annotations should be removed, but you should still extend HiveObject if you want save() functions etc.

It shouldn't be a problem that the generic type also extends HiveObject.

MarcelGarus avatar Apr 12 '20 17:04 MarcelGarus

@MarcelGarus Hi any idea if genericType has been implemented ever since ? Thanks in advance !

yannisoo avatar Jan 28 '22 18:01 yannisoo

AFAIK, that's not planned for the future. Hive creator @leisim is working on the new database Isar, so Hive is somewhat in a feature-freeze.

MarcelGarus avatar Feb 04 '22 15:02 MarcelGarus

Hive doesn't support generics out-of-the box. Yet. That means, generating code for generic classes doesn't work yet. Which in turn means, you need to write your own custom type adapter.

Here's a modified version of the automatically generated TemplateSequencePropertyAdapter, which supports generics. You can copy it into your project (I only changed the first 6 lines):

class TemplateSequencePropertyAdapter<T>
    extends TypeAdapter<TemplateSequenceProperty<T>> {
  TemplateSequencePropertyAdapter({@required this.typeId}) : assert(typeId != null);

  @override
  final int typeId;

  @override
  TemplateSequenceProperty read(BinaryReader reader) {
    var numOfFields = reader.readByte();
    var fields = <int, dynamic>{
      for (var i = 0; i < numOfFields; i++) reader.readByte(): reader.read(),
    };
    return TemplateSequenceProperty(
      fields[1] as int,
      defaultValue: fields[3] as T,
      hintText: fields[4] as String,
      optional: fields[6] as bool,
    )
      ..value = fields[2] as T
      ..dataType = fields[5] as Type;
  }

  @override
  void write(BinaryWriter writer, TemplateSequenceProperty obj) {
    writer
      ..writeByte(6)
      ..writeByte(1)
      ..write(obj.id)
      ..writeByte(2)
      ..write(obj.value)
      ..writeByte(3)
      ..write(obj.defaultValue)
      ..writeByte(4)
      ..write(obj.hintText)
      ..writeByte(5)
      ..write(obj.dataType)
      ..writeByte(6)
      ..write(obj.optional);
  }
}

You can then register this adapter for all the generic types you want to support:

await Hive.initFlutter();
Hive
  ..registerAdapter(CodeTemplateAdapter())
  ..registerAdapter(TemplateSequenceOptionAdapter())
  ..registerAdapter(TemplateSequencePropertyAdapter<int>(typeId: 9))
  ..registerAdapter(TemplateSequencePropertyAdapter<String>(typeId: 10))
  ..registerAdapter(TemplateSequencePropertyAdapter<double>(typeId: 11));
await Hive.openBox<CodeTemplate>(codeTemplatesBox);

Finally, you can use this hand-written adapter in your code:

CodeTemplate template = CodeTemplate(
  name: _nameController.text,
  language: _selectedLanguage,
  sequence: _sequence,
);

Box<CodeTemplate> _codeTemplatesBox = Hive.box(codeTemplatesBox);
await _codeTemplatesBox.add(template);

I know this is loads of boilerplate, but I suppose type-safety comes built-in with Hive 2.0.

Still works. Thanks!

definitelyme avatar May 10 '24 15:05 definitelyme