You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用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:

  1. 定义类型转换器类:
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);
    }
}
  1. 在你的RoomDatabase类上添加转换器注解:
@Database(entities = {DataModel.class}, version = 1)
@TypeConverters(DataConverter.class)
public abstract class AppDatabase extends RoomDatabase {
    // 原有Dao定义保留即可
}

该方案不推荐常规场景使用,存储为JSON后后续对单条用户数据的增删改查都会非常麻烦。


内容的提问来源于stack exchange,提问作者gopssays

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.05 19:45:00