【问题标题】:Update a Record if exists, else insert a new record in Room如果存在则更新记录,否则在 Room 中插入新记录
【发布时间】:2026-02-08 08:40:01
【问题描述】:

我是 Room、Rxjava 和其他 android 架构组件的新手。我正在尝试更新/插入 2 条记录(如果行已经存在,请更新它。否则插入一个新行。)我尝试按以下方式进行操作。但是,我没有工作。

游戏活动:

public class GameActivity extends AppCompatActivity {

...

    public void onGameWinnerChanged(Player winner) {


    mDisposable.add(gameViewModel.updateDb(winner)
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
            .subscribe(() -> Log.e("Success!!!", "updated records")
                    , throwable -> {
                throwable.printStackTrace();
            }));        
      }
}

GameViewModel:

public class GameViewModel extends ViewModel {

    ...

    public Completable updateDb(Player winner) {

        return Completable.fromAction(() -> {
            updateWinner(winner);
            Player loser = game.player1 == winner ? game.player2 : game.player1;
            updateLoser(loser);
        });
    }

    private void updateLoser(Player loser) {

           User user = userDataSource.getSingleRecordFromName(loser.name);
        if (user != null) {
            user.loss++;
            userDataSource.updateRecord(user);
        } else {
            user = new User(loser.name, "", 0, 0, 1);
            userDataSource.insertOrUpdateUser(user);
        }
    }

    private void updateWinner(Player winner) {

      User user = userDataSource.getSingleRecordFromName(winner.name);
      if (user != null) {
            user.wins++;
            userDataSource.updateRecord(user);
      } else {
            user = new User(winner.name, "", 0, 1, 0);
            userDataSource.insertOrUpdateUser(user);
      }
   }
}

LocalUserDataSource

public class LocalUserDataSource implements UserDataSource {

    private DaoAccess daoAccess;

    public LocalUserDataSource(DaoAccess daoAccess){
        this.daoAccess=daoAccess;
    }

    @Override
    public Flowable<List<User>> getUsers() {
        return daoAccess.fetchAllData();
    }

    @Override
    public void insertOrUpdateUser(User user) {
        Log.e("local user ds","insertOrUpdateUser");
        daoAccess.insertOnlySingleRecord(user);
    }

    @Override
    public User getSingleRecordFromName(String strName) {
        return daoAccess.getSingleRecord(strName);
    }


    @Override
    public void updateRecord(User user) {
        daoAccess.updateRecord(user);
    }

    @Override
    public void deleteRecord(User user) {
        daoAccess.deleteRecord(user);
    }
}

DaoAccess

@Dao
public interface DaoAccess  {

    @Insert
    void insertOnlySingleRecord(User user);

    @Query("SELECT * FROM User")
    Flowable<List<User>> fetchAllData();

    @Query("SELECT * FROM User WHERE name =:strName")
    User getSingleRecord(String strName);

    @Update
    void updateRecord(User user);

    @Delete
    void deleteRecord(User user);
}

问题是,当我尝试运行时,如果我尝试在 GameViewModel 类的下一行插入任何不在数据库中的用户,则会出现 NullPointer 异常。

User user = userDataSource.getSingleRecordFromName(loser.name);

谁能告诉我哪里错了或者我该怎么办?

编辑

我想要做的是首先获取一个用户,如果它在数据库中,更新递增或递减分数(如果获胜者则 +1 获胜列,如果失败者则 +1 损失列)。 如果用户在表中不存在,则创建一个新条目。

【问题讨论】:

  • @Insert(onConflict = OnConflictStrategy.REPLACE) void insertOnlySingleRecord(User user); 你可以像这样更新或替换。在你的`DaoAccess`中
  • @KuLdipPaTel 请查看已编辑的问题
  • @KuLdipPaTel 那么,在这种情况下获取单个记录的查询应该是什么?
  • 首先您要防止插入重复条目,其次如果您的玩家名称包含空格,您必须在 getSingleRecord 查询中更改。
  • 你能解释更多吗?我将如何实现这一目标? @KuLdipPaTel

标签: android rx-java2 android-room android-mvvm


【解决方案1】:

您的用户道应如下所示

@Dao
public interface DaoAccess  {

    @Insert(onConflict = OnConflictStrategy.REPLACE)
    void insertOnlySingleRecord(User user);

    @Query("SELECT * FROM User")
    Flowable<List<User>> fetchAllData();

    @Query("SELECT * FROM User WHERE lower(name) = lower(:strName) limit 1")
    User getSingleRecord(String strName);

    @Update
    void updateRecord(User user);

    @Delete
    void deleteRecord(User user);
}

你的用户实体应该像

 @Entity(tableName = "user_table", indices = @Index(value = {"name"}, unique = true))
public class UserEntity {

    @PrimaryKey(autoGenerate = true)
    private int id;

    @ColumnInfo(name = "name")
    private String name;
 }

【讨论】:

  • 谢谢。我会做。但是,在这种情况下,我的问题有所不同。我想首先获取一个用户,如果它在数据库中,更新递增或递减分数(如果获胜者则 +1 获胜列,如果失败者则 +1 损失列)。如果用户在表中不存在,则创建一个新条目。
  • 这里,indices = @Index(value = {"name"} 是什么意思?
  • 如果冲突,它将插入唯一的用户名,它将替换或更新为以前的条目。
  • 好的。再次感谢。
  • 好的。明白你的意思了。但是,就我而言,我也想更新另一个字段(赢/输)。(分数的增加)。那么如果不从表中获取用户,我将如何知道当前分数并增加它。
【解决方案2】:

您可以使用此查询简单地检查数据库中是否存在数据

// It will return **TRUE** if user exist in User Table else return **FALSE** 

@Query("SELECT * FROM User WHERE name == :strName")
    fun isUserExist(strName: String): Boolean

// It will return **user count** if user exist in User Table else return **0**

@Query("SELECT * FROM User WHERE name == :strName")
    fun isUserExist(strName: String): Int

// It will return **User** if user exist in User Table else return **null**

@Query("SELECT * FROM User WHERE name == :strName")
    fun isUserExist(strName: String): User

【讨论】:

  • 我们无法在主线程上查询,但是当我使用线程或协程或异步时,它曾经需要一点时间,所以我的 if else 条件失败,因为我正在检查项目是否存在或不存在于数据库中(如果存在)因此,如果不存在则不存储该值,然后将该值存储在 DB 中。我怎样才能摆脱这个问题?