In the app there is a realm database that has both initial data and data added by the user. The data added by the user should stay on the device even after the updates. The initial data is added through the class
public class RealmInitialData implements Realm.Transaction {
#Override
public void execute(Realm realm){
//initial data
Items item = new Items();
item.setName("Fragrance");
item.setTimestamp(System.currentTimeMillis());
realm.insertOrUpdate(item);
}
#Override
public int hashCode() {
return RealmInitialData.class.hashCode();
}
#Override
public boolean equals(Object obj) {
return obj != null && obj instanceof RealmInitialData;
}
}
The configuration (situated in a class which extends Application class) looks like this:
RealmConfiguration realmConfig = new RealmConfiguration.Builder()
.name("tasky.realm")
.schemaVersion(2)
.migration(new Migration())
.initialData(new RealmInitialData())
.build();
If I change/add something in the RealmInitialData for update, the data on the user application doesn't change. How can I both change the initial data (add or rewrite sth) and make no changes to the user's data?
You have to implement RealmMigration and pass it to RealmConfiguration.Builder().migration().
Something like this:
public class RealmMigrationAgent implements RealmMigration
{
#Override
public void migrate(DynamicRealm realm, long oldVersion, long newVersion) {
RealmSchema schema = realm.getSchema();
if (oldVersion == 0) {
schema.get("InventDisableObject").addField("Reason", int.class);
oldVersion++;
}
if (oldVersion == 1) {
if (!schema.get("ActionObject").hasField("Annual"))
schema.get("ActionObject").addField("Annual", boolean.class);
oldVersion++;
}
if (oldVersion == 2) {
if (!schema.get("ActionObject").hasField("Hash"))
schema.get("ActionObject").addField("Hash", String.class);
if (!schema.get("ActionObject").hasField("Manual"))
schema.get("ActionObject").addField("Manual", boolean.class);
oldVersion++;
}
if (oldVersion == 3) {
if (!schema.get("OutletObject").hasField("Code"))
schema.get("OutletObject").addField("Code", String.class);
oldVersion++;
}
}
}
Incrementally adding scheme updates for every new scheme version.
I can see you have some class Migration for migration reason - can you provide the code of it?
I have the same problem and i find the way.For example,if your new version is 7,try to use SharedRealm.getInstance(config).getSchemaVersion() to get the old version 6 before Realm.setDefaultConfiguration(config),then you can update old realm data after setDefaultConfiguration which old version < 7.May this can help you:
RealmConfiguration config = new RealmConfiguration.Builder()
.schemaVersion(7)
.migration(new RealmMigration() {
#Override
public void migrate(DynamicRealm realm, long oldVersion, long newVersion) {
RealmSchema schema = realm.getSchema();
if(oldVersion == 6) {
schema.get("ImageRealm")
.addField("isSelected", Boolean.class, FieldAttribute.REQUIRED)
.addField("isLow", Boolean.class, FieldAttribute.REQUIRED);
oldVersion++;
}
}
}).build();
SharedRealm sharedRealm = SharedRealm.getInstance(config);
long oldVersion = sharedRealm.getSchemaVersion();
Realm.setDefaultConfiguration(config);
long newVersion = Realm.getDefaultInstance().getVersion();
// 如果有历史记录,更新状态
if (oldVersion < 7) {
Realm.getDefaultInstance().executeTransaction(new Realm.Transaction() {
#Override
public void execute(Realm realm) {
RealmResults<DocRealmBean> allData = realm.where(DocRealmBean.class).findAll();
for (DocRealmBean bean : allData) {
bean.state = "0";
}
}
});
}
Related
I have a small migration class and I wan't to create the SportTypes table with some predefined rows when the app starts . I tried the commented solutions under the migrate method but no chance.
public class SportTypes extends RealmObject {
#PrimaryKey
private Integer id;
private String name;
private RealmList<SportTypes> sportTypes;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public RealmList<SportTypes> getSportTypes() {
return sportTypes;
}
public void setSportTypes(RealmList<SportTypes> sportTypes) {
this.sportTypes = sportTypes;
}
}
public class RealmMigrations implements RealmMigration {
#Override
public void migrate(DynamicRealm realm, long oldVersion, long newVersion) {
RealmSchema schema = realm.getSchema();
schema.create("SportTypes")
.addField("id", Integer.class, FieldAttribute.PRIMARY_KEY)
.addField("name", String.class);
// schema.get("SportTypes")
// .transform(new RealmObjectSchema.Function() {
// #Override
// public void apply(DynamicRealmObject obj) {
// Realm realm = Realm.getDefaultInstance();
// realm.beginTransaction();
// SportTypes sport = realm.createObject(SportTypes.class);
// sport.setId(0);
// sport.setName("cycling");
// sport.getSportTypes().add(sport);
// realm.commitTransaction();
// realm.close();
// }
// });
// schema.get("SportTypes").transform(new RealmObjectSchema.Function() {
// #Override
// public void apply(DynamicRealmObject obj) {
// obj.setInt("id", 0);
// obj.setInt("id", 1);
// obj.setInt("id", 2);
// obj.setInt("id", 3);
// obj.setInt("id", 4);
// obj.setString("name", "Running");
// obj.setString("name", "Cycling");
// obj.setString("name", "Swimming");
// obj.setString("name", "IndoorRunning");
// obj.setString("name", "IndoorCycling");
// }
// });
// Deliberately not using old version to run upper lines
if (oldVersion == 0) {
oldVersion++;
}
}
}
I call the below part in the onCreate method of the activity. I also manually changing the version to trigger the migrate event of the migration class but can't get any rows.
Realm.init(this);
final RealmConfiguration configuration = new RealmConfiguration.Builder().name("sports1761.realm").schemaVersion(1).migration(new RealmMigrations()).build();
Realm.setDefaultConfiguration(configuration);
// try {
// Realm.migrateRealm(configuration);
// } catch (FileNotFoundException e) {
// e.printStackTrace();
// }
Realm realm = Realm.getDefaultInstance();
RealmObjectSchema schema = realm.getSchema().get("SportTypes");
final RealmResults<SportTypes> sports = realm.where(SportTypes.class).findAll();
Integer size = sports.size();
I found the solution.You should get first the schema and create DynamicRealmObject and delete old records before adding new ones then just add the new records. If your object has a primary key you should create the object using field & primary key overload of the createObject method.
public class RealmMigrations implements RealmMigration {
#Override
public void migrate(DynamicRealm realm, long oldVersion, long newVersion) {
if (oldVersion == 0) {
RealmSchema schema = realm.getSchema();
RealmObjectSchema sportTypesSchema = schema.get("SportTypes");
realm.delete("SportTypes");
DynamicRealmObject sportType = realm.createObject("SportTypes", 0);
sportType.setString("name", "Running");
DynamicRealmObject sportType1 = realm.createObject("SportTypes", 1);
sportType.setString("name", "Cycling");
DynamicRealmObject sportType2 = realm.createObject("SportTypes", 2);
sportType.setString("name", "Swimming");
DynamicRealmObject sportType3 = realm.createObject("SportTypes", 3);
sportType.setString("name", "Indoor Running");
DynamicRealmObject sportType4 = realm.createObject("SportTypes", 4);
sportType.setString("name", "Indoor Cycling");
RealmList<DynamicRealmObject> listItems = sportType.getList("sportTypes");
List<DynamicRealmObject> values = Arrays.asList(sportType, sportType1, sportType2, sportType3, sportType4);
listItems.addAll(values);
oldVersion++;
}
}
}
Android Studio 3.2.
Realm 5.8.0
public class Merchant extends RealmObject {
#PrimaryKey
private long id;
private Image preview;
}
public class Image extends RealmObject {
#PrimaryKey
private long id;
}
I need to delete Merchants object with specific ids AND all it embedded objects (Image in my example).
So here code:
public static void updateMerchantList(final List<Merchant> thatMerchantsList) {
Realm realm = Realm.getDefaultInstance();
try {
realm.executeTransaction(new Realm.Transaction() {
#Override
public void execute(Realm realm) {
RealmList<Merchant> localMerchantList = getMerchantsRealmList();
if (!EqualsUtil.areEqualContentLists(localMerchantList, thatMerchantsList)) {
List<Merchant> itemNotExistInThatMerchants = new ArrayList<>(localMerchantList);
itemNotExistInThatMerchants.removeAll(thatMerchantsList);
if (itemNotExistInThatMerchants.size() > 0) {
localMerchantList.removeAll(itemNotExistInThatMerchants);
Long[] idsToDeleteArray = new Long[itemNotExistInThatMerchants.size()];
for (int index = 0; index < itemNotExistInThatMerchants.size(); index++) {
Merchant merchant = itemNotExistInThatMerchants.get(index);
idsToDeleteArray[index] = merchant.getId();
}
RealmResults<Merchant> localMerchantsForDelete = realm.where(Merchant.class).in(Merchant.ID, idsToDeleteArray).findAll();
boolean isDelete = localMerchantsForDelete.deleteAllFromRealm();
}
}
}
});
} finally {
realm.close();
}
}
public static RealmList<Merchant> getMerchantsRealmList() {
Realm realm = Realm.getDefaultInstance();
try {
RealmResults<Merchant> realmResults = realm.where(Merchant.class).findAll();
RealmList<Merchant> realmList = new RealmList<>();
realmList.addAll(realmResults.subList(0, realmResults.size()));
return realmList;
} finally {
realm.close();
}
}
As result 2 Merchant success delete from Realm (by method deleteAllFromRealm) .
Nice.
But all embedded objects (like Image) NOT delete from Realm.
Questions:
This is because I need to write custom method that cascade delete Merchant and all it embedded objects?
Is Realm can cascade delete objects?
I'm using Realms as a database in Android app. Works fine, but I've added a new label in my user model and I'm getting the error that I need to migrate my schema:
java.lang.RuntimeException: Unable to create application com.apelucy.apelucy.app.base.MyApplication: io.realm.exceptions.RealmMigrationNeededException: Migration is required due to the following errors:
- Property 'User.testRealm' has been added.
How can I do the migration? I've found other solutions here but I can't implement them in my code. I can't use a solution of delete and install the app. I now that work in development, but I need to update the app in production.
My UserRespository class:
public class UserRepository {
private static UserRepository sInstance = null;
private Context mContext = null;
public static UserRepository getInstance(Context context) {
if (sInstance == null) {
sInstance = new UserRepository();
sInstance.mContext = context;
}
return sInstance;
}
// DATABASE Methods
public void storeUser(final User user) {
AppSingleton.getInstance().setUser(user);
Realm realm = null;
try {
realm = Realm.getDefaultInstance();
realm.executeTransaction(realm1 -> realm1.insertOrUpdate(user));
} finally {
if (realm != null) {
realm.close();
}
}
}
public User retrieveUser() {
Realm realm = null;
User user = null;
try {
realm = Realm.getDefaultInstance();
User userRealmResult = realm.where(User.class)
.findFirst();
if (userRealmResult != null) {
user = realm.copyFromRealm(userRealmResult);
}
} finally {
if (realm != null) {
realm.close();
}
}
return user;
}
public void clearUser() {
// Clear Database objects
Realm realm = null;
try {
realm = Realm.getDefaultInstance();
realm.executeTransaction(realm1 -> realm1.delete(User.class));
} finally {
if (realm != null) {
realm.close();
}
}
}
}
Init realm in my Application:
Realm.init(this);
My model change:
#SerializedName("test")
#Expose
private String testRealm;
Migrations allow you to modify the schema of the application, which means that it lets you add, remove, rename tables/fields in the Realm schema. If you change a RealmModel class, then you must write the migration that will map the existing Realm file to reflect the new model classes.
RealmConfiguration config = new RealmConfiguration.Builder()
.schemaVersion(1)
.migration(new MyMigration())
.build();
Realm.setDefaultConfiguration(config);
The default schema version is 0.
Migrations are fairly straightforward:
you must increment the schema version, so Realm knows you want to increment the schema's version to a specific number
you must supply a migration that will handle the change from one version to another
Migrations describe the operations to do when you need to go from one schema version to another:
public class MyMigration implements RealmMigration {
#Override
public void migrate(final DynamicRealm realm, long oldVersion, long newVersion) {
RealmSchema schema = realm.getSchema();
// Migrate from version 0 to version 1
if (oldVersion == 0) {
RealmObjectSchema userSchema = schema.get("User");
userSchema.addField("testRealm", String.class);
oldVersion++;
}
if (oldVersion == 1) { // ...
// ...
}
}
#Override
public int hashCode() { return MyMigration.class.hashCode(); }
#Override
public boolean equals(Object object) { return object != null && object instanceof MyMigration; }
}
Add this in your Application file. This will Realm to delete everything if you add a new table to a column.
RealmConfiguration config = new RealmConfiguration.Builder().name("dbname.realm")
.deleteRealmIfMigrationNeeded()
.build();
Realm.setDefaultConfiguration(config);
How can I maintain a single Realm Instance throughout the complete lifecycle of the App and also close it.
I can achieve maintaining the instance using a singleton class, but then how do I close it when the app is closed?
Also, is it safe to not close Realm Instance once opened?
I tend to use a singleton RealmManager for the UI thread, and for background threads I open/close the Realm using a try-with-sources block.
So for UI thread:
public class RealmManager {
private static final String TAG = "RealmManager";
static Realm realm;
static RealmConfiguration realmConfiguration;
public static void initializeRealmConfig(Context appContext) {
if(realmConfiguration == null) {
Log.d(TAG, "Initializing Realm configuration.");
setRealmConfiguration(new RealmConfiguration.Builder(appContext).initialData(new RealmInitialData())
.deleteRealmIfMigrationNeeded()
.inMemory()
.build());
}
}
public static void setRealmConfiguration(RealmConfiguration realmConfiguration) {
RealmManager.realmConfiguration = realmConfiguration;
Realm.setDefaultConfiguration(realmConfiguration);
}
private static int activityCount = 0;
public static Realm getRealm() {
return realm;
}
public static void incrementCount() {
if(activityCount == 0) {
if(realm != null) {
if(!realm.isClosed()) {
Log.w(TAG, "Unexpected open Realm found.");
realm.close();
}
}
Log.d(TAG, "Incrementing Activity Count [0]: opening Realm.");
realm = Realm.getDefaultInstance();
}
activityCount++;
Log.d(TAG, "Increment: Count [" + activityCount + "]");
}
public static void decrementCount() {
activityCount--;
Log.d(TAG, "Decrement: Count [" + activityCount + "]");
if(activityCount <= 0) {
Log.d(TAG, "Decrementing Activity Count: closing Realm.");
activityCount = 0;
realm.close();
if(Realm.compactRealm(realmConfiguration)) {
Log.d(TAG, "Realm compacted successfully.");
}
realm = null;
}
}
}
And for background thread:
try(Realm realm = Realm.getDefaultInstance()) {
// ...
}
Why don't you create a wrapping class for your realm instance (may be a singleton) and then add a few methods to it, so that instead of closing realm each time you can just call your own method and close the used instance as soon as you're finished? Something like this.
public class WrappingRealm {
public static WrappingRealm getInstance() {
//create your singleton here. Be aware of synchronization issues
}
private Realm getRealm() {
return Realm.getDefaultInstance();
}
public void save(RealmModel obj) {
Realm currentRealm = getRealm();
currentRealm.executeTransaction {
//Do your stuff
}
currentRealm.close();
}
}
My last release app is using not encrypted realm.
Now, I want to update to use encrypted realm.
But I don't know how to migrate unencrypted data.
Help me please~ :(
Answer by myself.
I made util class to help migration. (unecncrypted file -> encrpyted file)
public class RealmEncryptionHelper {
private static final String ENCRYPTION_FILE_PREFIX = "encrypted_";
public static Realm createEncryptedRealm(Context context, RealmConfiguration.Builder builder) {
RealmConfiguration unencryptedConfig = builder.build();
RealmConfiguration encryptedConfig = builder.name(ENCRYPTION_FILE_PREFIX + unencryptedConfig.getRealmFileName())
.encryptionKey(AppSharedPreferences.getInstance(context).getRealmEncryptionKey())
.build();
migrationIfNeeded(unencryptedConfig, encryptedConfig);
return Realm.getInstance(encryptedConfig);
}
private static void migrationIfNeeded(RealmConfiguration unencryptedConfig, RealmConfiguration encryptedConfig) {
File unencryptedFile = new File(unencryptedConfig.getPath());
File encryptedFile = new File(encryptedConfig.getPath());
Realm unencryptedRealm = null;
if (!encryptedFile.exists() && unencryptedFile.exists()) {
try {
unencryptedRealm = Realm.getInstance(unencryptedConfig);
unencryptedRealm.writeEncryptedCopyTo(encryptedFile, encryptedConfig.getEncryptionKey());
} catch (IOException e) {
e.printStackTrace();
} finally {
if (unencryptedRealm != null) {
unencryptedRealm.close();
unencryptedFile.delete();
}
}
}
}
}
#Orlando
like this?
class EncryptionMigration implements RealmMigration {
#Override
public void migrate(DynamicRealm dynamicRealm, long oldVersion, long newVersion) {
byte[] encryptionKey = "flkajskdf............................".getBytes();
if (oldVersion == UNENCRYPT_VERSION) {
try {
dynamicRealm.writeEncryptedCopyTo(new File(dynamicRealm.getPath()), encryptionKey);
} catch (IOException e) {
e.printStackTrace();
}
}
}