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

Posted

技术标签:

【中文标题】更新android Room中实体的某些特定字段【英文标题】:Update some specific field of an entity in android Room 【发布时间】:2018-01-29 01:39:25 【问题描述】:

我正在为我的新项目使用 android 房间持久性库。 我想更新表格的某些字段。 我在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

【问题讨论】:

如何更新表中的列表。实际上我已经通过 TypeConverter 在表中插入了列表。但是,虽然带有更新,但它不起作用。请提出建议,如果您遇到任何此类问题。 @AmanGupta-ShOoTeR 您对上述评论有什么解决方案吗? 我的库 Kripton Persistence 库的工作方式与那个 Room 库非常相似。如果你想看看我是如何使用 Kripton 解决这个问题的,请访问abubusoft.com/wp/2019/10/02/… @AmanGupta-ShOoTeR 我在使用“@Query”进行更新时遇到了此类问题。然后我通过创建具有相同主键值而不是更新的对象来使用“@Insert(onConflict = OnConflictStrategy.REPLACE)”并且它起作用了 【参考方案1】:

我想知道如何更新一些字段(不是全部),例如方法 1 其中 id = 1

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

在我的情况下查询太长,因为我的实体中有很多字段

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

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

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

【讨论】:

是否可以在不传递任何参数的情况下更新表格?我的意思是交换一个布尔值。 @VishnuTB:如果您可以创建一个执行您想要的 SQL 语句,您应该可以将它与 @Query 一起使用。 @CommonsWare 明白了。我想获取一组具有布尔真值的行。但是房间拱门不允许我将真/假作为参数传递。而是true =1, false=0 SELECT * FROM note_table WHERE isNoteArchived == 0 Room 2.2.0-alpha01 (developer.android.com/jetpack/androidx/releases/…) 为@Update 引入了一个新参数,可让您设置目标实体。这应该可以进行部分更新。【参考方案2】:

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

【讨论】:

你能解释一下吗? 使用问题 DAO 方法中的实体将如下所示:@Query("UPDATE Tour SET endAddress = :end_address, startAdress = :start_address WHERE id = :tid) int updateTour(long tid, String end_address,字符串 start_address); 我的意思是在你的解决方案中 :) 最好让其他用户看到 哦,我认为这很明显。现已修复。【参考方案3】:

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

    您需要创建一个 dbManager 类,该类将在其构造函数中初始化数据库,并充当 viewModel 和 DAO 之间的中介,以及 .

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

    然后以这种方式更新您的用户项,假设您的 userId 为 1122,userName 为“xyz”,必须将其更改为“zyx”。

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

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

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

愉快的编码

【讨论】:

您真的在视图模型中执行数据库操作???上帝不请不... :'( 只是查看模型名称应该只是打你的脸 @mcfly,为什么认为在 View Model 中进行数据库操作不好? 单一责任原则被打破了是【参考方案4】:

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

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

【讨论】:

【参考方案5】:

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

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

@Entity
public class Tour 
    @ColumnInfo(name = "id")
    public long id;
    @ColumnInfo(name = "note")
    public String note;
    @ColumnInfo(name = "endAddress")
    public String endAddress;


public class TourUpdate 
    public long id;
    public String note;

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

【讨论】:

有趣的方式 这是在单个事务中更新多行和有限列的完美解决方案 根据the Room documentation,部分实体没有标注@Entity@ColumnInfo,但是可以使用普通的POJO。 但在未使用 @Entity@PrimaryKey 注释时会引发编译错误 @BabyishTank 这就是重点,这是用于数据库更新的。 endAddress 将在很长一段时间内保持它当前的值id,只有note 会被更新。【参考方案6】:

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

@Update 查询

基本上是给定的查询。无需进行一些新的查询。

@Dao
interface MemoDao 

    @Insert
    suspend fun insert(memo: Memo)

    @Delete
    suspend fun delete(memo: Memo)

    @Update
    suspend fun update(memo: Memo)

备忘录类

@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”。例如,如果您只想更新“标题”,则可以重用 来自已插入数据的“内容”和“照片”。 在实际代码中,这样使用

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

【讨论】:

还有什么需要做的吗?我有列信息名称,但按照示例 val memo = Memo(id, title, content, byteArrayList) 使用列信息名称对我不起作用。例如 Unresolved reference: dbMainLevel 其中 dbMainLevel 是列信息名称。【参考方案7】:

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

 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

【讨论】:

您调用 .getRemoteId(Id) 但您没有在那里传递任何 Id。顺便说一句,agruments 名称应该以小写开头【参考方案8】:

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

【讨论】:

【参考方案9】:

您可以使用 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);

【讨论】:

以上是关于更新android Room中实体的某些特定字段的主要内容,如果未能解决你的问题,请参考以下文章

JPA:仅更新特定字段

Android 数据存储-Room

Android Room 库错误:找不到字段的设置器。 (科特林)

Android Room:使用 Room 插入关系实体

Android jetpack room SQLLite 数据库 修改字段

将 Kotlin 内联类作为实体字段的房间数据库