204

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
}
Tur1ng
  • 745
  • 8
  • 22
Rasel
  • 5,488
  • 3
  • 30
  • 39
  • How to update a list in Table. Actually i have inserted the list in Table by TypeConverter. But while coming with update it is not working. Please suggest, if you have faced any issue like this. – Aman Gupta - ΔMΔN Jan 15 '18 at 07:31
  • @AmanGupta-ShOoTeR Did you get any solution for the comment above? – skygeek Oct 05 '18 at 07:10
  • My library Kripton Persistence Library works quite similar to that Room library. If you want to have a look at how I resolve this problem using Kripton, please visit https://www.abubusoft.com/wp/2019/10/02/from-stackoverflow-update-some-specific-field-of-an-entity/ – xcesco Oct 02 '19 at 08:52
  • @AmanGupta-ShOoTeR I faced such kinds of issue on update using '@Query'. Then I used '@Insert(onConflict = OnConflictStrategy.REPLACE)' by creating an object with same primary key value instead of update and it worked – Rasel Dec 03 '19 at 04:04

8 Answers8

162

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);
}
Ahmed Hegazy
  • 12,395
  • 5
  • 41
  • 64
Jurij Pitulja
  • 5,546
  • 4
  • 19
  • 25
111

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.

georgiecasey
  • 21,793
  • 11
  • 65
  • 74
  • 2
    interesting way – Rasel Aug 28 '20 at 02:58
  • 1
    this is perfect solution to update multiple row with limited column in single transaction – Saurabh Bhandari Sep 18 '20 at 09:38
  • 5
    According to [the Room documentation](https://developer.android.com/reference/androidx/room/Update), the partial entity is not annotated with `@Entity` or `@ColumnInfo`, but you can use a normal POJO. – noe May 31 '21 at 10:06
  • but it throws compilation error when not annotated with `@Entity` and `@PrimaryKey` – Mihir Patel Jun 28 '21 at 07:02
  • the best is create a `data class` – junior Sep 14 '21 at 15:47
  • 1
    "Now when you call update with a TourUpdate object, it will update endAddress " But the TourUpdate class is missing endAddress field how does it still get update? – BabyishTank Nov 24 '21 at 21:49
  • 1
    @BabyishTank that's the whole point, this is for DB updates. The endAddress will keep the value it currently has for that long `id`, only `note` will be updated. – georgiecasey Nov 24 '21 at 22:02
  • 1
    @BabyishTank actually looking at this now, I accepted an incorrect edit from another user which made no sense. I reverted to my original answer – georgiecasey Feb 05 '22 at 00:01
  • `@Entity` is unnecessary – Farshad Mar 06 '22 at 17:28
  • Can we have a java version of this answer? – AtomicallyBeyond Mar 19 '22 at 07:11
  • @noe "the Room documentation" has a java version, but it's actually Kotlin! – AtomicallyBeyond Mar 19 '22 at 07:37
  • @noe what are you talking about? you have to specify column info if it's different from field of model. For example in the table you have `end_address`, but field is `endAddress`. So of course you have to specify it for partial entity as well or how is it supposed to know which column to use? You misunderstood the documentation. But yes entity annotation is not needed for partial entities, it's exactly the same as for embedded entitles, it's logical – user924 Aug 15 '22 at 19:47
  • I understand this solution but the issue I am facing with this is that: The main entity class `Tour` in this case has some auto generated `id` and the new entity class `TourUpdate` has a `id` for itself and because of that it ends up creating a new row. How do I make changes to the same row, considering I don't know the `id` because it's automatically generated. – oyeraghib Nov 04 '22 at 11:49
90

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.

CommonsWare
  • 986,068
  • 189
  • 2,389
  • 2,491
  • Is it possible to update the table without passing any parameters? I mean swapping a boolean value. – Vishnu T B Aug 05 '19 at 09:21
  • 1
    @VishnuTB: If you can create a SQL statement that does what you want, you should be able to use it with `@Query`. – CommonsWare Aug 05 '19 at 11:11
  • @CommonsWare got it. I wanted to fetch a set of rows with a boolean true value. but Room arch didn't allow me to pass true/false as parameter. instead `true =1`, `false=0` `SELECT * FROM note_table WHERE isNoteArchived == 0` – Vishnu T B Aug 07 '19 at 09:42
  • 4
    Room 2.2.0-alpha01 (https://developer.android.com/jetpack/androidx/releases/room#2.2.0-alpha01) introduced a new parameter for @Update which lets you set the target entity. This should make partial updates possible. – Jonas Aug 14 '19 at 12:21
12

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);
    }
}
SUPERYAO
  • 326
  • 3
  • 11
7

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)
J.Dragon
  • 699
  • 8
  • 16
  • is there anything else that needs doing? I have column info names but using the column info name as per example val memo = Memo(id, title, content, byteArrayList) does not work for me. for example Unresolved reference: dbMainLevel where dbMainLevel is the column info name. – UnknownError Jan 13 '21 at 19:49
0

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
Shivegeeky
  • 183
  • 1
  • 7
  • you call .getRemoteId(Id) but you do not pass any Id there. And btw agruments names should start with lower case – Waldmann Oct 02 '21 at 09:42
-1

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);
B3ACOC
  • 1
  • 1
  • 1
-3

If you need to update user information for a specific user ID "x",

  1. 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 .
  2. 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

Amit Prajapati
  • 13,525
  • 8
  • 62
  • 84
Kunal Parte
  • 199
  • 1
  • 9