Update some specific field of an entity in android Room
AndroidSql UpdateAndroid RoomAndroid Problem Overview
I am using android room persistence library for my new project.
I want to update some field of table.
I have tried like in my Dao
-
// Method 1:
@Dao
public interface TourDao {
@Update
int updateTour(Tour tour);
}
But when I try to update using this method then it updates every field of the entity where it matches primary key value of tour object.
I have used @Query
// Method 2:
@Query("UPDATE Tour SET endAddress = :end_address WHERE id = :tid")
int updateTour(long tid, String end_address);
It is working but there will be many queries in my case because I have many fields in my entity. I want to know how can I update some field (not all) like Method 1
where id = 1; (id is the auto generate primary key).
// Entity:
@Entity
public class Tour {
@PrimaryKey(autoGenerate = true)
public long id;
private String startAddress;
private String endAddress;
//constructor, getter and setter
}
Android Solutions
Solution 1 - Android
According to SQLite Update Docs :
<!-- language: lang-java -->
@Query("UPDATE tableName SET
field1 = :value1,
field2 = :value2,
...
//some more fields to update
...
field_N= :value_N
WHERE id = :id)
int updateTour(long id,
Type value1,
Type value2,
... ,
// some more values here
... ,
Type value_N);
Example:
Entity:
@Entity(tableName = "orders")
public class Order {
@NonNull
@PrimaryKey
@ColumnInfo(name = "order_id")
private int id;
@ColumnInfo(name = "order_title")
private String title;
@ColumnInfo(name = "order_amount")
private Float amount;
@ColumnInfo(name = "order_price")
private Float price;
@ColumnInfo(name = "order_desc")
private String description;
// ... methods, getters, setters
}
Dao:
@Dao
public interface OrderDao {
@Query("SELECT * FROM orders")
List<Order> getOrderList();
@Query("SELECT * FROM orders")
LiveData<List<Order>> getOrderLiveList();
@Query("SELECT * FROM orders WHERE order_id =:orderId")
LiveData<Order> getLiveOrderById(int orderId);
/**
* Updating only price
* By order id
*/
@Query("UPDATE orders SET order_price=:price WHERE order_id = :id")
void update(Float price, int id);
/**
* Updating only amount and price
* By order id
*/
@Query("UPDATE orders SET order_amount = :amount, price = :price WHERE order_id =:id")
void update(Float amount, Float price, int id);
/**
* Updating only title and description
* By order id
*/
@Query("UPDATE orders SET order_desc = :description, order_title= :title WHERE order_id =:id")
void update(String description, String title, int id);
@Update
void update(Order order);
@Delete
void delete(Order order);
@Insert(onConflict = REPLACE)
void insert(Order order);
}
Solution 2 - Android
> I want to know how can I update some field(not all) like method 1 where id = 1
Use @Query
, as you did in Method 2.
> is too long query in my case because I have many field in my entity
Then have smaller entities. Or, do not update fields individually, but instead have more coarse-grained interactions with the database.
IOW, there is nothing in Room itself that will do what you seek.
UPDATE 2020-09-15: Room now has partial entity support, which can help with this scenario. See this answer for more.
Solution 3 - Android
As of Room 2.2.0 released October 2019, you can specify a Target Entity for updates. Then if the update parameter is different, Room will only update the partial entity columns. An example for the OP question will show this a bit more clearly.
@Update(entity = Tour::class)
fun update(obj: TourUpdate)
@Entity
public class TourUpdate {
@ColumnInfo(name = "id")
public long id;
@ColumnInfo(name = "endAddress")
private String endAddress;
}
Notice you have to a create a new partial entity called TourUpdate, along with your real Tour entity in the question. Now when you call update with a TourUpdate object, it will update endAddress and leave the startAddress value the same. This works perfect for me for my usecase of an insertOrUpdate method in my DAO that updates the DB with new remote values from the API but leaves the local app data in the table alone.
Solution 4 - Android
You could try this, but performance may be worse a little:
@Dao
public abstract class TourDao {
@Query("SELECT * FROM Tour WHERE id == :id")
public abstract Tour getTour(int id);
@Update
public abstract int updateTour(Tour tour);
public void updateTour(int id, String end_address) {
Tour tour = getTour(id);
tour.end_address = end_address;
updateTour(tour);
}
}
Solution 5 - Android
I think you don't need to update only some specific field. Just update whole data.
> @Update query
It is a given query basically. No need to make some new query.
@Dao
interface MemoDao {
@Insert
suspend fun insert(memo: Memo)
@Delete
suspend fun delete(memo: Memo)
@Update
suspend fun update(memo: Memo)
}
Memo.class
@Entity
data class Memo (
@PrimaryKey(autoGenerate = true) val id: Int,
@ColumnInfo(name = "title") val title: String?,
@ColumnInfo(name = "content") val content: String?,
@ColumnInfo(name = "photo") val photo: List<ByteArray>?
)
Only thing you need to know is 'id'. For instance, if you want to update only 'title', you can reuse 'content' and 'photo' from already inserted data. In real code, use like this
val memo = Memo(id, title, content, byteArrayList)
memoViewModel.update(memo)
Solution 6 - Android
We need the primary key of that particular model that you want to update. For example:
private fun update(Name: String?, Brand: String?) {
val deviceEntity = remoteDao?.getRemoteId(Id)
if (deviceEntity == null)
remoteDao?.insertDevice(DeviceEntity(DeviceModel = DeviceName, DeviceBrand = DeviceBrand))
else
DeviceDao?.updateDevice(DeviceEntity(deviceEntity.id,remoteDeviceModel = DeviceName, DeviceBrand = DeviceBrand))
}
In this function, I am checking whether a particular entry exists in the database if exists pull the primary key which is id over here and perform update function.
This is the for fetching and update records:
@Query("SELECT * FROM ${DeviceDatabase.DEVICE_TABLE_NAME} WHERE ${DeviceDatabase.COLUMN_DEVICE_ID} = :DeviceId LIMIT 1")
fun getRemoteDeviceId(DeviceId: String?): DeviceEntity
@Update(onConflict = OnConflictStrategy.REPLACE)
fun updatDevice(item: DeviceEntity): Int
Solution 7 - Android
You can update row in database by id using URI
Tour tourEntity = new Tour();
tourEntity.end_address = "some adress";
tourEntity.start_address= "some adress";
//tourEntity..... other fields
tourEntity.id = ContentUris.parseId(Uri.parse("content://" + BuildConfig.APPLICATION_ID + File.separator + id));
//get your updatemethod with abstract func in your database class (or with another way, wich you use in project)
int tourDaoUpdate = getInstance(context).tour().update(tourEntity);
You should also add to your update method OnConflictStrategy
@Update(onConflict = OnConflictStrategy.REPLACE)
int updateTour(Tour tour);
Solution 8 - Android
after trying to fix a similar problem my self, where I had changed from @PrimaryKey(autoGenerate = true)
to int UUID
, I couldn't find how to write my migration so I changed the table name, it's an easy fix, and ok if you working with a personal/small app
Solution 9 - Android
If you need to update user information for a specific user ID "x",
-
you need to create a dbManager class that will initialise the database in its constructor and acts as a mediator between your viewModel and DAO, and also .
-
The ViewModel will initialize an instance of dbManager to access the database. The code should look like this:
@Entity class User{ @PrimaryKey String userId; String username; } Interface UserDao{ //forUpdate @Update void updateUser(User user) } Class DbManager{ //AppDatabase gets the static object o roomDatabase. AppDatabase appDatabase; UserDao userDao; public DbManager(Application application ){ appDatabase = AppDatabase.getInstance(application); //getUserDao is and abstract method of type UserDao declared in AppDatabase //class userDao = appDatabase.getUserDao(); } public void updateUser(User user, boolean isUpdate){ new InsertUpdateUserAsyncTask(userDao,isUpdate).execute(user); } public static class InsertUpdateUserAsyncTask extends AsyncTask<User, Void, Void> { private UserDao userDAO; private boolean isInsert; public InsertUpdateBrandAsyncTask(BrandDAO userDAO, boolean isInsert) { this. userDAO = userDAO; this.isInsert = isInsert; } @Override protected Void doInBackground(User... users) { if (isInsert) userDAO.insertBrand(brandEntities[0]); else //for update userDAO.updateBrand(users[0]); //try { // Thread.sleep(1000); //} catch (InterruptedException e) { // e.printStackTrace(); //} return null; } } } Class UserViewModel{ DbManager dbManager; public UserViewModel(Application application){ dbmanager = new DbMnager(application); } public void updateUser(User user, boolean isUpdate){ dbmanager.updateUser(user,isUpdate); } } Now in your activity or fragment initialise your UserViewModel like this: UserViewModel userViewModel = ViewModelProviders.of(this).get(UserViewModel.class);
Then just update your user item this way, suppose your userId is 1122 and userName is "xyz" which has to be changed to "zyx".
Get an userItem of id 1122 User object
> User user = new user(); > if(user.getUserId() == 1122){ > user.setuserName("zyx"); > userViewModel.updateUser(user); > }
This is a raw code, hope it helps you.
Happy coding