使用Retrofit拉取数据存入RoomDatabase出现字段存储报错如何解决
报错原因说明
你修改后的DataModel中加入了List<Datum> data字段,Room默认不支持直接存储集合、自定义对象类型的字段,SQLite本身仅支持基本数据类型、字符串、二进制等存储类型,无法直接映射List结构,因此抛出该错误。
除此之外你原有代码还存在Retrofit解析逻辑错误:目标接口返回的是外层携带分页信息的对象,并非直接的用户数组,原有Api接口定义的Call<List<DataModel>>返回值也会直接解析失败。
推荐实现方案(符合数据库设计范式)
不需要把接口返回的外层结构直接作为Room实体,拆分数据类分别负责网络解析和数据库存储即可:
步骤1:拆分数据类
首先定义接口响应包装类,仅用于Retrofit解析,不需要加Room的@Entity注解:
public class UserResponse { @SerializedName("page") public Integer page; @SerializedName("per_page") public Integer perPage; @SerializedName("total") public Integer total; @SerializedName("total_pages") public Integer totalPages; @SerializedName("data") public List<DataModel> data; // 这里的DataModel对应你最开始写的用户实体类 }
保留你最开始写的用户实体类即可,无需修改:
@Entity(tableName = "userdata",indices = @Index(value = {"id"},unique = true)) public class DataModel { @PrimaryKey@NonNull @SerializedName("id") @ColumnInfo(name = "id") int id; @SerializedName("email") @ColumnInfo(name = "email") String email; @SerializedName("first_name") @ColumnInfo(name = "first_name") String first_name; @SerializedName("last_name") @ColumnInfo(name = "last_name") String last_name; @SerializedName("avatar") @ColumnInfo(name = "avatar") String avatar; // 原有getter、setter逻辑全部保留即可 }
步骤2:修改ApiInterface定义
调整返回值类型为我们刚定义的响应包装类,同时修正拼写错误、去掉多余的问号:
public interface UserApi { @GET("/api/users") Call<UserResponse> getAllUsers( @Query("per_page") int per_page ); }
步骤3:修改网络请求逻辑
解析成功后取出响应里的用户列表再存入Room,同时设置per_page=3:
private void makeRequest() { Retrofit retrofit=new Retrofit.Builder() .baseUrl("https://reqres.in") .addConverterFactory(GsonConverterFactory.create()) .build(); UserApi api=retrofit.create(UserApi.class); Call<UserResponse> call=api.getAllUsers(3); // 传入需求要求的per_page=3 call.enqueue(new Callback<UserResponse>() { @Override public void onResponse(Call<UserResponse> call, Response<UserResponse> response) { if (response.isSuccessful() && response.body() != null) { // 取用户列表存入Room repository.insert(response.body().data); } } @Override public void onFailure(Call<UserResponse> call, Throwable t) { Log.d("main", "onFailure: "+t.getMessage()); } }); }
可选方案:需要存储分页信息到数据库
如果业务要求必须把page、total等分页信息也存入本地,可以通过添加TypeConverter实现,把List转成JSON字符串存储,读取时再转回List:
- 定义类型转换器类:
public class DataConverter { Gson gson = new Gson(); @TypeConverter public String listToJson(List<DataModel> data) { return gson.toJson(data); } @TypeConverter public List<DataModel> jsonToList(String data) { Type listType = new TypeToken<List<DataModel>>() {}.getType(); return gson.fromJson(data, listType); } }
- 在你的RoomDatabase类上添加转换器注解:
@Database(entities = {DataModel.class}, version = 1) @TypeConverters(DataConverter.class) public abstract class AppDatabase extends RoomDatabase { // 原有Dao定义保留即可 }
该方案不推荐常规场景使用,存储为JSON后后续对单条用户数据的增删改查都会非常麻烦。
内容的提问来源于stack exchange,提问作者gopssays
相关产品推荐
相关产品推荐

