Fairly new to room and having a hard time finding info on this. Currently we define our database like this:
#Database(entities = {TwcLocation.class,
CurrentObservation.class,
Day.class,
Hour.class,
Station.class,
StationCurrentObservation.class}, version = 1, exportSchema = false)
public abstract class TwcLocationDatabase extends RoomDatabase {
Now, we want to remove a bunch of unused entities so it looks like this:
#Database(entities = {TwcLocation.class, Tag.class}, version = 1)
#TypeConverters({TwcLocationTypeConverter.class})
public abstract class NbcRoomDatabase extends RoomDatabase {
Question: How do I do this migration?
You need to increment the version (so it will become version = 2).
Next, when building the Room instance by using the Room.databaseBuilder method, add the addMigration() line.
As migration, pass it the following to remove the tables:
// Migration from version 1 to 2
static final Migration MIGRATION_1_2 = new Migration(1, 2) {
#Override
public void migrate(SupportSQLiteDatabase database) {
// Remove the table
database.execSQL("DROP TABLE day"); // This line for each table that you want to remove
}
};
Later on you might need multiple different migrations, you can do so by using the addMigrations() method:
Room.databaseBuilder(..., ..., ...)
.addMigrations(MIGRATION_1_2, MIGRATION_2_3)
.build()
Other option is to use fallbackToDestructiveMigration and increment database version. In such case you will not have to provide migrations. However it will clear data in all old tables.
Related
This question already has answers here:
Polymorphic entities in Room
(2 answers)
Closed 1 year ago.
TL;DR: How can I store and retrieve objects of type ClassA and ClassB which both inherit from ClassP in the same "list", using the Room Persistence Library?
In other words, How should I store a List<? extends BaseAnimal> in my Room DB?
I have Animals, for the purpose of this question.
public abstract class BaseAnimal {
#PrimaryKey(autoGenerate = true)
private long id;
public BaseAnimal(long id){this.id = id;}
public abstract void func(int param);
public long getId() {
return id;
}
}
An animal has an id and implements some function func. There will likely be many kinds of animals. To start with, I have an Elephant which additionally has a trunkLength property, and a Giraffe which additionally has a neckLength property.
public class Giraffe extends BaseAnimal {
public long neckLength;
public Giraffe(long id) {
super(id);
}
#Override
public void func(int param) {
}
}
I have several instances of Elephant and Giraffe in my application. How can I, using the Android Room Persistence Library, store and retrieve them sorted by BaseAnimal.id?
Expectations
At first, I hoped that it would be as simple as this:
Annotate the BaseAnimal class with #Entity(tableName = "base_animal_table")
Annotate my extension of the RoomDatabase with
#Database(entities = {BaseAnimal.class }, version = 1)
Add a function for inserting to the Data Access Object
#Insert
void insertAnimal(BaseAnimal animal);
Add a method to the Repository
public void insertAnimal(BaseAnimal animal){
new insertAnimalAsyncTask(recipeDAO).execute(animal);
}
private static class insertAnimalAsyncTask extends AsyncTask<BaseAnimal, Void, Void> {
private RecipeDAO mAsyncTaskDao;
insertAnimalAsyncTask(RecipeDAO dao) {
mAsyncTaskDao = dao;
}
#Override
protected Void doInBackground(final BaseAnimal... params) {
mAsyncTaskDao.insertAnimal(params[0]);
return null;
}
}
I expected that this would lead to room generating a table named base_animal_table which would look somewhat like this (with some sample data):
| id | ElephantTrunkLength | GiraffeNeckLength |
| 0 | NULL | 12 |
| 12 | 1337 | NULL |
I also expected that I could retrieve the data from this table something like this:
// this is in the DAO
#Query("SELECT * from `base_animal_table` ORDER BY id ASC")
LiveData<List<BaseAnimal>> getAllAnimals();
and then get a list that contains both, entities of type Elephant with their trunkLength property set, and entities of type Giraffe with their neckLength set.
This seems not to be straightforward. How can I implement this?
One approach I see is the following, but I am not sure whether it is the best way. And I am also starting to doubt that Room makes this any easier than using plain SQLite. I tried to produce a working example for this approach with room, but there are still some unresolved issues.
One Approach
Create a table base_animals which only contains the id and other base animal attributes, along with an indicator of their child type:
// base_animals
| id | animalType |
| 0 | "GIRAFFE" |
| 12 | "ELEPHANT" |
Use this table to keep a list of all animals and their ids. Create a table elephants and a table giraffes with the id and their specific properties (trunkLength and neckLength, respectively).
Then we can store an animal in the DB by creating an entry in both the base table and the child table.
To retrieve a specific animal by id, we can now find the animalType in the base_animals table and with that decide in which child table - elephants or giraffes we need to search.
The problem I have with this approach is that it requires that I have to write quite some code and I have to update it whenever I create a new animal entity - e.g. a Dolphin.
So, my question is: How should I store a List<? extends BaseAnimal> in my Room DB?
I would prefer it if I could leave BaseAnimal an abstract class.
I'm not sure that using inheritance is a good idea here.
I would suggest using composition instead.
Keep a class named Animal (not BaseAnimal).
The animal should have a type just like you suggested.
Another table (AnimalProperties) would hold the properties of each animal.
It will have a foreign key to the Animals table and a property name and property value.
Now...
If this is all that you need, meaning a class with type and properties then you're all set.
But if you also need the class to behave differently like implementing a fly() method for a bird and run() a dog then you should consider using Composition to add behaviors to the Animal.
If you are not familiar with Composition have a look here for a simple explanation.
I have an app which makes a database by Room. Then I move the database to the server to populated. While updating or initializing the app, the populated database is downloaded by the app. But when I want to use it by Room I get an error message:
A migration from 2 to 1 is necessary. Please provide a Migration in
the builder or call fallbackToDestructiveMigration in the builder in
which case Room will re-create all of the tables.
I cleaned whole build folder then I did all again. But I get the same error again!
Where I get error is:
String SALE_DATABASE_NAME = "SaleDatabase.db";
SaleDatabase saleDatabase = Room.databaseBuilder(this,
SaleDatabase.class, SALE_DATABASE_NAME)
//.fallbackToDestructiveMigration()
.allowMainThreadQueries()
.build();
saleDatabase.getPathDao().getPaths(); //Getting error
Database class:
#Database(entities = {OrderEntity.class, OrderDetailEntity.class
, CardIndexDetailEntity.class, CardIndexEntity.class
, CategoryEntity.class, CodingEntity.class
, CustomerBasicEntity.class, CustomerBuyEntity.class
, CustomerChequeEntity.class, CustomerCreditEntity.class
, PathEntity.class, UnvisitedCustomerReasonEntity.class
, ProfileCategoryEntity.class, SubCategoryDetailEntity.class
, SubCategoryEntity.class, ReasonEntity.class}, version = 1)
public abstract class SaleDatabase extends RoomDatabase{
public abstract PathDao getPathDao();
#Override
protected SupportSQLiteOpenHelper createOpenHelper(DatabaseConfiguration config) {
return null;
}
#Override
protected InvalidationTracker createInvalidationTracker() {
return null;
}
}
My hash code in both database and SaleDatabase_Impl.java class is same.
So my questions are:
Why does it say migration 2 to 1 as a downgraded job?
How can I ignore version until application built completely
Is there a way to clean and reset room if I update my version to 2?
I solved this way:
Clear app data
In the manifest set android:allowBackup="false"
So basically i am using room and trying to add migration from database version 1 to 2 but my alter command is not working
My current implementation is below :
void init() {
db = Room.databaseBuilder(Global.getInstance(),
AppDatabase.class, "feed").addMigrations(MIGRATION_1_2).build();
}
Migration property :
static final Migration MIGRATION_1_2 = new Migration(1,2) {
#Override
public void migrate(SupportSQLiteDatabase database) {
database.execSQL("ALTER TABLE 'post' ADD COLUMN 'age' INTEGER NOT NULL DEFAULT 0");
Log.d("VROM","Migration");
}
};
Database implementation :
#Database(entities = {Feed.class, DownloadModel.class}, version = 1) public abstract class AppDatabase extends RoomDatabase {
public abstract DaoAccess getFeedDao();}
So after incrementing the version from 1 to 2, the execSQL() is executed but new column is not added in my db.
I have pulled my db from app directory and checked multiple times but column is not there. Apart from that if I kill my app and launch it again the migrate method is called again , don't know if this is the intended functionality but it breaks the functionality for me.I thought migrate will be only called once same as onUpgrade()
Make sure your column is in model class. In your case, you are adding column age like this: ADD COLUMN 'age' INTEGER, so you must have int age in your model class.
Also, it is a good idea to write migration test to known exactly what is failing. You can find about migration test in android documentation here: https://developer.android.com/topic/libraries/architecture/room.html#db-migration-testing
Try Changing version=2 in AppDatabase inside #Database.
I have a list view in android where i have to check every time do display the List item or not
to reduce the requests what i did is saved the id in a single row like
1,2,10,
everything was working fine to search i just had to use
String[] favs = fav.split(",");
for (int index = 0; index <(favs.length); index++) {
if(favs[index]==""){}else {
wishlist.add(Integer.parseInt(favs[index].trim()));
}
if(clicklist.contains((int)temp.getId())) //like this
and to remove from db like, this
temp2.replaceAll(""+m1.getId()+",", "") // and save in the db
now issue is i have two more data field associated with id like
10|data1|data2,100|apple|dog,150|data12|data24
Question 1 is this data model ok for small db
Question 2 how to perform search and delete in new data set?
please help!
Using a db is a proper choice here, i suggest you to take a look to the recently released Room, an Android component made by Google developers to support data persistence more easily.
You should of course know the basis of sql language.
In your case you should annotate your data class with #Entity annotation:
#Entity
public class DataModel {
#PrimaryKey
private int uid;
#ColumnInfo(name = "animal")
private String animal;
#ColumnInfo(name = "fruit")
private String fruit;
// Getters and setters are ignored for brevity,
// but they're required for Room to work.
}
And then, to answer your question about CRUD operations, define a Dao:
#Dao
public interface UserDao {
#Query("SELECT * FROM DataModel")
List<DataModel> getAll();
#Insert
void insertAll(DataModel... dataModels);
#Delete
void delete(DataModel dataModel);
}
I need some help with Realm.io, I've just been presented to it in a project I'm joining. The former developer showed the source code of our app recently and told me he is using Realm.io just to check if it's the first time the app has been opened by the user. Here is a code snippet of what he is using to do that on the onCreate() method. I'm using Android Studio for development.
Realm.init(getApplicationContext());
final Realm realm = Realm.getDefaultInstance();
final RealmResults<Configuracao> configuracoes =
realm.where(Configuracao.class)
.equalTo("chave", "primeiroAcesso")
.findAll();
The problem is that now I need to insert new data on the database so I've created a class that looks like this:
public class medicine extends RealmObject {
#PrimaryKey
private int id;
private String med;
private String doctor;
/* Setters and getters here */
}
I'm running into the RealmMigrationNeeded exception, I read the docs and I'm aware that I need to do the migration.
My question is: Where exactly do I put the migration code? Should I put it in the new class file?
Also, in the documentation they tell me that I need to change the version of the schema through something like this:
RealmConfiguration config1 = new RealmConfiguration.Builder()
.name("default1.realm")
.schemaVersion(3)
.migration(new Migration())
.build();
But they also say that if that version doesn't exist an exception will be thrown so I'm guessing I need to change the schema before doing that?
Do I have to change anything in the database itself and then call the migration inside the app, or the migration is the process to change the schema? Sorry about the long text but I'm really confused. Thanks for the help in advance.
Yes, you need to create a custom Migration class that must implement RealmMigration. The schema version you provide in the configuration is the version your Realm will have after the migration is run. You can see an example here: https://github.com/realm/realm-java/blob/master/examples/migrationExample/src/main/java/io/realm/examples/realmmigrationexample/model/Migration.java
In your case it would look something like this:
public class MyMigration implements RealmMigration {
#Override
public void migrate(final DynamicRealm realm, long oldVersion, long newVersion) {
RealmSchema schema = realm.getSchema();
if (oldVersion == 2) {
schema.create("medicine")
.addField("id", int.class, FieldAttribute.PRIMARY_KEY)
.addField("med", String.class)
.addField("doctor", String.class);
}
}
}