Lỗi khi thay đổi trong Realm - Android
Như các bạn đã biết thị hiện nay Realm được sử dụng khá rộng rãi do những ưu điểm vượt bậc so với các hệ quản trị CSDL khác. Tuy nhiên Realm không phải là toàn năng và chắc chắn nó vẫn có nhiều lỗi mà nếu người dùng không cẩn thận sẽ mắc phải. Một trong số đó là lỗi khi bạn thay đổi trong ...
Như các bạn đã biết thị hiện nay Realm được sử dụng khá rộng rãi do những ưu điểm vượt bậc so với các hệ quản trị CSDL khác.
Tuy nhiên Realm không phải là toàn năng và chắc chắn nó vẫn có nhiều lỗi mà nếu người dùng không cẩn thận sẽ mắc phải.
Một trong số đó là lỗi khi bạn thay đổi trong Database. (Thêm sửa xóa 1 trường hay 1 bảng bất kỳ). Lỗi này khiến cho Realm họat động không còn chính xác nữa và nó thậm chí còn khiến cho app của bạn có thể bị crash.
time: FATAL EXCEPTION: main Process: com.example.framgia.imarketandroid, PID: 18100 io.realm.exceptions.RealmMigrationNeededException: RealmMigration must be provided at io.realm.BaseRealm.migrateRealm(BaseRealm.java:717) at io.realm.Realm.migrateRealm(Realm.java:1351) at io.realm.Realm.migrateRealm(Realm.java:1338) at io.realm.Realm.createInstance(Realm.java:221) at io.realm.RealmCache.createRealmOrGetFromCache(RealmCache.java:126) at io.realm.Realm.getInstance(Realm.java:178)
Và ngày hôm nay mình sẽ chia sẻ về cách khắc phục nhược điểm này của Realm
Nếu bạn nào hay sử dụng SQLite thì chắc chắn đã quen với việc phải tăng version database mỗi khi bạn có thay đổi trong database đúng không nào. Thì đối với Realm cung vậy. Lỗi này nguyên nhận chính là do bạn đã thay đổi database mà chưa cập nhập version cho nó.
Realm hỗ trợ chúng ta 3 phương pháp một khi bạn cập nhập database. Rất hưu ích và vô cùng đơn giản. Mình sẽ trình bày cả 3 cách và các bạn có thể chọn cách nào phù hợp với dự ạn của các bạn cũng được.
Cách 1: Xác định rõ các phiên bản khi tạo 1 RealmConfiguration
// If the schema does not have that version a RealmMigrationNeededException will be thrown. RealmConfiguration config0 = new RealmConfiguration.Builder() .name("default0.realm") .schemaVersion(3) .build(); // Bạn có thể gọi Realm.migrateRealm(). và đặt trong try/catch để bắt trường hợp ngoại lệ try { Realm.migrateRealm(config0, new Migration()); } catch (FileNotFoundException ignored) { // If the Realm file doesn't exist, just ignore. } realm = Realm.getInstance(config0); showStatus("Default0"); showStatus(realm); realm.close();
Cách 2: Bạn có thể sử dụng phương thức .deleteRealmIfMigrationNeeded() nếu bạn không quan tâm đến migrations. Tuy nhiên điều này sẽ xóa tất cả dữ liệu trong Realm.
RealmConfiguration config2 = new RealmConfiguration.Builder() .name("default2.realm") .schemaVersion(3) .deleteRealmIfMigrationNeeded() .build();
**Cách 3 **: Bạn sử dụng tính năng mà Realm đã hỗ trợ rất mạnh .
Khi sử dụng Realm chúng ta phải kế thừa lớp RealmMigration để thuận tiện cho việc cập nhật CSDL, thêm/bớt các bảng cũng như các trường.
Giả sử ta có 1 lớp Migration extend RealmMigration như sau :
public class Migration implements RealmMigration { // TODO something }
Các trường hợp thay đổi trong Realm mà cần phải cập nhập trong Migration
- Thay đổi các trường trong 1 bảng
Ví dụ :
Old_Version :
class Person @Required String firstName; @Required String lastName; int age;
New_Version:
class Person @Required String fullName; // combine firstName and lastName into single field. int age;
Vậy bạn cần viết đoạn mã sau để cập nhật version cho nó . Giả sử lúc này version lúc này là 0.
if (oldVersion == 0) { RealmObjectSchema personSchema = schema.get("Person"); // Combine 'firstName' and 'lastName' in a new field called 'fullName' personSchema .addField("fullName", String.class, FieldAttribute.REQUIRED) .transform(new RealmObjectSchema.Function() { @Override public void apply(DynamicRealmObject obj) { obj.set("fullName", obj.getString("firstName") + " " + obj.getString("lastName")); } }) .removeField("firstName") .removeField("lastName"); oldVersion++; }
Trước tiên bạn phải addField mới, sau đó thì removeField cũ đi. Nhớ tăng version .
- Add a new model class
class Pet // add a new model class @Required String name; @Required String type; >>>>>>>>>>>>>>>>>>>>> class Person @Required String fullName; int age; RealmList<Pet> pets; // add an array property
Giả sử đây là lần thay đổi thứ 2 thì old_version sẽ là 1.
if (oldVersion == 1) { // Create a new class RealmObjectSchema petSchema = schema.create("Pet") .addField("name", String.class, FieldAttribute.REQUIRED) .addField("type", String.class, FieldAttribute.REQUIRED); // Add a new field to an old class and populate it with initial data schema.get("Person") .addRealmListField("pets", petSchema) .transform(new RealmObjectSchema.Function() { @Override public void apply(DynamicRealmObject obj) { if (obj.getString("fullName").equals("JP McDonald")) { DynamicRealmObject pet = realm.createObject("Pet"); pet.setString("name", "Jimbo"); pet.setString("type", "dog"); obj.getList("pets").add(pet); } } }); oldVersion++; }
- Trường hợp cuối cùng
class Pet @Required String name; int type; // type becomes int >>>>>>>>>>>>>>>>>>>>> class Person String fullName; // fullName is nullable now RealmList<Pet> pets; // age and pets re-ordered (no action needed) int age;
Tiếp tục tưởng tượng thì bây giờ old_version sẽ là 2
if (oldVersion == 2) { RealmObjectSchema personSchema = schema.get("Person"); personSchema.setNullable("fullName", true); // fullName is nullable now. // Change type from String to int schema.get("Pet") .addField("type_tmp", int.class) .transform(new RealmObjectSchema.Function() { @Override public void apply(DynamicRealmObject obj) { String oldType = obj.getString("type"); if (oldType.equals("dog")) { obj.setLong("type_tmp", 1); } else if (oldType.equals("cat")) { obj.setInt("type_tmp", 2); } else if (oldType.equals("hamster")) { obj.setInt("type_tmp", 3); } } }) .removeField("type") .renameField("type_tmp", "type"); oldVersion++; }
Ngoài ra các bạn có thể tham khảo chi tiết về Realm tại đây
Chúc các bạn một ngày làm việc vui vẻ !