I've implemented database in realm on Android an iOS. I'm hardly sure that they are the same but if I'm replacing file from iOS and getting Realm instance using this file I get RealmMigrationNeededException. Is there any way to compare schemas in realm files from Android and iOS? If I'm doing the same action with file from another Android device it works.
Edit:
After adding empty Migration:
public class Migration implements RealmMigration {
#Override
public long execute(Realm realm, long version) {
return version;
}
}
I get: "Primary key not defined for field 'id' in existing Realm file. Add #PrimaryKey." Both platforms have 'id' field implemented as primary key.
There doesn't exit a tool yet that can output the entire schema unfortunately. You can use our Realm Browser for IOS, which will show you some of it, but if I remember correctly it will not show you e.g which fields are indexed: https://itunes.apple.com/us/app/realm-browser/id1007457278?mt=12
The migration exception should give you some idea what is wrong though?
Related
I am using realm in our iOS and Android app. For some reason i want to rename one of my realm object.
Initially we name the object Demo and now I want to change it to RealmDemo
In android we achieved it by using #RealmClass annotation
#RealmClass(name = "Demo")
open class RealmDemo : RealmObject() {
}
On iOS side i am not sure how exactly i can do similar as i did in android.
class RealmDemo: Object {
override static func className() -> String {
"Demo"
}
}
I tried above ^ but getting following error "Terminating app due to uncaught exception 'RLMException', reason: 'Object type 'Demo' not managed by the Realm'"
Two things.
First, You can name an object anything you want and change its name at any time.
HOWEVER, that's a destructive change, and Realm doesn't have any way to know the the newly named object 'is the same object' as the prior object.
How that's handled depends on what the use case is:
If this is a development situation, delete your local Realm files and run the app and the object with the new name will be created automatically.
If this is production then a migration block is needed (as on any platform) to migrate the data from the old object to the new one.
Secondly, The other important thing is the name of the object is now RealmDemo, whereas the prior object is Demo
class RealmDemo: Object {
so technically those are two separate objects. To Realm, you've abandoned the Demo object totally and that's a destructive change. Demo is still hanging around but is not referenced in your code so an error is thrown
On a possibly unrelated note, the className function references Demo
override static func className() -> String {
"Demo"
}
But the object name is RealmDemo.
It's not clear why the className function exists but it's not required or really needed. See the documentation for objects to get a feel for their structure - they may need a Primary Key
Seems like realm does not support className overriding for cocoa/ios.
https://github.com/realm/realm-cocoa/issues/2194
https://github.com/realm/realm-cocoa/issues/6624
I've been struggling performing a simple migration. What I just want to achieve is add a new Class in realm.
The code below is inside a method that is called inside onCreate.
Realm.init(this)
val config = RealmConfiguration.Builder()
.name("db_name")
.schemaVersion(5L)
.migration { realm, oldVersion, newVersion ->
val schema = realm.schema
var _oldVersion = oldVersion
if (_oldVersion == 4L) {
if (schema.contains(XModel::class.java.simpleName))
schema.remove(XModel::class.java.simpleName)
if (!schema.contains(XModel::class.java.simpleName))
schema.create(XModel::class.java.simpleName)
.addField(XModel::id.name, Long::class.javaPrimitiveType,
FieldAttribute.PRIMARY_KEY)
...
.addField(XModel::N.name, Int::class.javaPrimitiveType)
_oldVersion += 1
}
}
.build()
Realm.setDefaultConfiguration(config)
As what the title suggest, the new class in the schema was created inside the migration object, but when I try to access it in other parts of the application using a realm query or a simple call to schema.get("XModel") it will throw an error XModel doesn't exist in current schema. Any comment will really help. Thank you...
Edit:
Additional information. I have 2 realm objects, each are in different android modules, one module is dependent to the other. I somehow have some progress, now Im a bit confuse, do I need to declare 2 configurations? Then it would mean 2 realm instance? How to switch from both, I want to merge them into 1 realm.
Edit2:
Another clarification about realm. If you have 2 android modules, each of them using realm, will they have different realm even if in the realm configuration they have the same name?
Background
I want to give you a background of what im doing because I think its needed to fully understand my case.
Originally I only have one module, but then after refactoring and also because of future apps to be develop, I need to pull out the common classes from the existing module and put it in a separate lower-level module that the future apps can depend on. This new lower-level module will also be responsible for most of the data layer, so realm was transferred to this module. But I can't just ignore the realm of the existing app because some users might already populated it, and I need to transfer those data to the new database.
I am using Room Persistence Library 1.1.0. I could find the database file at /data/data/<package_name>/databases/ using Android Studio's Device File Explorer.
It contains multiple tables and I can access contents of that tables without any problem using room-DAOs. However when opening with sqlite-browser, is shows no table.
What might be the reason? Is it possible to resolve the issue without switching back to old SQLiteOpenHelper from room?
Solution
To open such databases* with sqlite-browser, you need to copy all three files. All must be in the same directory.
* Databases stored in multiple files as stated in the question.
Why three files?
As per docs, Starting from version 1.1.0, Room uses write-ahead logging as default journal mode for devices which has sufficient RAM and running on API Level 16 or higher. It was Truncate for all devices until this version. write-ahead logging has different internal structure compared to Truncate.
Take a look at the files temporary files used by SQLite now and then :
Until version 1.1.0
From version 1.1.0
If you want to change the journal mode explicitly to Truncate, you can do it this way. But, it is not recommended because WAL is much better compared to Truncate.
public static void initialize(Context context) {
sAppDatabase = Room.databaseBuilder(
context,
AppDatabase.class,
DATABASE_NAME)
.setJournalMode(JournalMode.TRUNCATE).build();
}
Is it possible to move it to single file without changing to Truncate ?
Yes, it is. Query the following statement against the database.
pragma wal_checkpoint(full)
It is discussed in detail here here.
Copy all three files from Device File Explorer in AndroidStudio to your PC directory and open the db file in Db Browser for SQLite (http://sqlitebrowser.org). Make sure all three files are in the same folder.
You can use the wal_checkpoint pragma to trigger a checkpoint which will move the WAL file transactions back into the database.
theRoomDb.query("pragma wal_checkpoint(full)", null)
or
// the result
// contains 1 row with 3 columns
// busy, log, checkpointed
Cursor cursor = theRoomDb.query("pragma wal_checkpoint(full)", null)
See PRAGMA Statements for more details about the pragma parameter values and results.
If the WAL is not enabled the pragma does nothing.
By the way, I tested with Room 1.1.1, and the WAL mode was not used by default, I had to enable it.
Room database Export and Import Solution
Im facing same problem in one of my project, i spend two days to resolve this issue.
Solution
Don't create multiple instance for Room library. Multiple instance creating all the problems.
MyApplication
class MyApplication: Application()
{
companion object {
lateinit var mInstanceDB: AppDatabase
}
override fun onCreate() {
super.onCreate()
mInstanceDB = AppDatabase.getInstance(this)
}
}
AppDatabase
fun getInstance(context: Context): AppDatabase
{
if (sInstance == null) {
sInstance = Room.databaseBuilder(context.applicationContext,AppDatabase::class.java, "database").allowMainThreadQueries().build()
return sInstance!!
}
}
Now use this instance in any number of activity or fragment just like that
{
var allcustomer = MyApplication.mInstanceDB.customerDao.getAll()
}
Export and Import use this library
implementation 'com.ajts.androidmads.sqliteimpex:library:1.0.0'
Github link
This question already has answers here:
"realm migration needed", exception in android while retrieving values from realm db
(5 answers)
Closed 5 years ago.
Whenever I change the model like adding more fields, the app crash with io.realm.exceptions.RealmMigrationNeededException error. This can only be resolved when I uninstalled and reinstalled the app.
Any suggestion to do migration? I am using only the default instance.
If you don't have any problem in loosing your old data then you can delete Realm Configuration and create new one.
Realm realm = null;
try {
realm = Realm.getInstance(MainActivity.this);
} catch (RealmMigrationNeededException r) {
Realm.deleteRealmFile(MainActivity.this);
realm = Realm.getInstance(MainActivity.this);
}
OR
RealmConfiguration config2 = new RealmConfiguration.Builder(this)
.name("default2")
.schemaVersion(3)
.deleteRealmIfMigrationNeeded()
.build();
realm = Realm.getInstance(config2);
you have to do Migration if you don't want to loose your data please see this example here.
You should be able to find the information you need here:
https://realm.io/docs/java/latest/#migrations
Just changing your code to the new definition will work fine, if you
have no data stored on disk under the old database schema. But if you
do, there will be a mismatch between what Realm sees defined in code &
the data Realm sees on disk, so an exception will be thrown.
Realm migrations in 0.84.2 are changed quite a bit, the key points on making a realm (0.84.2) migration work for me were understanding that:
The schemaVersion is always 0 when your app has a realm db without
specifying the schemaVersion. Which is true in most cases since you
probably start using the schemaVersion in the configuration once you
need migrations & are already running a live release of your app.
The schemaVersion is automatically stored and when a fresh install of your app occurs and you are already on schemaVersion 3, realm
automatically checks if there are exceptions, if not it sets the
schemaVersion to 3 so your migrations aren't run when not needed.
This also meens you don't have to store anything anymore in
SharedPreferences.
In the migration you have to set all values of new columns when the type is not nullable, ...
Empty Strings can be inserted but only when setting convertColumnToNullable on the column
I'm doing my first Realm migration and started thinking about the version number. On what is this version number based?
Because if it is based on what is on your phone, how do I handle it if a new person installs the app and gets a migration? Because it will also update the fields which where already set because of a fresh install.
Christian from Realm here. The migration API is still in a very experimental state and kinda ugly, so right now the version number always start with 0, and the only way for changing that is through a migration.
This means that if you want a fresh install with a different version other than 0, you will have to do something like:
// Pseudo code
public class RealmHelper() {
private static SharedPreferences prefs;
public static Realm getInstance() {
if (!prefs.getBoolean("versionSet", false)) {
String path = new File(context.getFilesDir(), Realm.DEFAULT_REALM_NAME).getAbsolutePath();
Realm.migrateRealmAtPath(path, new RealmMigration() {
#Override
public long execute(Realm realm, long version) {
return 42; // Set version numbers
}
})
prefs.edit().putBoolean("versionSet", true).apply();
}
return Realm.getInstance();
}
}
This is going to be a lot better soon though: https://github.com/realm/realm-java/pull/929
Realm migrations in 0.84.2 are changed quite a bit (see Christian's hint on the new API), the key points on making a realm (0.84.2) migration work for me were understanding that:
The schemaVersion is always 0 when your app has a realm db without
specifying the schemaVersion. Which is true in most cases since you
probably start using the schemaVersion in the configuration once you
need migrations & are already running a live release of your app.
The schemaVersion is automatically stored and when a fresh install of your app occurs and you are already on schemaVersion 3, realm
automatically checks if there are exceptions, if not it sets the
schemaVersion to 3 so your migrations aren't run when not needed.
This also meens you don't have to store anything anymore in
SharedPreferences.
In the migration you have to set all values of new columns when the type is not nullable, current version of realm, ...
Empty Strings can be inserted but only when setting convertColumnToNullable on the column