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();
}
Related
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)
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.
I'm trying to build a Room Database but it says that Context is null even tho I tried the same classes in different application and it works as intended.
#Database(entities = {City.class, User.class}, version = AppDatabase.DB_VERSION, exportSchema = false)
public abstract class AppDatabase extends RoomDatabase {
static final int DB_VERSION = 1;
private static AppDatabase INSTANCE;
private static final String DATABASE_NAME = "my_db";
public abstract UserDao userDao();
public abstract CityDao cityDao();
public static AppDatabase getInstance() {
if (INSTANCE == null) {
INSTANCE = Room.databaseBuilder(RoomApp.getAppContext(), AppDatabase.class, DATABASE_NAME)
.allowMainThreadQueries()
.build();
}
return INSTANCE;
}
}
public class RoomApp extends Application {
private static WeakReference<RoomApp> appContext;
#Override
public void onCreate() {
super.onCreate();
appContext = new WeakReference<>(this);
AppDatabase.getInstance(); //this will init & fill db
}
public static Context getAppContext() {
return appContext != null ? appContext.get() : null;
}
}
Whenever I access AppDatabase.getInstance(); I get an error message saying: "Cannot provide null context for the database."
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()
I have been assigned an IT project in which we have to program various different GUI's to do various things. We are also using a database. Let's assume we are accessing an "EntityManager" in a class called "Database":
public class GUI1 {
private Database myDatabase;
public void setDatabase(Database DB){
myDatabase = DB;
}
}
public class GUI2 {
private Database myDatabase;
public void setDatabase(Database DB){
myDatabase = DB;
}
}
public class GUI3 {
private Database myDatabase;
public void setDatabase(Database DB){
myDatabase = DB;
}
}
etc...
Lets say I'm in "GUI1" and I want to switch to "GUI3". After initializing "GUI3" I would have to pass "myDatabase" reference to it via the "setDatabase()" method, but if I want to go back to "GUI1", I would have to pass back the database reference again...
By now I have around 15 GUIs and it get's annoying to copy and paste the same code around when I know it could be replaced easily. In this case, wouldn't it be correct to just use a static reference to whatever I want inside the "Database" class instead of passing around the reference between all my "GUI*" classes?
Create a singleton database object, where everybody access the same object:
public class Database {
private Database(){ // privatize the constructor
// your code here
}
private static Database INSTANCE;
public static Database getInstance() {
if(INSTANCE == null) {
// let's make it thread-safe
synchronized(Database.class) {
if(INSTANCE == null) // may have changed in the mean while
// by other thread
INSTANCE = new Database();
}
}
return INSTANCE;
}
}
EDIT: Even better, from a thread-safe perspective is the enum:
public enum Database {
INSTANCE(); // pair of parenthesis, for constructor
Database() { // constructor
// your code here
}
public static Database getInstance() {
return INSTANCE;// initialization controlled by system
}
public void someMethod(){
// even allows you to add custom methods
}
}