isar / hive

Lightweight and blazing fast key-value database written in pure Dart.
Apache License 2.0
4.1k stars 406 forks source link

How to migrate hive box on production #640

Closed ermand-hoxha closed 2 years ago

ermand-hoxha commented 3 years ago

I have publish my app on PlayStore and AppStore. Now i have made a change on one box i have created before. How can i migrate box to the new one, because i want to put new field with userId on it.

themisir commented 3 years ago

You can add a new field to exists HiveType-s. Only thing to keep in mind that you can not modify previous HiveFields. If you need to remove a field, you can remove it but skip its field ID.

For example:

class Customer {
  @HiveField(1)
  final int id;

  @HiveField(2)
  final String name;
}

To modify String name with int age you can write like:

class Customer {
  @HiveField(1)
  final int id;

  @HiveField(3) // We should not use previous field ID with different data type, so we skip it to avoid read errors & data damages.
  final int age;
}

You can read more about migrations on Hive docs:

Generate Adapter > Updating a class

ermand-hoxha commented 3 years ago

What i want is to add new field, but with value. Example: Old box:

      class Exam{ 
           @HiveField(1) 
           final int id; 
           @HiveField(2) 
           final String name; 
       }

New box:

        class Exam { 
            @HiveField(1)
             final int id; 
            @HiveField(2) 
             final String name; 
             @HiveField(3) 
              final String  userId
       }
themisir commented 3 years ago

@ermand-hoxha your class migration is okay.

An additional question: are you using null safety? If you do I would suggest using nullable String type cause previous entries doesn't contains that field: @HiveField(3) finalString? userId.

ermand-hoxha commented 3 years ago

No i'm not yet, i will. Thank you. But how can i fill previus data with userId value?

themisir commented 3 years ago

But how can i fill previus data with userId value?

I just published hive_generator v1.1.0 and hive v2.0.4, which adds default value support. So, after upgrading both packages you can define default value as like below:

@HiveField(3, defaultValue: 0)
final String userId;
ermand-hoxha commented 3 years ago

After i changed hive version, it required me to upgrade some others packages. I upgraded them and then i found this issue:

  Because flutter_cached_pdfview 0.3.5 depends on path_provider ^1.6.27 and no versions of flutter_cached_pdfview match >0.3.5 <0.4.0, flutter_cached_pdfview ^0.3.5 requires path_provider ^1.6.27.

My packages version:

themisir commented 3 years ago

You can try adding dependency_override until flutter_cached_pdfview becomes compatible with path_provider 2.0.0.

dependency_overrides:
  path_provider: ^2.0.0
Diaglyonok commented 3 years ago

@TheMisir , is there way to move data from one class to another? I mean, there is class Example with some fields, and I want to create class Example2 with similar values and move data (Example -> Example2).

themisir commented 3 years ago

@TheMisir , is there way to move data from one class to another? I mean, there is class Example with some fields, and I want to create class Example2 with similar values and move data (Example -> Example2).

for (final key in exampleBox.keys) {
  final example = exampleBox.get(key);
  example2Box.put(key, Example2(
    field1: example.field1,
    field2: example.field2,
    ...
  ));
}
svonidze commented 3 years ago

from https://docs.hivedb.dev/#/custom-objects/generate_adapter?id=updating-a-class

If you add new fields, any objects written by the "old" adapter can still be read by the new adapter.

that is not 100% true, since hive is migrated to null-safety you have to specify defaultValue to any new not-null filed otherwise you will get

type 'Null' is not a subtype of type 'bool' in type cast

due to previously generated code fields[1] as bool.

so with @HiveField(1, defaultValue: false) the hive_generator will rewrite the code fields[1] == null ? false : fields[1] as bool,

I think this info has to be added to the docs, @TheMisir do you agree?

jack24254029 commented 3 years ago

Can typeId reused? I can't find information on document.

For example:

  1. Create class A
    @HiveType(typeId: 1)
    class A {}
  2. Delete class A, create class B and use the same typeId
    @HiveType(typeId: 1)
    class B {}
themisir commented 3 years ago

Can typeId reused? I can't find information on document.

For example:

  1. Create class A
@HiveType(typeId: 1)
class A {}
  1. Delete class A, create class B and use the same typeId
@HiveType(typeId: 1)
class B {}

If you try to read from a box that previously written data with class A, the openBox operation will fail because it'll try to read data written using serializer (adapter) for class A but now tries to read with adapter created for class B. If both classes doesn't contains same fields in same order it'll fail.

jack24254029 commented 3 years ago

If you try to read from a box that previously written data with class A, the openBox operation will fail because it'll try to read data written using serializer (adapter) for class A but now tries to read with adapter created for class B. If both classes doesn't contains same fields in same order it'll fail.

After delete class A, I'll never need to read/written data with class A and never register adapter created for class A, is class B still fail? If the answers is YES, from my understand, HiveType#typeId's rules are same with HiveField#id. Did I get that right?

themisir commented 3 years ago

If the answers is YES, from my understand, HiveType#typeId's rules are same with HiveField#id. Did I get that right?

For strict reasons we usually say the same rules applies for both. But if you don't open the box that contains data written by class A adapter, it should work fine. (Also consider that case for your exists user base).

jack24254029 commented 3 years ago

For strict reasons we usually say the same rules applies for both. But if you don't open the box that contains data written by class A adapter, it should work fine. (Also consider that case for your exists user base).

Thank you !

UsamaKarim commented 2 years ago

How do you get rid of the error of uninitialized variable?

Example

@HiveField(3, defaultValue: false)
 final bool isRead;

Error

The final variable 'isRead' must be initialized.
Try initializing the variable.
themisir commented 2 years ago

How do you get rid of the error of uninitialized variable?

It looks like the error is from dart analyzer. You have to initialize final members of classes. In your case you probably have to initialize it from class constructor.

class Example {
  final bool isRead;

  Example({ required this.isRead }); // required because `isRead` is not nullable.
}