Room Database Empty when Restarted App ( Same DB Name) - java

I am newbie to the Android X Room Database. Earlier I used Sqlitehelper class to access the sqlite databases and then migrated to room. The question is , when I insert data into Room database table it stores for that moment and when I restart the app the data is gone. I want to have multiple tables in a single database.
DAO :
#androidx.room.Dao
public interface ItineraryDao {
#Insert(onConflict = OnConflictStrategy.IGNORE)
void insert(ItineraryData model);
#Update
void update(ItineraryData model);
#Delete
void delete(ItineraryData model);
#Query("DELETE FROM "+ CONSTANTS.ITINERARY_TABLE)
void deleteAllItinerary();
#Query("SELECT * FROM "+CONSTANTS.ITINERARY_TABLE+" ORDER BY Date ASC")
LiveData<List<ItineraryData>> getAllItinerary();
Database:
#Database(entities = {ItineraryData.class},version = 1,exportSchema = false)
public abstract class ItineraryDatabase extends RoomDatabase {
private static ItineraryDatabase instance;
public abstract ItineraryDao Dao();
public static synchronized ItineraryDatabase getInstance(Context mCon){
if (instance == null) {
instance =
Room.databaseBuilder(mCon.getApplicationContext(),
ItineraryDatabase.class, CONSTANTS.ITINERARY_TABLE)
.fallbackToDestructiveMigration()
.addCallback(roomCallback)
.build();
}
return instance;
}
private static RoomDatabase.Callback roomCallback = new RoomDatabase.Callback() {
#Override
public void onCreate(#NonNull SupportSQLiteDatabase db) {
super.onCreate(db);
new PopulateDbAsyncTask(instance).execute();
}
};
private static class PopulateDbAsyncTask extends AsyncTask<Void, Void, Void> {
PopulateDbAsyncTask(ItineraryDatabase instance) {
ItineraryDao dao = instance.Dao();
}
#Override
protected Void doInBackground(Void... voids) {
return null;
}
The above is the code I used is every table to generate table with different Dao's . As I need to create the tablets in a single database i used same CONSTANTS.ITINERARY_TABLE for all of them. If I use differnt names in CONSTANTS.ITINERARY_TABLE this works fine. But it creates different databases.
How can I create multiple tables in a single database without loosing data on restart. I went thorugh some earlier posts. But they suggest to use different database names.
Thank you

How can I create multiple tables in a single database without loosing data on restart.
You define multiple tables by specifying multiple #Entity annotated classes in the entities parameter of the #Database annotation.
That is how you tell Room what tables will be created.
Where you use ItineraryDatabase.class, CONSTANTS.ITINERARY_TABLE, this is telling Room that the database (i.e. the file name) will be named as per CONSTANTS.ITINERARY_TABLE.
I suspect that you want the same table layout/schema but multiple tables (possibly itself not a good idea as each table has overheads when a single column could indicate the equivalent of the row belonging to a specific set of data).
you may wish to edit your question to say more about what you are trying to achieve and you may therefore get more elaborate and specific answers. If you do then feel free to notify myself by comment as such to this answer. At the minimum I would then look at any changes, but there is a very good chance that I would elaborate.
Additional (DEMO)
Considering the comment:-
I want to have different table with different table schema's in a single database file. I have seperate entity classes for them. Do i need to have getinstance for each table in this single class ( which extends with roomdatabase) ? One Database Class with multiple getinstance?
and also the presumption of a single schema (not that it matters much if not, simpler if not). Then consider the following code that is based very much on your code.
ItineraryData (used as a model for two tables, so not #Entity annotated - can be ignored if different schemas);-
class ItineraryData {
#PrimaryKey
Long id=null;
long date=System.currentTimeMillis() / 1000;
#NonNull
String otherColumn;
}
CONSTANTS :-
class CONSTANTS {
static final String ITINERARY_TABLE = "it01";
static final String ITINERARY_TABLE_2 = "it02";
}
ItineraryTable01 (bit of a cheat as schema grabbed from ItineraryData) :-
#Entity(tableName = CONSTANTS.ITINERARY_TABLE)
class ItineraryTable01 extends ItineraryData {
}
ItineraryTable02 (cheat again):-
#Entity(tableName = CONSTANTS.ITINERARY_TABLE_2)
class ItineraryTable02 extends ItineraryData {
}
ItineraryDao
#Dao
public interface ItineraryDao {
#Insert(onConflict = OnConflictStrategy.IGNORE)
void insert(ItineraryTable01 model);
#Insert(onConflict = OnConflictStrategy.IGNORE)
void insert(ItineraryTable02 model);
#Update
void update(ItineraryTable01 model);
#Update
void update(ItineraryTable02 model);
#Delete
void delete(ItineraryTable01 model);
#Delete
void delete(ItineraryTable02 model);
#Query("DELETE FROM " + CONSTANTS.ITINERARY_TABLE)
void deleteAllItineraryTable01();
#Query("DELETE FROM " + CONSTANTS.ITINERARY_TABLE_2)
void deleteAllItineraryTable02();
#Query("SELECT * FROM " + CONSTANTS.ITINERARY_TABLE + " ORDER BY Date ASC")
/*LiveData<*/List<ItineraryTable01>/*>*/ getAllItineraryTable01();
#Query("SELECT * FROM " + CONSTANTS.ITINERARY_TABLE + " ORDER BY Date ASC")
/*LiveData<*/List<ItineraryTable02>/*>*/ getAllItineraryTable02();
}
commented out LiveData so can run on MainThread (although queries not used in demo, but just in case)
note the close duplications for each table.
ItineraryDatabase
#Database(entities = { ItineraryTable01.class,ItineraryTable02.class/*ItineraryData.class*/} ,version = 1,exportSchema = false)
public abstract class ItineraryDatabase extends RoomDatabase {
private static ItineraryDatabase instance;
public abstract ItineraryDao Dao();
public static synchronized ItineraryDatabase getInstance(Context mCon) {
if (instance == null) {
instance =
Room.databaseBuilder(mCon.getApplicationContext(),
ItineraryDatabase.class, CONSTANTS.ITINERARY_TABLE)
.allowMainThreadQueries() /*<<<<<<<<<< Added for convenience brevity of demo */
.fallbackToDestructiveMigration()
.addCallback(roomCallback)
.build();
}
return instance;
}
private static RoomDatabase.Callback roomCallback = new RoomDatabase.Callback() {
#Override
public void onCreate(#NonNull SupportSQLiteDatabase db) {
super.onCreate(db);
new PopulateDbAsyncTask(instance).execute();
}
};
private static class PopulateDbAsyncTask extends AsyncTask<Void, Void, Void> {
PopulateDbAsyncTask(ItineraryDatabase instance) {
ItineraryDao dao = instance.Dao();
}
#Override
protected Void doInBackground(Void... voids) {
return null;
}
}
}
allow to run on Main thread, otherwise unchanged (no need for callback though, it does nothing)
Finally to demo some code in an activity :-
public class MainActivity extends AppCompatActivity {
ItineraryDatabase db;
ItineraryDao dao;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ItineraryTable01 it01 = new ItineraryTable01();
ItineraryTable02 it02 = new ItineraryTable02();
it01.otherColumn = "blah01";
it02.otherColumn = "blah02";
db = ItineraryDatabase.getInstance(this);
dao = db.Dao();
/* Not until following code is executed is the database actually opened */
dao.insert(it01); /* Inserts into table01 */
dao.insert(it02); /* Inserts into table02 */
}
}
Results
When first run
Using App Inspection:-
and
Run again (aka rerun)
and
as can be seen the data (rows highlighted) from the first run has been retained and data from the second run has been added (different generated id's and timestamps)

Related

Query in DAO is always returns null eventhough data exists in Database in Android-Room-LiveData-Fragments

my database class is
public abstract class MyDatabase extends RoomDatabase {
private static MyDatabase instance;
public abstract DataAccess dataAccess();
public static MyDatabase getInstance(final Context context) {
if(instance == null) {
synchronized (MyDatabase.class) {
if(instance == null) {
instance = Room.databaseBuilder(context.getApplicationContext(), MyDatabase.class, "app.db")
.createFromAsset("test.db")
.fallbackToDestructiveMigration()
.build();
}
}
}
return instance;
}
repository class is
public class MyRepository {
private DataAccess dataAccess;
public MyRepository(Application application) {
MyDatabase myDatabase = MyDatabase.getInstance(application);
dataAccess = mDatabase.dataAccess();
}
public LiveData<List<String>> getVillages(int id) {
return dataAccess.getVillage(id);
}
private LiveData<List<VillagesModel>> fetchAllVillages(String search) {
return dataAccess.AllVillages(search);
}
}
DAO class
#Dao
public interface DataAccess {
#Query("SELECT distinct Village FROM Villages WHERE village like :search")
LiveData<List<String>> AllVillages(String search);
#Query("SELECT Village FROM Villages WHERE id = :id")
LiveData<List<String>> getVillages(int id);
}
ViewModel class is
public class VillageViewModel extends AndroidViewModel {
private MyRepository repository;
private LiveData<List<lVillagesModel>> allVillages;
public VillageViewModel(#NonNull Application application) {
super(application);
repository = new MyRepository(application);
}
public LiveData<List<VillagesModel>> getAllVillages(String poSearch) {
return repository.AllVillages(pinSearch, poSearch);
}
}
in Fragment on button click i called public method getVillages is returning always null.
in Fragment Button Onclick method i used observer like this
VillageViewModel.getAllVillages(search).observe(getViewLifecycleOwner(), new Observer<List<VillagesModel>>() {
#Override
public void onChanged(List<VillagesModel> fvillagesModels) {
// updated the recyclerview contents here.
adapter.notifyDataSetChanged(); // this is recyclerView adapter
}
}
This call on ViewModel method returns nothing. While debugging the app, I found that Query from DAO is returning null even though Database has prepopulated values.
Please guide me to solve the problem as I already wasted 6 hours searching in internet for solution.

Android Room implementation with background threading handling queries

I am recently trying to learn Android and I am very new to backend knowledge e.g. threading and stuff. I got Room figured out and try to integrate it with front end component. So, I am not worried how front end adapting the data I want it to present. I have the problem trying to design and implement the integration in a clean way using thread and trying to implement threading since I am new to it.
Here is my code.
Database.class
#Database(entities = {Groups.class, Member.class}, version = 1, exportSchema
= false)
public abstract class DatabaseConfig extends RoomDatabase {
private static final String DATABASE_NAME = "db";
private static DatabaseConfig INSTANCE;
public abstract GroupDao groupDao();
public abstract MemberDao memberDao();
public static DatabaseConfig getDatabase(final Context context) {
if (INSTANCE == null) {
synchronized (DatabaseConfig.class) {
if (INSTANCE == null) {
INSTANCE =
Room.databaseBuilder(context.getApplicationContext(),
DatabaseConfig.class, DATABASE_NAME)
.addCallback(DatabaseCallBack)
.build();
}
}
}
return INSTANCE;
}
private static RoomDatabase.Callback DatabaseCallBack =
new RoomDatabase.Callback(){
#Override
public void onOpen (#NonNull SupportSQLiteDatabase db){
super.onOpen(db);
}
};
}
GroupRepo.class
public class GroupRepo {
private final GroupDao groupDao;
//ExecutorService es = Executors.newSingleThreadExecutor();
public GroupRepo (Context context){
DatabaseConfig db = DatabaseConfig.getDatabase(context);
groupDao = db.groupDao();
}
public List<Groups> getAllGroups(){
/*
So my idea is to have some sort of threading implement from here
and use executor.run() to run my query and capture data
*/
}
}
Groups.class
#Dao
public interface GroupDao {
#Query("SELECT * from groups")
List<Groups> getAllGroups();
#Query("Select * from groups where groups.id = :groupsId")
GroupAllMembers getAllMember(int groupsId);
#Insert
void insert(Groups... groups);
#Delete
void delete(Groups groups);
}
I am not going to post my Entity class since my intention is not about that. I am fairly new to background threads. Please help and ideally provide some example to help me understand.
There are two ways to handle this: if you are doing the query on the database for UI view, I'd recommend your Day return LiveData> and then put that inside of a viewmodel. All of this is covered in Android docs.
If you are doing it in a service or don't want to interact with UI simply do this:
Thread(Runnable() {
#Override
public void run() {
// Do your stuff here with Room
}
}).start()

How to Update SQLite database with new database

I am new to
SQLite
Room Persistence Library
I am making mobile network bundles and offers app to show that I create database with Room Insert Annotation:
Insert
void insertBundle(BundleOfferModel bundleOfferModel);
Then, the database is created in the app databases directory. I then copy that to my PC and add some new data into it, and copy the new modified database file to assets folder of apk. When I run my app again app shows old data but I want to see updated database file data. How can I accomplish this?
Database
#Database(entities = {BundleOfferModel.class}, version = 1, exportSchema = false)
public abstract class AppDatabase extends RoomDatabase {
private static final String DATABASE_NAME = "bundles_offers.db";
private static AppDatabase INSTANCE;
public static AppDatabase getAppDatabase(Context context) {
if (INSTANCE == null) {
INSTANCE = Room.databaseBuilder(context.getApplicationContext(), AppDatabase.class, DATABASE_NAME )
.openHelperFactory(new AssetSQLiteOpenHelperFactory())
.allowMainThreadQueries()
.build();
}
return INSTANCE;
}
public static void destroyInstance() {
INSTANCE = null;
}
public abstract BundleOfferDao bundleDao();
}
Dao
#Dao
public interface BundleOfferDao {
#Insert(onConflict = OnConflictStrategy.REPLACE)
void insertBundle(BundleOfferModel bundleOfferModel);
#Query("SELECT * FROM bundles_offers")
List<BundleOfferModel> getAllBundlesOffers();
}

Make inherited method to use subclass' variables instead of superclass'

I am working on a project in Java and I decided to use MVC architecture.
I have Model class that looks like this:
public class Model {
static String table_name = null;
// ...
public static Model create(Map<String, Object> data) {
// insert `data` into database table `table_name`
}
// ...
}
I want to have class User that extends Model, but I don't want to re-implement create() in User. I just want to change value of table_name and want the inherited create() to use subclass' table_name:
public class User extends Model {
static String table_name = "users";
// ...
}
How can I achieve this?
I know a solution where I add setTableName() method to Model, but that works only if table_name is non-static. However, I need it to be static, because I want create() to be static so that I can do this: User.create().
The fact that there is a static method called 'create' on the Model class doesn't mean that there will automatically be a static 'create' method on a User class that extends the Model class - static methods belong to the class, and they're not inherited. If you implement your own 'create' method on the subclass, there's no relation between that and the 'create' method on the Model class. It sounds like you want a Factory.
If you want to stick with an approach similar to what you've done so far, something like this might work. It keeps the persistence logic in one place, while allowing each model to define its own table name and data mapping.
abstract class Model {
private PersistenceManager persistenceManager = new PersistenceManager();
abstract String getTableName();
abstract Model map(Object persistenceResult);
public void load(Map<String, Object> data) {
Object persistenceResult = persistenceManager.create(data, getTableName());
//Set appropriate fields on this class.
map(persistenceResult);
}
}
class User extends Model {
#Override
String getTableName() {
return "Users";
}
#Override
Model map(Object persistenceResult) {
//Mapping logic.
return null;
}
}
class PersistenceManager {
public Object create(Map<String, Object> data, String tableName) {
//Persistence logic.
//Return result of DB insert here.
return null;
}
}
class ModelFactory {
public static Model createModel(Class modelClass, Map<String, Object> data) {
Model model;
if (modelClass == User.class) {
model = new User();
} else {
//Cater for other models.
throw new IllegalArgumentException("Invalid model.");
}
model.load(data);
return model;
}
}
You can then use it as follows.
Map<String, Object> userData = new HashMap<>();
userData.put("name", "Bob");
Model user = ModelFactory.createModel(User.class, userData);
There is still room for cleanup in the code (like using Generics), but I'll leave that up to you.
Parent can't access child's member (and rightfully so). Look here
What you could do is created a protected method to do the task (it accepts the tablename too) and use that instead.
public class Model {
static String table_name = "A";
public static Model create(Map<String, Object> data) {
return create(data, table_name);
}
protected static Model create(Map<String, Object> data, String table_name) {
// logic here
}
}
public class User extends Model {
static String table_name = "B";
public static Model create(Map<String, Object> data) {
return create(data, table_name);
}
}

More flexible factory

I'm currently working on a little project for school. In my Java application I need a database, and I would like to make it possible to make my application capable to use different types of databases. So I currently implemented a txt-database and a PostgreSQL. In the future, it should be possible to add other database types. Like XML or MySQL, ...
To create a database instance, I designed a factory that uses an enum. It works perfectly, but Itsn't really flexibel in my opionio. So, I did some research, but didn't find a real good example that is clear for me.
This is my enum:
public enum DatabaseType {
TXT,
SQL,
XML;
}
This is my factory:
public class DatabaseFactory {
public Database createDatabase(DatabaseType type, String databaseName) throws DatabaseException {
if(type.equals(DatabaseType.TXT)) {
return new FileDatabase(databaseName);
}else if(type.equals(DatabaseType.SQL)) {
return new SQLDatabase(databaseName);
}else if(type.equals(DatabaseType.XML)) {
return new XMLDatabase(databaseName);
}else {
//default
return new FileDatabase(databaseName);
}
}
}
My aim is to only edit the enum in the future, without touching the factory itself. This should give me enough flexibility, but I've no idea how I could do this.
You could put the factories in the enum itself.
public enum DatabaseType {
TXT {
#Override
public Database createDatabase(String databaseName) {
return new FileDatabase(databaseName);
}
},
SQL {
#Override
public Database createDatabase(String databaseName) {
return new SQLDatabase(databaseName);
}
},
XML {
#Override
public Database createDatabase(String databaseName) {
return new XMLDatabase(databaseName);
}
};
public abstract Database createDatabase(String databaseName);
}
In Java, enums are not just nice names for integral values (like in C). A better way to think of an enum is as a class with a fixed number of instances. Together with the concept of anonymous classes, you can give each value in the enumeration different properties and methods specific for that value.
Use reflection:
Your enum:
public enum DatabaseType {
FILE(FileDatabase.class),
SQL(SQLDatabase.class);
private Database db;
DatabaseType(Class<Database> db) {
this.db = db;
}
/*package friendly*/ Class<Database> getDatabase() {
return this.db;
}
}
Your factory:
public class DatabaseFactory {
public static Database create(DatabaseType type, String dbName) throws Exception {
Database db = null;
Constructor cons = type.getDatabase().getDeclaredConstructor(new Class[] { String.class });
cons.setAccessible(true);
db = cons.newInstance(dbName);
return db;
}
}
Your Database implementors:
public class FileDatabase extends Database {
/* can only be instantiated via reflection */
private FileDatabase(String databaseName) {
// init db.
}
}

Categories