更新 android 房间中实体的某些特定字段

Update some specific field of an entity in android Room

我正在为我的新项目使用 android 房间持久性库。 我想更新 table 的某些字段。 我在 Dao -

中尝试过
// Method 1:

@Dao
public interface TourDao {
    @Update
    int updateTour(Tour tour);
}

但是,当我尝试使用此方法进行更新时,它会更新与旅游对象的主键值匹配的实体的每个字段。 我用过@Query

// Method 2:

@Query("UPDATE Tour SET endAddress = :end_address WHERE id = :tid")
int updateTour(long tid, String end_address);

它正在工作,但在我的案例中会有很多查询,因为我的实体中有很多字段。我想知道如何更新某些字段(不是全部),例如 Method 1 where id = 1; (id 是自动生成的主键)。

// Entity:

@Entity
public class Tour {
    @PrimaryKey(autoGenerate = true)
    public long id;
    private String startAddress;
    private String endAddress;
    //constructor, getter and setter
}

I want to know how can I update some field(not all) like method 1 where id = 1

使用 @Query,就像您在方法 2 中所做的那样。

is too long query in my case because I have many field in my entity

然后有更小的实体。或者,不要单独更新字段,而是与数据库进行更粗粒度的交互。

IOW,Room 本身没有任何东西可以满足您的需求。

更新 2020-09-15:Room 现在有部分实体支持,这有助于解决这种情况。有关更多信息,请参阅

根据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);

示例:

实体:

@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
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);
}

如果您需要更新特定用户 ID 的用户信息 "x",

  1. 您需要创建一个 dbManager class 它将在其构造函数中初始化数据库并充当您的 viewModel 和 DAO 之间的中介,以及 .
  2. ViewModel会初始化一个dbManager实例来访问数据库。 代码应如下所示:

       @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);
    

    然后就这样更新你的用户项,假设你的用户 ID 是 1122,用户名是 "xyz",必须更改为 "zyx"。

    获取id为1122的用户对象的userItem

User user = new user();
 if(user.getUserId() == 1122){
   user.setuserName("zyx");
   userViewModel.updateUser(user);
 }

这是一个原始代码,希望对您有所帮助。

编码愉快

你可以试试这个,但性能可能会差一点:

@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);
    }
}

自 2019 年 10 月发布的 Room 2.2.0 起,您可以指定更新的目标实体。那么如果更新参数不同,Room 只会更新部分实体列。 OP 问题的示例将更清楚地说明这一点。

@Update(entity = Tour::class)
fun update(obj: TourUpdate)

@Entity
public class TourUpdate {
    @ColumnInfo(name = "id")
    public long id;
    @ColumnInfo(name = "endAddress")
    private String endAddress;
}

请注意,您必须创建一个名为 TourUpdate 的新部分实体,以及问题中的真实 Tour 实体。现在,当您使用 TourUpdate 对象调用更新时,它将更新 endAddress 并保持 startAddress 值不变。这非常适合我在我的 DAO 中使用 insertOrUpdate 方法的用例,该方法使用来自 API 的新远程值更新数据库,但将本地应用程序数据单独留在 table 中。

我认为您不需要只更新某些特定字段。 只需更新整个数据。

@Update 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>?
)

您唯一需要知道的是 'id'。例如,如果您只想更新 'title',您可以从已插入的数据中重用 'content' 和 'photo' 在实际代码中,像这样使用

val memo = Memo(id, title, content, byteArrayList)
memoViewModel.update(memo)

我们需要您要更新的特定模型的主键。 例如:

 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))
}

在这个函数中,我正在检查数据库中是否存在特定条目,如果存在,则将 id 主键拉到这里并执行更新功能。

这是用于获取和更新记录的:

@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

在尝试解决我自己的类似问题后,我从 @PrimaryKey(autoGenerate = true) 更改为 int UUID,我找不到如何编写迁移,所以我更改了 table 名称,这是一个简单的修复,如果你使用 personal/small 应用程序

您可以使用 URI 按 ID 更新数据库中的行

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);

您还应该添加到您的更新方法 OnConflictStrategy

@Update(onConflict = OnConflictStrategy.REPLACE)
int updateTour(Tour tour);