I have an adapter and RecyclerView to display the list of contacts saved locally. When the user clicks on an entry, a new activity is launcher where they can edit it. However, I also want to have a "Delete" button at the very bottom to delete the entry, and then go back to the adapter activity. Using the delete query of Room, or otherwise, how can I delete the entry from the EditContact activity?
In ContactDao, I have the following:
// Delete single entry
#Query("DELETE FROM contacts_table WHERE id = :userId")
void deleteByContactId(long userId);
ContactRepository:
public void delete(Contact contact) {
new DeleteContactAsyncTask(contactDao).execute(contact);
}
private static class DeleteContactAsyncTask extends AsyncTask<Contact, Void, Void> {
private ContactDao contactDao;
private DeleteContactAsyncTask(ContactDao contactDao) {
this.contactDao = contactDao;
}
#Override
protected Void doInBackground(Contact... contacts) {
contactDao.delete(contacts[0]);
return null;
}
}
Finally, I want in EditContact to delete the entry on click:
public void deleteContact(View view) {
// Delete the entry based on the ID
}
Hi write a query in your dao file
//get a contact
#Query("select * FROM contacts_table WHERE id = :userId")
Contact getConatctByContactId(long userId);
Once you get the contact id in your edit activity, call ths function to get the contact.
But since your delete function works with id only, you do not need to retrieve to delete from the table
UPDATE
declare the dao in the database class
public abstract class AppDatabase extends RoomDatabase {
public abstract ContactDao contactDao();
}
And call it using your database instance
database.contactPointDao().deleteContact()
I was extensively trying to debug my code because I forgot to call .execute() at the end of my DeleteContactByIdAsyncTask(contactDao, id);
Below are my final code snippets:
ContactDao:
// Delete single entry
#Query("DELETE FROM contacts_table WHERE id = :userId")
void deleteByContactId(long userId);
ContactRepository:
private static class DeleteContactByIdAsyncTask extends AsyncTask<Contact, Void, Void> {
private ContactDao contactDao;
private int id;
private DeleteContactByIdAsyncTask(ContactDao contactDao, int id) {
this.contactDao = contactDao;
this.id = id;
}
#Override
protected Void doInBackground(Contact... contacts) {
contactDao.deleteByContactId(id);
return null;
}
}
ContactViewModel:
public void deleteContactById(int id) {
contactRepository.deleteContactById(id);
}
And, in the ViewContact activity, upon returning the result:
if (requestCode == EDIT_CONTACT_REQUEST && resultCode == RESULT_OK && isDeleted) {
Toast.makeText(this, "Contact Deleted", Toast.LENGTH_SHORT).show();
contactViewModel.deleteContactById(id);
onBackPressed();
}
And that's it. Entry is deleted, and upon returning to the activity with the RecyclerView, the entries are automatically updated.
Related
I've been doing Android Studio for a month only and I've got to say I'm kind of confused with Room database, I'm sorry if the question sounds confused.
I'm using a Room database as stated, here are my Database-related classes:
#Entity(tableName="board")
public class BoardItem {
#PrimaryKey(autoGenerate = true)
#ColumnInfo(name="board_id")
private int boardId;
#ColumnInfo(name="board_name")
private String boardName;
#ColumnInfo(name="board_description")
private String boardDescription;
#ColumnInfo(name="board_image_list")
#TypeConverters(ImageListTypeConverter.class)
private List<ImageItem> boardImageList;
public BoardItem(String boardName, String boardDescription){
this.boardName = boardName;
this.boardDescription = boardDescription;
this.boardImageList = new ArrayList<>();
}
public int getBoardId() { return boardId; }
public String getBoardName() {
return boardName;
}
public String getBoardDescription() {
return boardDescription;
}
public String getPhotosCount() {
return String.valueOf(this.boardImageList.size());
}
public void setBoardId(int boardId) {
this.boardId = boardId;
}
public List<ImageItem> getBoardImageList() {
return boardImageList;
}
public void setBoardImageList(List<ImageItem> list) { this.boardImageList = list; }
#Dao
public interface BoardItemDAO {
#Insert(onConflict = OnConflictStrategy.IGNORE)
void addBoardItem(BoardItem boardItem);
#Transaction
#Query("SELECT * from board ORDER BY board_id DESC")
LiveData<List<BoardItem>> getBoardItems();
#Transaction
#Query("SELECT * from board ORDER BY board_id DESC")
List<BoardItem> getBoardItemsNow();
}
#Database(entities = {BoardItem.class}, version = 1)
public abstract class BoardItemDatabase extends RoomDatabase {
public abstract BoardItemDAO boardItemDAO();
//Singleton
private static volatile BoardItemDatabase INSTANCE;
private static final int NUMBER_OF_THREADS = 4;
static final ExecutorService databaseWriteExecutor = Executors.newFixedThreadPool(NUMBER_OF_THREADS);
static BoardItemDatabase getDatabase(final Context context){
if (INSTANCE == null) {
synchronized (BoardItemDatabase.class) {
if (INSTANCE == null) {
INSTANCE = Room.databaseBuilder(context.getApplicationContext(), BoardItemDatabase.class, "board_database")
.allowMainThreadQueries()
.build();
}
}
}
return INSTANCE;
}
}
//Repository
public class BoardItemRepository {
private BoardItemDAO boardItemDAO;
private LiveData<List<BoardItem>> boardItemList;
private List<BoardItem> boardItemListNow;
public BoardItemRepository (Application application) {
BoardItemDatabase db = BoardItemDatabase.getDatabase(application);
boardItemDAO = db.boardItemDAO();
boardItemList = boardItemDAO.getBoardItems();
boardItemListNow = boardItemDAO.getBoardItemsNow();
}
//Room executes all queries on a separate thread
//Observed LiveData will notify the observer when data has changed
public LiveData<List<BoardItem>> getBoardItemList() { return boardItemList; }
//this method is called on a non-UI thread or the app will throw an exception. Room ensures
//that there are no long running operations on the main thread, blocking the UI.
public void addBoardItem(final BoardItem boardItem) {
BoardItemDatabase.databaseWriteExecutor.execute(() -> boardItemDAO.addBoardItem(boardItem));
}
public List<BoardItem> getBoardItemListNow() {return boardItemListNow; }
}
Note that BoardItem and ImageItem are classes I made myself: a board is supposed to contain multiple ImageItems.
My boardItem has different fields, one of which is a list of ImageItems.
Now, in a specific fragment I try to update this list of ImageItems in a board that already exists in my database, which is the board with id = 0 (the very first board in the db). I try to retrieve the list from the Database and replace it with a new one.
I have used LiveData in certain cases to update the view of my app when the item change, but I have non LiveData methods for this specific piece of my code I need to change my database as soon as I click the button that contains this code:
List<BoardItem> boardItems = boardListViewModel.getBoardItemsNow();
newList = boardItems.get(0).getBoardImageList();
newList.add(newItem);
boardItems.get(0).setBoardImageList(newList);
When I click the button, the code is executed with no errors, but the database isn't updated; it contains the list as it was before, without the new item.
Thanks in advance, again I'm sorry if this sounds confusing!
EDIT:
here's my ViewModel:
public class BoardListViewModel extends AndroidViewModel {
private final MutableLiveData<BoardItem> boardSelected = new MutableLiveData<>();
private LiveData<List<BoardItem>> boardItems;
private List<BoardItem> boardItemsNow;
public BoardListViewModel(#NonNull Application application) {
super(application);
BoardItemRepository repo = new BoardItemRepository(application);
boardItems = repo.getBoardItemList();
boardItemsNow = repo.getBoardItemListNow();
}
public void select(BoardItem boardItem) {
boardSelected.setValue(boardItem);
}
public LiveData<BoardItem> getSelected() {
return boardSelected;
}
public LiveData<List<BoardItem>> getBoardItems() {
return boardItems;
}
public BoardItem getBoardItem(int position) {
return boardItems.getValue() == null ? null : boardItems.getValue().get(position);
}
public List<BoardItem> getBoardItemsNow() { return boardItemsNow; }
}
I believe that your issue is that you are not updating but attempting to add (insert) a new item (row) via :-
#Insert(onConflict = OnConflictStrategy.IGNORE)
void addBoardItem(BoardItem boardItem);
As the boardid already exists and that it is the primary key, which is implicitly unique, a conflict occurs, and this conflict is ignored, thus the new row is not added and the database remains unchanged.
What you should be doing is updating the existing row, so you want an #Update dao.
So add and then use the following dao :-
#Update(onConflict = OnConflictStrategy.IGNORE)
int updateBoardItem(BoardItem boardItem);
the int returned is the number of rows that have been update (you would expect 1, 0 if a conflict resulted in the update being ignored).
I have a RecyclerView list of CardView items that is working properly. Upon creation of a new CardView that is inserted into the database, I would like to fire a Toast that informs the user that the CardView was successfully added and show the CardView number. The CardView number is the Id of the CardView item inserted into the database. The data is saved to the database when the user clicks on a Save button that fires onClickSave().
I set up an #Query in the Dao to get the MAX(cardId):
Dao
...
#Query("SELECT MAX(cardId) FROM cards")
LiveData<Integer> getMax();
#Insert
void insertCard(Card card);
Problem is that two Toasts are firing. The first Toast is returning the previously created CardView number and then the second Toast is firing and it shows the latest CardView number that was just added. For example, the Toast will show CardView number 33 and then a second Toast fires that shows the expected CardView number 34 that was just created (I confirm that CardViews 33 and 34 are both in the database and the two highest items, using DB Browser for SQLite software).
AddorUpdateCardActivity
...
private int newMax = -1;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mViewModel = new ViewModelProvider(this).get(cardViewModel.class);
}
public void onClickSave(View v) {
// set card data
// then insert data in database
mViewModel.insertCard(card1);
mViewModel.getMax().observe(this, value -> { newMax = value; Toast.makeText(AddorUpdateCardActivity.this, "card #" + newMax + " was saved to the list", Toast.LENGTH_LONG).show();});
}
ViewModel
...
public cardViewModel(Application application) {
super(application);
repository = new cardRepository(application);
getMax = repository.getMax();
}
public LiveData<Integer> getMax() {
return getMax;
}
public void insertCard(Card card) {
repository.insertCard(card);
}
cardRepository
private CardDao cardDao;
private LiveData<Integer> getMax;
public cardRepository(Application application) {
RoomDatabase db = RoomDatabase.getDatabase(application);
cardDao = db.cardDao();
}
public LiveData<Integer> getMax() {
return cardDao.getMax;
}
public void insertCard(Quickcard newcard) {
AsyncTask.execute(() -> cardDao.insertCard(newcard));
}
What am I missing here? If the card is inserted properly into the database then why wouldn't the ViewModel observer just return this new CardView number rather than two Toasts?
For reference, I show the previous code I used prior to Room and ViewModel that used a cursor to get the latest and highest inserted Id:
public class SQLiteDB extends SQLiteOpenHelper {
...
public int getLastInsertId() {
int index = 0;
SQLiteDatabase sdb = getReadableDatabase();
Cursor cursor = sdb.query(
"sqlite_sequence",
new String[]{"seq"},
"name = ?",
new String[]{TABLE_NAME},
null,
null,
null,
null
);
sdb.beginTransaction();
try {
if (cursor !=null) {
if (cursor.moveToLast()) {
index = cursor.getInt(cursor.getColumnIndex("seq"));
}
}
...
}
return index;
}
The view model operations you call within onClickSave are asynchronous:
public void onClickSave(View v) {
mViewModel.insertCard(card1);
mViewModel.getMax().observe(this, value -> { newMax = value; makeText(AddorUpdateCardActivity.this, "TEXT", .LENGTH_LONG).show();});
}
The implementation of LiveData records the data version as well as the last version seen by the observer.
Therefore insertCard starts to operate on a worker thread while you start observing getMax from the main thread with a newly created observer. Thus you'll receive the current value as well as the new value after the database was updated.
Instead you could observe it only once in onCreate() and wait for the updates triggered by the database:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mViewModel = new ViewModelProvider(this).get(cardViewModel.class);
mViewModel.getMax().observe(this, value -> { newMax = value; makeText(AddorUpdateCardActivity.this, "TEXT", .LENGTH_LONG).show();});
}
public void onClickSave(View v) {
mViewModel.insertCard(card1);
}
The Room Insert operation inside AsyncTask takes a while before the maxCount variable is updated. Since you are showing the Toast inside a button click, the message is displayed right away without receiving the updated value from LiveData.
Move the Toast message inside the obverve() method so that it gets triggered only after a LiveData change.
mViewModel.getMax().observe(this, value -> {
newMax = value;
Toast.makeText(AddorUpdateCardActivity.this, "card #" + newMax + " was saved to the list", Toast.LENGTH_LONG).show();
});
At this point, the code should be working but you'll get multiple LiveData events for a single Insert. This is happening because you have used 2 separate instances of Dao for Insert and Query operation.
public cardRepository(Application application) {
RoomDatabase db = RoomDatabase.getDatabase(application);
cardDao = db.cardDao(); // <---------- Instance #1
getMax = cardDao.getMax();
}
public LiveData<Integer> getMax() {
return getMax;
}
public void insertCard(Card newcard) {
new InsertAsyncTask(quickcardDao).execute(newcard);
}
private static class InsertAsyncTask extends AsyncTask<Card, Void, Integer> {
private CardDao asyncTaskDao;
InsertAsyncTask(CardDao dao) {
asyncTaskDao = dao; // <---------- Instance #2
}
#Override
protected Integer doInBackground(final Card... params) {
asyncTaskDao.insertCard(params[0]);
return null;
}
}
To resolve it use the same Dao instance everywhere:
public cardRepository(Application application) {
RoomDatabase db = RoomDatabase.getDatabase(application);
cardDao = db.cardDao();
}
public LiveData<Integer> getMax() {
return cardDao.getMax();
}
public void insertCard(Card newcard) {
AsyncTask.execute(() -> cardDao.insertCard(newcard));
}
Because of using AsyncTask to insert card to database, that function take some time to complete and you show your toast, instantly! Change your activity to this:
AddorUpdateCardActivity
...
private int newMax = -1;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mViewModel = new ViewModelProvider(this).get(cardViewModel.class);
mViewModel.getMax().observe(this, integer2 -> {
newMax = integer2;
Toast.makeText(AddorUpdateCardActivity.this, "card #" + newMax + " was saved to the list", Toast.LENGTH_LONG).show();
hideProgressBar();
});
}
public void onClickSave(View v) {
//set card data
// then insert data in database
mViewModel.insertCard(card1);
showProgressBar();
}
I am creating an app that requires a team of players. I am using the Team ID as the team primary key and the foreign key for each player. In one fragment I create a new team. When the team is created and added to my room database it initially has the ID of 0 or not set even though I have auto generate set to true. I then navigate to the team roster view which has the ability to add new players to the team. When I create a new player and use the new teams ID in the team view model the team ID is still 0 or not set so the app crashes and there is a foreign key restraint failure. After the crash if I reopen the app or if I avoid the crash by going back to the team list and selecting the team that was just created with an initial id of 0, when I create a player this time the team will have a valid ID. Why does room not immediately assign a unique ID when the object is created and it waits for navigation away and back to the fragment or a restart of the app? Relevant code below, feels like I might be giving too code much but I am following jetpack best practices that I found from android documentation, and I do not know where the problem is stemming from. https://developer.android.com/jetpack/docs/guide.
Database
#Database (entities = {Team.class,
Player.class},
version = 6)
public abstract class AppDatabase
extends RoomDatabase
{
private static final String DATABASE_NAME = "Ultimate_Stats_Database";
private static volatile AppDatabase instance;
public abstract TeamDAO teamDao ();
public abstract PlayerDAO playerDAO ();
static synchronized AppDatabase getInstance (Context context)
{
if (instance == null)
{
// Create the instance
instance = create(context);
}
// Return the instance
return instance;
}
private static AppDatabase create (final Context context)
{
// Create a new room database
return Room.databaseBuilder(
context,
AppDatabase.class,
DATABASE_NAME)
.fallbackToDestructiveMigration() // TODO Add migrations, poor practice to ignore
.build();
}
}
Team entity
#Entity (tableName = "teams")
public class Team
implements Parcelable
{
#PrimaryKey (autoGenerate = true)
private long id;
private String name;
public Team ()
{
this.name = "";
}
public Team (String name)
{
this.name = name;
}
...
Team DAO
#Dao
public abstract class TeamDAO
{
#Insert (onConflict = OnConflictStrategy.REPLACE)
public abstract long insert (Team team);
#Delete
public abstract int deleteTeam (Team team);
#Query ("SELECT * FROM teams")
public abstract LiveData<List<Team>> getAllTeams ();
}
Team Repository (Inserting Only)
private TeamDAO teamDao;
private LiveData<List<Team>> teams;
public TeamRepository (Application application)
{
AppDatabase db = AppDatabase.getInstance(application);
teamDao = db.teamDao();
teams = teamDao.getAllTeams();
}
private static class insertAsyncTask
extends AsyncTask<Team, Void, Void>
{
private TeamDAO asyncTeamTaskDao;
insertAsyncTask (TeamDAO teamDao)
{
asyncTeamTaskDao = teamDao;
}
#Override
protected Void doInBackground (final Team... params)
{
// Trace entry
Trace t = new Trace();
// Insert the team into the database
asyncTeamTaskDao.insert(params[0]);
// Trace exit
t.end();
return null;
}
}
Team View Model
public class TeamViewModel
extends AndroidViewModel
{
private TeamRepository teamRepository;
private LiveData<List<Team>> teams;
private MutableLiveData<Team> selectedTeam;
public TeamViewModel (Application application)
{
super(application);
teamRepository = new TeamRepository(application);
teams = teamRepository.getAllTeams();
selectedTeam = new MutableLiveData<Team>();
}
public LiveData<Team> getSelectedTeam()
{
return selectedTeam;
}
public void selectTeam(Team team)
{
selectedTeam.setValue(team);
}
public LiveData<List<Team>> getTeams ()
{
return teams;
}
public void insert (Team team)
{
teamRepository.insert(team);
}
...
Player entity
#Entity(tableName = "players",
foreignKeys = #ForeignKey(entity = Team.class,
parentColumns = "id",
childColumns = "teamId"),
indices = {#Index(value = ("teamId"))})
public class Player
implements Parcelable
{
#PrimaryKey (autoGenerate = true)
private long id;
private String name;
private int line;
private int position;
private long teamId;
public Player ()
{
this.name = "";
this.line = 0;
this.position = 0;
this.teamId = 0;
}
public Player(String name,
int line,
int position,
long teamId)
{
this.name = name;
this.line = line;
this.position = position;
this.teamId = teamId;
}
....
Player DAO
#Dao
public abstract class PlayerDAO
{
#Insert (onConflict = OnConflictStrategy.REPLACE)
public abstract void insert (Player player);
#Delete
public abstract int deletePlayer (Player player);
#Query ("SELECT * FROM players WHERE teamId = :teamId")
public abstract LiveData<List<Player>> getPlayersOnTeam (long teamId);
#Query ("SELECT * FROM players")
public abstract LiveData<List<Player>> getAllPlayers();
#Query ("SELECT * FROM players WHERE id = :id")
public abstract LiveData<Player> getPlayerById (long id);
}
Player Repository (Inserting Only)
private PlayerDAO playerDAO;
private LiveData<List<Player>> players;
public PlayerRepository(Application application)
{
AppDatabase db = AppDatabase.getInstance(application);
playerDAO = db.playerDAO();
players = playerDAO.getAllPlayers();
}
public void insert (Player player)
{
new PlayerRepository.insertAsyncTask(playerDAO).execute(player);
}
private static class insertAsyncTask
extends AsyncTask<Player, Void, Void>
{
private PlayerDAO asyncTaskDao;
insertAsyncTask (PlayerDAO dao)
{
asyncTaskDao = dao;
}
#Override
protected Void doInBackground (final Player... params)
{
// Get the player being inserted by its id
LiveData<Player> player = asyncTaskDao.getPlayerById(((Player) params[0]).getId());
if (player != null)
{
// Delete the old record of the player
asyncTaskDao.deletePlayer(params[0]);
}
// Insert the player into the database
asyncTaskDao.insert(params[0]);
return null;
}
}
...
Player View Model
public class PlayerViewModel
extends AndroidViewModel
{
private PlayerRepository playerRepository;
private LiveData<List<Player>> players;
private MutableLiveData<Player> selectedPlayer;
public PlayerViewModel(Application application)
{
super(application);
playerRepository = new PlayerRepository(application);
players = playerRepository.getAllPlayers();
selectedPlayer = new MutableLiveData<Player>();
}
public LiveData<Player> getSelectedPlayer()
{
return selectedPlayer;
}
public void selectPlayer(Player player)
{
selectedPlayer.setValue(player);
}
public LiveData<List<Player>> getPlayers ()
{
return players;
}
public void insert (Player player)
{
playerRepository.insert(player);
}
...
Where I create the team (in the TeamListFragment and when a dialog fragment is completed)
public void onDialogPositiveClick (String teamName)
{
// Trace entry
Trace t = new Trace();
// Create a new team object
Team newTeam = new Team();
// Name the new team
newTeam.setName(teamName);
// Insert the team into the database and set it as the selected team
teamViewModel.insert(newTeam);
teamViewModel.selectTeam(newTeam);
// Trace exit
t.end();
// Go to the player list view
routeToPlayerList();
}
In the playerListFragment when it is created
/*------------------------------------------------------------------------------------------------------------------------------------------*
* If the view model has a selected team *
*------------------------------------------------------------------------------------------------------------------------------------------*/
if (sharedTeamViewModel.getSelectedTeam().getValue() != null)
{
// Set the team to the team selected
team = sharedTeamViewModel.getSelectedTeam().getValue();
// Set the team name fields default text
teamNameField.setText(team.getName());
}
In the playerFragment when the save button is clicked
#Override
public void onClick (View v)
{
// Trace entry
Trace t = new Trace();
// Update the player object with the info given by the user
boolean success = getUserInput();
/*------------------------------------------------------------------------------------------------------------------------------*
* If the input was valid *
*------------------------------------------------------------------------------------------------------------------------------*/
if (success)
{
// Set the player id to the team that is selected
player.setTeamId(sharedTeamViewModel.getSelectedTeam()
.getValue()
.getId());
// Input the the player into the player view model
sharedPlayerViewModel.insert(player);
// Remove this fragment from the stack
getActivity().onBackPressed();
}
// Trace exit
t.end();
}
If there is any other code that is needed let me know
This is an expected behavior. Room will not directly update the id field in the newTeam.
It does not make sense for Room to change the input object, not to mention that Room does not assume that the entity fields are mutable. You can make all your Entity fields immutable and I believe it is a good practice to make your entity classes immutable whenever possible.
If you want to retrieve the id of the inserted row, check out this SO link: Android Room - Get the id of new inserted row with auto-generate
I made some simple project for test, but i'm having some trouble with delete. It won't work. I can add contact normally, but when I try to delete it, nothing happens and i don't have any erros. Here is my code:
Entity
#Entity
public class Contact {
#PrimaryKey(autoGenerate = true)
private int id;
#ColumnInfo(name = "contact_name")
private String contactName;
#ColumnInfo(name = "contact_number")
private String contactNumber;
#ColumnInfo(name = "contact_image")
#Nullable
private String contactImage;
...
My Dao:
#Dao
public interface ContactDao {
#Query("SELECT * FROM Contact")
LiveData<List<Contact>> getContacts();
#Query("SELECT * FROM Contact WHERE id = :contact_id")
Contact getContactById(int contact_id);
#Insert
void addContact(Contact contact);
#Delete
void deleteContact(Contact contact);
}
ViewModel:
public class ContactViewModel extends AndroidViewModel {
private LiveData<List<Contact>> contacts;
private ContactsDatabase contactsDatabase;
public ContactViewModel(#NonNull Application application) {
super(application);
contactsDatabase = ContactsDatabase.getINSTANCE(this.getApplication());
contacts = contactsDatabase.contactDao().getContacts();
}
public LiveData<List<Contact>> getContacts() {
return contacts;
}
public void deleteContact(Contact contact) {
new deleteAT(contactsDatabase).execute(contact);
}
private class deleteAT extends AsyncTask<Contact, Void, Void> {
private ContactsDatabase contactsDatabase;
deleteAT(ContactsDatabase db) {
this.contactsDatabase = db;
}
#Override
protected Void doInBackground(Contact... contacts) {
contactsDatabase.contactDao().deleteContact(contacts[0]);
return null;
}
}
}
Any solutions ?
In some cases it might be better to add a custom delete by-id instead of using the auto-generated delete by-object.
In this case it'd be
#Query("DELETE FROM Contact WHERE id = :contact_id")
void deleteContactById(int contact_id);
See https://stackoverflow.com/a/47554641/6513193 for more info.
Take a look at the generated addContact() code. It doesn't set the id on your Contact instance so you need to do it yourself.
#Entity
public class Contact {
#PrimaryKey(autoGenerate = true)
private long id;
//...
}
#Dao
public interface ContactDao {
#Insert
long addContact(Contact contact); //returns autogenerated id
#Delete
void deleteContact(Contact contact);
}
Then this should work:
long insertedId = dao.addContact(contact);
contact.setId(insertedId);
dao.deleteContact(contact);
To elaborate on the other answer you can simply use contact.setId(myDatabase.addContact(contact))
Then later on your local instance of contact will have the correct ID set allowing it to be deleted if necessary.
Also worth noting that this case is only possible when the primary key gets generated for the first time, as it's possible to insert an entry with no primary key but it's impossible to pull a pre-existing entry from a database without a primary key.
I am trying to update my database via new android room library, but it is not working. Here it is my approach
#IgnoreExtraProperties
#Entity(tableName = CarModel.TABLE_NAME,
indices = {#Index(value = "car_name", unique = true)})
public class CarModel {
public static final String TABLE_NAME = "cars";
#PrimaryKey(autoGenerate = true)
private int id;
#ColumnInfo(name = "car_name")
private String name;
#ColumnInfo(name = "car_price")
private String price;
private String type;
private String position;
}
MainActivity.java
viewModel.isCarsEmpty().observe(MainActivity.this, new Observer<Integer>() {
#Override
public void onChanged(#Nullable Integer rowCount) {
if (rowCount == 0) {
viewModel.insertItems(list);
} else {
viewModel.updateItems(list);
}
}
});
CarViewModel.java
public LiveData<Integer> isCarsEmpty() {
return appDatabase.carDao().isDbEmpty();
}
public void insertItems(List<CarModel> carModels) {
new insertCarsAsyncTask(appDatabase).execute(carModels);
}
private class insertCarsAsyncTask extends AsyncTask<List<CarModel>, Void, Void> {
private AppDatabase db;
public insertCarsAsyncTask(AppDatabase appDatabase) {
db = appDatabase;
}
#Override
protected Void doInBackground(List<CarModel>... params) {
db.carDao().insertCars(params[0]);
return null;
}
}
public void updateItems(List<CarModel> list) {
new updateCarsTask(appDatabase).execute(list);
}
private class updateCarsTask extends AsyncTask<List<CarModel>, Void, Void> {
private AppDatabase db;
public updateCarsTask(AppDatabase appDatabase) {
db = appDatabase;
}
#Override
protected Void doInBackground(List<CarModel>... params) {
db.carDao().updateCars(params[0]);
return null;
}
}
CarDao.java
#Insert(onConflict = REPLACE)
void insertCars(List<CarModel> cars);
#Update
void updateCars(List<CarModel> param);
#Query("SELECT count(*) FROM " + CarModel.TABLE_NAME)
LiveData<Integer> isDbEmpty();
I did debugging, new data comes and calling viewModel.updateItems(list) method.Thanks in advance!
Make sure the row you want to updated and model you are sending to update should have same id which you have defined as primary key.
I am sorry for posting this as an answer but I am not allowed to add a simple comment yet so here is my idea:
Have you tried using only insertCars() instead of updateCars()?
Anyway it looks like your isCarsEmpty() LiveData callback gets triggered the whole time because when the observer is called the Database is altered again.. I am not quite sure what you want to accomplish tho.
The update means you fetch a CarModel which is already in the database and after updating this CarModel you return the new values to the Database, except the Id you return the same Id
what to do
you need for example a global variable of type CarModel and implement set functions in the CarModel
In MainActivity assignt the fetched CarModel to the global variable and use the set functions to update the values of member variables of the CarModel
then pass this global variable to the update function in MainActivity
I have example in my Github like this