In my android app, I am storing data in database using Room Database library.I can store and get data in a single session.There is no problem in that. The problem is when I close the app and open again I am not getting the data stored in the local database.Those data are deleted.
I've searched in google and some of them said create room data database instance using databaseBuilder.But I am using that only.but I am getting this error.
This is my instance initialization.
This is my AppDtabase class ;
#Database(entities = {SelectedBuilding.class}, exportSchema =true, version=2)
public abstract class AppDatabase extends RoomDatabase {
private static AppDatabase INSTANCE;
public abstract SelectedBuildingDao myDao();
public static AppDatabase getAppDatabase(Context context){
if (INSTANCE==null){
INSTANCE= Room.databaseBuilder(context.getApplicationContext(),AppDatabase.class,"Selected_buildings")
.addMigrations(MIGRATION_1_2)
.build();
}
return INSTANCE;
}
public static void destroyInstance(){
INSTANCE=null;
}
static final Migration MIGRATION_1_2 = new Migration(1, 2) {
#Override
public void migrate(SupportSQLiteDatabase database) {
database.execSQL("ALTER TABLE selected_building ADD COLUMN Building_Id TEXT");
}
};
}
INSTANCE= Room.databaseBuilder(context.getApplicationContext(),AppDatabase.class,"Selected_area").build();
Can anyone help me to solve this issue?
Thanks in Advance.
Related
I've set up a room database with 3 columns (title, descriptions, genre). I want to query the genre column with a user-specified genre(comedy, horror, etc) and return the results.
DAO Interface
I want the Query to only retrieve the entries where the genre matches the genre selected by the user.
#Dao
public interface MovieDAO {
#Query SELECT * FROM movie_table WHERE genre")
pubic LiveData<List<Movie>> getAllMovies();
}
Repository Class
In the Repository.class, can I pass the genre String selected by the user to the Query this way?
public class MovieRepository {
private MovieDao movieDao;
private LiveData<List<Movie>> allMovies;
public MovieRepository(Application application) {
MovieDatabase database = MovieDatabase.getInstance(application);
movieDao = database.MovieDao();
allMovies = movieDao.getAllMovies
}
public void findMoviesByGenre(String genre) {
movieDao.findMoviesByGenre(genre);
}
}
ViewModel class
I'm not sure if I'm missing something in the findMovieByGenre() method
public class MovieViewModel exteneds AndroidViewModel {
private MovieRepository repository;
private LiveData<List<Movie>> allMovies
// Constructor,
public MovieViewModel(#NonNull Application application) {
super(application);
repository = new MovieRepository(Application)
allMovies = repository.getAllMovies();
}
**public void findMovieByGenre(String genre) {
repository.findMoviesByGenre(genre);
}**
}
Activity
This is the part I'm really struggling with, how does the activity call the ViewModel and pass in the genre string parameter? I've tried the approach below but the observe returns the following error.
Cannot resolve method 'observe(com.example.roomexample.MainActivity, anonymous android.arch.lifecycle.Observer>)'
If I remove the genre string in from of the observe, I get the error below.
findMovieByGenre(String)in MovieViewModel cannot be applied
to ()
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
movieViewModel = ViewModelProviders.of(this). get(MovieViewModel.class);
movieViewModel.findMovieByGenre("comedy").observe(this, new Observer<List<Movie>>() {
#Override
public void onChanged(#Nullable final List<Movie> movies) {
Log.d("Movie", movies.get(num).getTitle());
Log.d("Movie", movies.get(num).getDescription());
Log.d("Movie", movies.get(num).getGenre());
}
});
}
In short I want to match the genre selected by the user and match it to the genre entry in the database and return the matching results.
My code is based on the following tutorials. If you have any additional material that code help me in my quest please pass it along.
Google coding Labs
Coding in Flow
Here is a link to my code as it currently stands.
https://github.com/Shawn-Nichol/RoomExample
If you are using MVVM architecture with LiveData follow this method.
1. Observe the LiveData List in MoviesActivity.java
final LiveData<List<MoviesData>> viewModelData = moviesViewModel.getMoviesByGenre("comedy");
viewModelData.observe(this, new Observer<List<MoviesData>>() {
#Override
public void onChanged(List<MoviesData> moviesData) {
//Handle the Movies List here.
}
});
2. In MoviesViewModel.java
public LiveData<List<NotificationData>> getMoviesByGenre(String genere) {
MoviesRepository mRepository = new MoviesRepository(application);
LiveData<List<MoviesData>> mMoviesData = mRepository.getMoviesByGenre(genere);
return mMoviesData;
}
3. MoviesRepository.java
private MoviesDao mMoviesDao;
//Initialize.
AppDatabase db = AppDatabase.getAppDatabase(application);
mMoviesDao = db.moviesDao();
public LiveData<List<MoviesData>> getMoviesByGenre(String genere) {
mMovies = mMoviesDao.findMovieByGenre(genere);
return mMovies;
}
3. In MoviesDao
#Query("SELECT * FROM movie_table ORDER BY genre")
public LiveData<List<Movies> findMovieByGenre(String genre);
So you can Observe query result in your Activity class' Observe method.
To access your app's data using the Room persistence library, you work with data access objects, or DAOs. You may have to use DAO in android room.
By accessing a database using a DAO class instead of query builders or direct queries, you can separate different components of your database architecture
#Dao
public interface MyDao {
#Query("SELECT * FROM movie_table ORDER BY genre")
public ListMovies[] findMovieByGenre(String Genre);
}
I would like to ask if is possible to have multiple database under one project, with Room Persistence Library? Changing dynamic the selection of the database.
Thanks
It is possible.
Let's assume you have two sets of entities and two sets of DAOs. You can obtain access to two databases by:
creating two classes that extends RoomDatabase:
AppDatabase 1:
#Database(entities = {/*... the first set of entities ...*/}, version = 1)
public abstract class AppDatabase1 extends RoomDatabase {
// the first set of DAOs
}
AppDatabase2:
#Database(entities = {/*... the second set of entities ...*/}, version = 1)
public abstract class AppDatabase2 extends RoomDatabase {
// the second set of DAOs
}
instantiating the two databases:
Note that you'll use two different file names.
AppDatabase db1 = Room.databaseBuilder(getApplicationContext(), AppDatabase1.class, "database1.db").build();
AppDatabase db2 = Room.databaseBuilder(getApplicationContext(), AppDatabase2.class, "database2.db").build();
In this case, you can use both databases, but you won't be able to create queries between them. If you need to attach the two databases, then you should take a look at the link #Anees provided
You can reuse the entities and DAO if databases have the same schema and dynamically switch between them (helpful if you want to have a different database file for each user).
Entity class
#Entity
public class User {
#PrimaryKey
#NonNull
public String uid;
#ColumnInfo(name = "first_name")
public String firstName;
#ColumnInfo(name = "last_name")
public String lastName;
}
DAO class
#Dao
public interface UserDao {
#Query("SELECT * FROM user")
List<User> getAll();
#Query("SELECT * FROM user WHERE uid IN (:userIds)")
List<User> loadAllByIds(int[] userIds);
#Query("SELECT * FROM user WHERE first_name LIKE :first AND " +
"last_name LIKE :last LIMIT 1")
User findByName(String first, String last);
#Insert
void insertAll(User... users);
#Delete
void delete(User user);
}
Database class
#Database(entities = {User.class}, version = 1)
public abstract class AppDatabase extends RoomDatabase {
public abstract UserDao userDao();
}
DatabaseClient class
public class DatabaseClient {
private Context mCtx;
private AppDatabase appDatabase;
private static String databaseName;
private static DatabaseClient mInstance;
private DatabaseClient(Context mCtx, String dbName) {
this.mCtx = mCtx;
if(databaseName == null || !databaseName.equalsIgnoreCase(dbName)) {
databaseName = dbName;
}
appDatabase = Room.databaseBuilder(mCtx, AppDatabase.class, databaseName).build();
}
public String getDatabaseName() {
return databaseName;
}
public static synchronized DatabaseClient getInstance(Context mCtx, String dbName) {
if (mInstance == null || databaseName == null || !databaseName.equalsIgnoreCase(dbName)) {
mInstance = new DatabaseClient(mCtx, dbName);
}
return mInstance;
}
public AppDatabase getAppDatabase() {
return appDatabase;
}
}
Now you can query based on a particular database by passing its name in the parameter in my case here let's say myDb
List<User> users = DatabaseClient.getInstance(getApplicationContext(), myDb).getAppDatabase().userDao().getAll()
Remember whenever you perform the first call with a database name, it creates the database file. If a new user arrives and calls to insert its info, it automatically creates a new database file and inserts the info data into it.
Just solved it with koin. I was creating an instant message app, and mutiple account login was required. After user1 logon my app, the databse name with im_id could be got, then by inject, I created the database with the id. Then user1 one logout , I just unload the datasource module and jump to the login activity. User2 then login, I reload the datasource module and created database for user2 with his im_id. Code as follows:
val dataSourceModule = module{
single {
Room.databaseBuilder(androidApplication(), AppDataBase::class.java, get<GsSelectedAndImTokenPersistence>().gsImToken?.gsImId ?: "im_database" )
.build()
}
single { get<AppDataBase>().gsInfoDao() }
single { get<AppDataBase>().gsGameInfoDao() }
single { get<AppDataBase>().gameClientDao() }
single { SharedPreferencesDataSourceImpl(androidContext()) } binds (
arrayOf(
ImDeviceIdPersistence::class,
GsSelectedAndImTokenPersistence::class
))
}
fun unLoadDataSourceModule() {
unloadKoinModules(dataSourceModule)
}
fun reLoadDataSourceModule() {
loadKoinModules(dataSourceModule)
}
The interesting is, even if
get().gsImToken?.gsImId
is null, it won't create the default database with name "im_database" by using koin inject.
And This is where I create the database by inject ,after I got the im_id from server
viewModel.gsImToken.observe(provideLifecycleOwner(), {
ELogger.d("database initial","init database===")
// Incase of the datasource module is not loaded by now
KoinInitializer.reLoadDataSourceModule()
val gs: AppDataBase by inject()
gs.gsGameInfoDao().run {
viewModel.initDao(this)
}
})
And the logout place:
class SettingViewModel(
...,
val db: AppDataBase
): ViewModel() {
...
fun onLogout(){
...
db.close()
KoinInitializer.unLoadDataSourceModule()
...
}
}
I am using Android Room, and I would like to get ID of new inserted row. I have declared column in my model class:
#PrimaryKey (autoGenerate = true)
#ColumnInfo (name = "productID")
int id;
And then I know I can retrive it by dao returning long:
#Insert
long insert(Product p);
At first I was using "thread" calls directly in View. And as you know, it is not recommended method. So I am trying to change it for ModelView and repository. But I don't know how can I get this ID.
My repository class:
public class ProductRepository {
private ProductDao mProductDao;
ProductRepository(Application application) {
AppDatabase db = AppDatabase.getDatabase(application);
mProductDao = db.pDao();
}
public void insertProduct(Product p) {
new insertAsyncTask(mProductDao).execute(p);
}
private static class insertAsyncTask extends AsyncTask<Product, Void, Void> {
private ProductDao mAsyncTaskDao;
insertAsyncTask(ProductDao dao) {
mAsyncTaskDao = dao;
}
#Override
protected Void doInBackground(final Product... params) {
mAsyncTaskDao.insert(params[0]);
return null;
}
}
}
And my model class:
public class ProductModelView extends AndroidViewModel {
private ProductRepository mRepository;
public ProductModelView(Application application) {
super(application);
mRepository = new ProductRepository(application);
}
public void insert(Product p) {
mRepository.insertProduct(p);
}
}
And in my Activity I am inserting new object like this:
mProductModelView.insert(pc);
So how I can retrive this long value from "insert" and get it in my activity? I guess LiveData could be a good way to go, but to be honest I dont havy any ideas how to achieve it :(
The best way to do this is by using LiveData. If you want to use MVVM might as well learn how to use LiveData. It's easy.
In your DAO interface, declare a method like this:
#Query("SELECT * FROM Product ORDER BY id DESC LIMIT 1")
LiveData<Product> getLastProductLive();
This method returns the last Product inserted as LiveData
Then inside your Repository:
public LiveData<Product> getLastProductLive(){
return mProductDao.getLastProductLive();
}
And then inside your ViewModel:
public LiveData<Product> getLastProductLive(){
return mRepository.getLastProductLive();
}
And finally inside your Activity:
mProductViewModel.getLastProductLive().observe(this, product -> {
long lastInsertedRowId = product.getId();
}
By using LiveData, any time that a product is added to table, it triggers this method and you can get the id of the last inserted row.
I'm trying to store values of some variable that my application regulary obtains from API. I whant to add new row to the database table only when variable changes its value to be able to show user some kind of "history of changes". I'm using ROOM for storing data.
I've created an entity:
#Entity(tableName = "balance_history",
indices = {#Index("received_at")})
public class BalanceResponse {
//region getters & setters
...
//endregion
#PrimaryKey(autoGenerate = true)
#ColumnInfo(name = "id")
private long mId;
#ColumnInfo(name = "money")
private double mMoney;
#ColumnInfo(name = "received_at")
private DateTime mReceivedAt;
}
Dao:
#Dao
public abstract class DatabaseDao {
#Query("SELECT * FROM balance_history ORDER BY received_at DESC LIMIT 1")
public abstract LiveData<BalanceResponse> selectLatestBalanceResponse();
public void insertNewBalanceResponse(BalanceResponse balanceResponse) {
String sqlRequest = "INSERT INTO balance_history(money, received_at) " +
"SELECT ?, ? " +
"WHERE NOT EXISTS(SELECT 1 FROM (SELECT * FROM balance_history ORDER BY received_at DESC LIMIT 1) WHERE money = ?);";
SupportSQLiteDatabase database = DatabaseStorage.getInstance().getAppDatabase().getOpenHelper().getWritableDatabase();
database.execSQL(sqlRequest,
new Object[]{balanceResponse.getMoney(), balanceResponse.getReceivedAt().getMillis(), balanceResponse.getMoney()});
}
}
Database object:
#Database(entities = {BalanceResponse.class}, version = 1)
#TypeConverters(DateTimeConverter.class)
public abstract class AppDatabase
extends RoomDatabase {
public abstract DatabaseDao getDatabseDao();
}
Singleton for storing single database object:
public class DatabaseStorage {
//region singleton
private static final DatabaseStorage ourInstance = new DatabaseStorage();
public static DatabaseStorage getInstance() {
return ourInstance;
}
//endregion
#NonNull
public AppDatabase getAppDatabase() {
return mAppDatabase;
}
#NonNull
private AppDatabase mAppDatabase;
private DatabaseStorage() {
mAppDatabase =
Room.databaseBuilder(MyApp.getAppContext(), AppDatabase.class, "app-database")
.build();
}
}
And viewmodel that I instantiate in my Activity's onCreate():
public class BalanceView implements Observer<BalanceResponse> {
private LiveData<BalanceResponse> mLatestBalanceResponse;
public BalanceView(LifecycleOwner lifecycleOwner){
mLatestBalanceResponse = DatabaseStorage.getInstance().getAppDatabase().getDatabseDao()
.selectLatestBalanceResponse();
mLatestBalanceResponse.observe(lifecycleOwner, this);
//finding views here
}
#Override
public void onChanged(#Nullable BalanceResponse balanceResponse) {
//displaying changes here
}
}
I've expected triggering of BalanceView.onChanges() method each time when method DatabaseDao.insertNewBalanceResponse() inserts a row.
Actually BalanceView.onChanges() method never gets fired. Why is that so? How can I accomplish this?
p.s. However, If I replace method DatabaseDao.insertNewBalanceResponse() with original:
#Insert
public abstract Long insertBalanceResponse(BalanceResponse balanceResponse);
Everithing works fine and method onChange() gets invoked. But this kind of insert statement doesn't fit my needs.
I have the same issue and here I got a hint to solve this issue.
#Dao
interface RawDao {
#RawQuery
User getUserViaQuery(SupportSQLiteQuery query);
}
SimpleSQLiteQuery query = new SimpleSQLiteQuery("SELECT * FROM User WHERE id = ? LIMIT 1",
new Object[]{userId});
User user2 = rawDao.getUserViaQuery(query);
For More details, check https://developer.android.com/reference/android/arch/persistence/room/RawQuery.
I go through SQL query other solution. i am not able to find suitable solutions for my problem.
In My project, i have the insert data into the table. I have to follow some steps
Step1:- I have to check data through a primary key that the data is available or not.
Step 2: if data is available then I have to update that data and return response code. if not I have to go step 3
Step 3: if data is not in the table then insert data into it and return code.
I am using Room Library. i am confused how to write in #Dao to perform that task.
Thanks in advance
Android Architecture Components introduced Android Room Persistence Library which is best for sqlite android database handling. Entity in Room Persistence represents a database table and Dao is where we define database interactions. Example
#Entity
public class Trail {
public #PrimaryKey String id;
public String name;
public double kilometers;
public int difficulty;
}
possible Dao for this table will be
#Dao
public interface TrailDao {
#Insert(onConflict = IGNORE)
void insertTrail(Trail trail);
#Query("SELECT * FROM Trail")
List<Trail> findAllTrails();
#Update(onConflict = REPLACE)
void updateTrail(Trail trail);
#Query("DELETE FROM Trail")
void deleteAll();
}
Further you need to provide RoomDatabase implementation, Example
#Database(entities = {Trail.class}, version = 1)
public abstract class AppDatabase extends RoomDatabase {
private static AppDatabase INSTANCE;
public abstract TrailDao trailDao();
public static AppDatabase getInMemoryDatabase(Context context) {
if (INSTANCE == null) {
INSTANCE =
Room.inMemoryDatabaseBuilder(context.getApplicationContext(), AppDatabase.class)
.allowMainThreadQueries()
.build();
}
return INSTANCE;
}
public static void destroyInstance() {
INSTANCE = null;
}
}
Use It like
AppDatabase. getInMemoryDatabase(context).trailDao().findAllTrails();