Android Room @Upsert注解触发SQLite唯一约束异常排查
Android Room @Upsert触发唯一约束异常的原因及解决方法
问题根源
Room的@Upsert默认仅以实体类的主键作为冲突判断依据。你的CoinStatus实体中,主键是自增的primaryKey,而唯一约束定义在coin_query_id字段上。当调用updateFavorites时,传入的CoinStatus对象的primaryKey通常是默认值(比如0),Room会判定这是新实体并执行插入操作,而此时coin_query_id已存在于数据库中,直接触发唯一约束冲突异常。
解决方案
方案一:将coin_query_id设为主键(推荐,逻辑更清晰)
既然coin_query_id本身是唯一的,直接把它设为主键,去掉多余的自增主键。这样@Upsert会自动基于这个唯一主键判断冲突:
@Entity(tableName = "coin_status") public class CoinStatus { @PrimaryKey @ColumnInfo(name = "coin_query_id") private String coinQueryId; @ColumnInfo(name = "is_favorite") private boolean isFavorite; public CoinStatus(String coinQueryId, boolean isFavorite) { this.coinQueryId = coinQueryId; this.isFavorite = isFavorite; } // 省略getter和setter }
修改后,@Upsert会正常工作:存在对应coin_query_id的记录时更新,不存在时插入。
方案二:指定@Upsert的冲突判断列(Room 2.4.0+可用)
如果必须保留自增主键,可在@Upsert注解中指定conflictColumns参数,明确以coin_query_id作为冲突判断依据:
@Dao public interface CoinStatusDao { @Upsert(conflictColumns = {"coin_query_id"}) Single<List<Long>> updateFavorites(List<CoinStatus> favoriteCoins); }
注意:该特性要求Room版本≥2.4.0,若你的Room版本较低,此方案不可用。
方案三:自定义SQL实现Upsert(兼容所有Room版本)
如果上述方案都不适用,可直接用INSERT OR REPLACE的SQL语句手动实现Upsert逻辑:
@Dao public interface CoinStatusDao { // 单条数据Upsert @Query("INSERT OR REPLACE INTO coin_status (coin_query_id, is_favorite) VALUES (:coinQueryId, :isFavorite)") int upsertSingleFavorite(String coinQueryId, boolean isFavorite); // 批量Upsert(需配合事务) @Transaction default List<Integer> upsertFavorites(List<CoinStatus> favoriteCoins) { List<Integer> resultList = new ArrayList<>(); for (CoinStatus status : favoriteCoins) { int affectedRows = upsertSingleFavorite(status.getCoinQueryId(), status.isFavorite()); resultList.add(affectedRows); } return resultList; } }
内容的提问来源于stack exchange,提问作者WHOATEMYNOODLES
相关产品推荐
相关产品推荐

