1

如果有像我下面的 JSON 结构这样的结构,我们应该如何创建实体类?没有这方面的例子。虽然在很久以前写的文章中,@embeded 用于内部数组,但现在使用了像转换器这样的结构。我们应该使用哪一个?这些有什么作用?如何创建我的类型的结构?请帮助Java

所有需要的结构都可以在这里找到:https ://github.com/theoyuncu8/roomdb

JSON数据


{
"MyData": [
 {
   "food_id": "1",
   "food_name": "Food 1",
   "food_image": "imageurl",
   "food_kcal": "32",
   "food_url": "url",
   "food_description": "desc",
   "carb_percent": "72",
   "protein_percent": "23",
   "fat_percent": "4",
   "units": [
     {
       "unit": "Unit A",
       "amount": "735.00",
       "calory": "75.757",
       "calcium": "8.580",
       "carbohydrt": "63.363",
       "cholestrl": "63.0",
       "fiber_td": "56.12",
       "iron": "13.0474",
       "lipid_tot": "13.01",
       "potassium": "11.852",
       "protein": "717.1925",
       "sodium": "112.02",
       "vit_a_iu": "110.7692",
       "vit_c": "110.744"
     },
     {
       "unit": "Unit C",
       "amount": "32.00",
       "calory": "23.757",
       "calcium": "53.580",
       "carbohydrt": "39.363",
       "cholestrl": "39.0",
       "fiber_td": "93.12",
       "iron": "93.0474",
       "lipid_tot": "93.01",
       "potassium": "9.852",
       "protein": "72.1925",
       "sodium": "10.0882",
       "vit_a_iu": "80.7692",
       "vit_c": "80.744"
     }
   ]
 },
 {
   "food_id": "2",
   "food_name": "Food 2",
   "food_image": "imageurl",
   "food_kcal": "50",
   "food_url": "url",
   "food_description": "desc",
   "carb_percent": "25",
   "protein_percent": "14",
   "fat_percent": "8",
   "units": [
     {
       "unit": "Unit A",
       "amount": "25.00",
       "calory": "25.757",
       "calcium": "55.580",
       "carbohydrt": "53.363",
       "cholestrl": "53.0",
       "fiber_td": "53.12",
       "iron": "53.0474",
       "lipid_tot": "53.01",
       "potassium": "17.852",
       "protein": "757.1925",
       "sodium": "122.02",
       "vit_a_iu": "10.7692",
       "vit_c": "10.744"
     },
     {
       "unit": "Unit C",
       "amount": "2.00",
       "calory": "2.757",
       "calcium": "5.580",
       "carbohydrt": "3.363",
       "cholestrl": "3.0",
       "fiber_td": "3.12",
       "iron": "3.0474",
       "lipid_tot": "3.01",
       "potassium": "77.852",
       "protein": "77.1925",
       "sodium": "12.02",
       "vit_a_iu": "0.7692",
       "vit_c": "0.744"
     },
     {
       "unit": "Unit G",
       "amount": "1.00",
       "calory": "2.1",
       "calcium": "0.580",
       "carbohydrt": "0.363",
       "cholestrl": "0.0",
       "fiber_td": "0.12",
       "iron": "0.0474",
       "lipid_tot": "0.01",
       "potassium": "5.852",
       "protein": "0.1925",
       "sodium": "1.02",
       "vit_a_iu": "0.7692",
       "vit_c": "0.744"
     }
   ]
 }
]
}

实体类

食品类

public class Foods {
    @SerializedName("food_id")
    @Expose
    private String foodId;
    @SerializedName("food_name")
    @Expose
    private String foodName;
    @SerializedName("food_image")
    @Expose
    private String foodImage;
    @SerializedName("food_kcal")
    @Expose
    private String foodKcal;
    @SerializedName("food_url")
    @Expose
    private String foodUrl;
    @SerializedName("food_description")
    @Expose
    private String foodDescription;
    @SerializedName("carb_percent")
    @Expose
    private String carbPercent;
    @SerializedName("protein_percent")
    @Expose
    private String proteinPercent;
    @SerializedName("fat_percent")
    @Expose
    private String fatPercent;

// here

    @SerializedName("units")
    @Expose
    private List<FoodUnitsData> units = null;

    // getter setter

}

FoodUnitsData 类

public class FoodUnitsData {
   @SerializedName("unit")
   @Expose
   private String unit;
   @SerializedName("amount")
   @Expose
   private String amount;
   @SerializedName("calory")
   @Expose
   private String calory;
   @SerializedName("calcium")
   @Expose
   private String calcium;
   @SerializedName("carbohydrt")
   @Expose
   private String carbohydrt;
   @SerializedName("cholestrl")
   @Expose
   private String cholestrl;
   @SerializedName("fiber_td")
   @Expose
   private String fiberTd;
   @SerializedName("iron")
   @Expose
   private String iron;
   @SerializedName("lipid_tot")
   @Expose
   private String lipidTot;
   @SerializedName("potassium")
   @Expose
   private String potassium;
   @SerializedName("protein")
   @Expose
   private String protein;
   @SerializedName("sodium")
   @Expose
   private String sodium;
   @SerializedName("vit_a_iu")
   @Expose
   private String vitAIu;
   @SerializedName("vit_c")
   @Expose
   private String vitC;


   // getter setter
}

4

2 回答 2

1

这些有什么作用?

TypeConverters用于将 room 无法处理的类型转换为它可以处理的类型(字符串、基元、整数类型如 Integer、Long、十进制类型如 Double、Float)。

@Embedded基本上说将@Embedded 类的成员变量包含为列。例如@Embedded FoodUnitsData foodUnitsData;

从 Room 角度测试/验证模式

使用上面的类和用@Database ( FoodDatabase ) 注释的类中定义的实体,编译/构建项目并修复房间抱怨的任何东西是一个好主意(在这种情况下没有)。

所以FoodDataabse是:-

@Database(entities = {Foods.class, FoodUnitsDataEntity.class /*<<<<<<<<<< ADDED*/}, version = 1)
public abstract class FoodDatabase extends RoomDatabase {
    public abstract DaoAccess daoAccess(); //* do not inlcude this line until the DaoAccess class has been created
}
  • 注意见评论re DaoAccess(即注释掉该行)

然后 CTRL + F9 并检查构建日志

第四道访问

显然 FoodUnitsDataEntity 行需要添加、更新和删除。如果 Foods 对象可以驱动将 FoodUnitsDataEntity 行全部添加到一起,那也将非常方便。这需要一个带有主体的方法,因此 DaoAccess 从接口更改为抽象类以方便这种方法。

我们应该使用哪一个?

您的主要问题是FoodUnitsData列表

尽管您可以转换 List 并使用 TypeConverter,但我不建议这样做。

  • 您可能会转换为 JSON 字符串(因此您从 JSON 中提取到对象,然后将嵌入的对象存储为 JSON)。您使数据膨胀,也使使用该数据变得困难。

  • 例如,假设您想搜索含有 1000 卡路里或更多卡路里的食物,这将需要非常复杂的查询,或者您将加载所有数据库,然后循环遍历食物和单位。

我会说这@Embedded是使用的方法。连同使用@Ignore(相反,即排除成员变量作为列)。即你会@Ignore Foods 类中的列表。

  • @Embedded然后,您可以轻松地在查询中使用单个值。

  • 然后你可以做类似的事情SELECT * FROM the_table_used_for_the_foodunitsdata WHERE calory > 1000,你会得到一个返回的 FoodUnitsData 列表。SQLite 将非常有效地做到这一点。

工作示例

因此,将上述内容放入一个工作示例中:-

首先是Foods类并添加 @Ignore 注释:-

@Entity(tableName = "food_data") // ADDED to make it usable as a Room table
public class Foods {
    @SerializedName("food_id")
    @Expose
    @PrimaryKey // ADDED as MUST have a primary key
    @NonNull // ADDED Room does not accept NULLABLE PRIMARY KEY
    private String foodId;
    @SerializedName("food_name")
    @Expose
    private String foodName;
    @SerializedName("food_image")
    @Expose
    private String foodImage;
    @SerializedName("food_kcal")
    @Expose
    private String foodKcal;
    @SerializedName("food_url")
    @Expose
    private String foodUrl;
    @SerializedName("food_description")
    @Expose
    private String foodDescription;
    @SerializedName("carb_percent")
    @Expose
    private String carbPercent;
    @SerializedName("protein_percent")
    @Expose
    private String proteinPercent;
    @SerializedName("fat_percent")
    @Expose
    private String fatPercent;
    @SerializedName("units")
    @Expose
    @Ignore // ADDED AS going to be a table
    private List<FoodUnitsData> units = null;

    @NonNull // ADDED (not reqd)
    public String getFoodId() {
        return foodId;
    }


    public void setFoodId(@NonNull /* ADDED @NonNull (not reqd)*/ String foodId) {
        this.foodId = foodId;
    }

    public String getFoodName() {
        return foodName;
    }

    public void setFoodName(String foodName) {
        this.foodName = foodName;
    }

    public String getFoodImage() {
        return foodImage;
    }

    public void setFoodImage(String foodImage) {
        this.foodImage = foodImage;
    }

    public String getFoodKcal() {
        return foodKcal;
    }

    public void setFoodKcal(String foodKcal) {
        this.foodKcal = foodKcal;
    }

    public String getFoodUrl() {
        return foodUrl;
    }

    public void setFoodUrl(String foodUrl) {
        this.foodUrl = foodUrl;
    }

    public String getFoodDescription() {
        return foodDescription;
    }

    public void setFoodDescription(String foodDescription) {
        this.foodDescription = foodDescription;
    }

    public String getCarbPercent() {
        return carbPercent;
    }

    public void setCarbPercent(String carbPercent) {
        this.carbPercent = carbPercent;
    }

    public String getProteinPercent() {
        return proteinPercent;
    }

    public void setProteinPercent(String proteinPercent) {
        this.proteinPercent = proteinPercent;
    }

    public String getFatPercent() {
        return fatPercent;
    }

    public void setFatPercent(String fatPercent) {
        this.fatPercent = fatPercent;
    }

    public List<FoodUnitsData> getUnits() {
        return units;
    }

    public void setUnits(List<FoodUnitsData> units) {
        this.units = units;
    }
}
  • Foods类现在有两个用途:-
  1. 作为用于提取 JSON 的类(其中单元将相应地填充 FoodUnitsData 对象)
  2. 作为房间表的模型。
  • 看评论

其次是FoodUnitsDataEntity类。

这是一个新类,它将基于 FoodUnitsData 类,但包括 FoodsUnitsData 类不提供的两个重要值/列,即:-

  • 将作为主键的唯一标识符,以及
  • 用于在 Foods 表中建立行与其父级之间关系的映射/参考。由于该列将被非常频繁地使用(即它对于建立关系至关重要),因此在列上建立一个索引是有意义的(加速建立关系(就像书中的索引会加速查找内容))
  • 由于存在关系,因此明智的做法是确保保持参照完整性。那就是你不想要孤立的单位。因此使用了外键约束(规则说孩子必须有父母)。
  • 因为基于 FoodUnitsData 对象构建/插入会很方便,所以添加了一个构造函数,该构造函数将从 FoodUnitsData 对象(加上所有重要的 Foods 映射/引用/关联值)创建一个 FoodUnitsDataEnity 对象。

所以 :-

/*
    NEW CLASS that:-
        Has a Unique ID (Long most efficient) as the primary Key
        Has a column to reference/map to the parent FoodUnitsData of the food that owns this
        Embeds the FoodUnitsData class
        Enforces referential integrity be defining a Foreign Key constraint (optional)
            If parent is delete then children are deleted (CASCADE)
            If the parent's foodId column is changed then the foodIdMap is updated in the children (CASCADE)
 */
@Entity(
        tableName = "food_units",
        foreignKeys = {
                @ForeignKey(
                        entity = Foods.class, /* The class (annotated with @ Entity) of the owner/parent */
                        parentColumns = {"foodId"}, /* respective column referenced in the parent (Foods) */
                        childColumns = {"foodIdMap"}, /* Column in the table that references the parent */
                        onDelete = CASCADE, /* optional within Foreign key */
                        onUpdate = CASCADE /* optional with foreign key */
                )
        }
)
class FoodUnitsDataEntity {
    @PrimaryKey
    Long foodUnitId = null;
    @ColumnInfo(index = true)
    String foodIdMap;
    @Embedded
    FoodUnitsData foodUnitsData;

    FoodUnitsDataEntity(){}
    FoodUnitsDataEntity(FoodUnitsData fud, String foodId) {
        this.foodUnitsData = fud;
        this.foodIdMap = foodId;
        this.foodUnitId = null;
    }
}

第三个FoodUnitsData

这堂课没问题。但是,对于演示/示例构造函数,按以下方式添加:-

public class FoodUnitsData {
    @SerializedName("unit")
    @Expose
    private String unit;
    @SerializedName("amount")
    @Expose
    private String amount;
    @SerializedName("calory")
    @Expose
    private String calory;
    @SerializedName("calcium")
    @Expose
    private String calcium;
    @SerializedName("carbohydrt")
    @Expose
    private String carbohydrt;
    @SerializedName("cholestrl")
    @Expose
    private String cholestrl;
    @SerializedName("fiber_td")
    @Expose
    private String fiberTd;
    @SerializedName("iron")
    @Expose
    private String iron;
    @SerializedName("lipid_tot")
    @Expose
    private String lipidTot;
    @SerializedName("potassium")
    @Expose
    private String potassium;
    @SerializedName("protein")
    @Expose
    private String protein;
    @SerializedName("sodium")
    @Expose
    private String sodium;
    @SerializedName("vit_a_iu")
    @Expose
    private String vitAIu;
    @SerializedName("vit_c")
    @Expose
    private String vitC;

    /* ADDED Constructors */
    FoodUnitsData(){}
    FoodUnitsData(String unit,
                  String amount,
                  String calory,
                  String calcium,
                  String cholestrl,
                  String carbohydrt,
                  String fiberTd,
                  String iron,
                  String lipidTot,
                  String potassium,
                  String protein,
                  String sodium,
                  String vitAIu,
                  String vitC
    ){
        this.unit = unit;
        this.amount = amount;
        this.calory = calory;
        this.calcium = calcium;
        this.cholestrl = cholestrl;
        this.carbohydrt = carbohydrt;
        this.fiberTd = fiberTd;
        this.iron = iron;
        this.lipidTot = lipidTot;
        this.potassium = potassium;
        this.sodium = sodium;
        this.protein = protein;
        this.vitAIu = vitAIu;
        this.vitC = vitC;

    }
    /* Finish of ADDED code */


    public String getUnit() {
        return unit;
    }

    public void setUnit(String unit) {
        this.unit = unit;
    }

    public String getAmount() {
        return amount;
    }

    public void setAmount(String amount) {
        this.amount = amount;
    }

    public String getCalory() {
        return calory;
    }

    public void setCalory(String calory) {
        this.calory = calory;
    }

    public String getCalcium() {
        return calcium;
    }

    public void setCalcium(String calcium) {
        this.calcium = calcium;
    }

    public String getCarbohydrt() {
        return carbohydrt;
    }

    public void setCarbohydrt(String carbohydrt) {
        this.carbohydrt = carbohydrt;
    }

    public String getCholestrl() {
        return cholestrl;
    }

    public void setCholestrl(String cholestrl) {
        this.cholestrl = cholestrl;
    }

    public String getFiberTd() {
        return fiberTd;
    }

    public void setFiberTd(String fiberTd) {
        this.fiberTd = fiberTd;
    }

    public String getIron() {
        return iron;
    }

    public void setIron(String iron) {
        this.iron = iron;
    }

    public String getLipidTot() {
        return lipidTot;
    }

    public void setLipidTot(String lipidTot) {
        this.lipidTot = lipidTot;
    }

    public String getPotassium() {
        return potassium;
    }

    public void setPotassium(String potassium) {
        this.potassium = potassium;
    }

    public String getProtein() {
        return protein;
    }

    public void setProtein(String protein) {
        this.protein = protein;
    }

    public String getSodium() {
        return sodium;
    }

    public void setSodium(String sodium) {
        this.sodium = sodium;
    }

    public String getVitAIu() {
        return vitAIu;
    }

    public void setVitAIu(String vitAIu) {
        this.vitAIu = vitAIu;
    }

    public String getVitC() {
        return vitC;
    }

    public void setVitC(String vitC) {
        this.vitC = vitC;
    }
}

第四道访问

显然应该添加新的 FoodUnitsDataEntity 的惰性/更新/删除。但是请注意,现有的已更改为不返回 void 而是 long 用于插入和 int 用于更新删除。

  • inserts 返回 -1 或 rowid(所有表(如果使用 Room)将具有唯一标识插入行的隐藏列)。因此,如果它是 -1,则未插入行(或 < 0)。
  • 删除和更新返回受影响(更新/删除)的行数。

能够传递一个 Food 对象并插入所有单位行将是有益的。由于这需要一个带有主体而不是接口的方法,因此将使用抽象类。

所以 DaoAccess 变成了:-

@Dao
public /* CHANGED TO abstract class from interface */ abstract class DaoAccess {
    @Query("SELECT * FROM food_data")
    abstract List<Foods> getAll();

    @Insert(onConflict = OnConflictStrategy.IGNORE)
    abstract long insert(Foods task);
    @Insert(onConflict = OnConflictStrategy.IGNORE)
    abstract long insert(FoodUnitsDataEntity foodUnitsDataEntity); 

    @Delete
    abstract int delete(Foods task);
    @Delete
    abstract int delete(FoodUnitsDataEntity foodUnitsDataEntity);

    @Update
    abstract int update(Foods task);
    @Update
    abstract int update(FoodUnitsDataEntity foodUnitsDataEntity);

    @Query("") /* Trick Room to allow the use of @Transaction*/
    @Transaction
    long insertFoodsWithAllTheFoodUnitsDataEntityChildren(Foods foods) {
        long rv = -1;
        long fudInsertCount = 0;
        if (insert(foods) > 0) {
          for(FoodUnitsData fud: foods.getUnits()) {
              if (insert(new FoodUnitsDataEntity(fud,foods.getFoodId())) > 0) {
                  fudInsertCount++;
              }
          }
          if (fudInsertCount != foods.getUnits().size()) {
              rv = -(foods.getUnits().size() - fudInsertCount);
          } else {
              rv = 0;
          }
        }
        return rv;
    }
}

第五食品数据库

只需将FoodUnitsDataEntity添加为实体:-

@Database(entities = {Foods.class, FoodUnitsDataEntity.class /*<<<<<<<<<< ADDED*/}, version = 1)
public abstract class FoodDatabase extends RoomDatabase {
    public abstract DaoAccess daoAccess(); 
}

第六次在 Activity MainActivity中测试上述内容

这项活动将:-

  1. 使用一些嵌入式 FoodUnitsData 构建一个 Foods 对象。
  2. 将其保存为 JSON 字符串,从 JSON 字符串中提取(记录 JSON 字符串)
  3. 获取数据库实例。
  4. 获取 DaoAccess 的一个实例。
  5. 使用该insertFoodsWithAllTheFoodUnitsDataEntityChildren方法插入食物和相关/相关的孩子。

按照 :-

public class MainActivity extends AppCompatActivity {

    FoodDatabase fooddb;
    DaoAccess foodDao;

    @Override
    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        /* Build data to test */
        Foods foods = new Foods();
        foods.setFoodId("MyFood");
        foods.setCarbPercent("10.345");
        foods.setFoodDescription("The Food");
        foods.setFatPercent("15.234");
        foods.setFoodImage("The Food Image");
        foods.setFoodKcal("120");
        foods.setFoodName("The Food");
        foods.setFoodUrl("URL for the Food");
        foods.setProteinPercent("16.234");
        foods.setUnits(Arrays.asList(
                new FoodUnitsData("100","15","1200","11","12","13","14","15","16","17","18","19","20","21"),
                new FoodUnitsData("1001","151","12001","11","12","13","14","15","16","17","18","19","20","21"),
                new FoodUnitsData("1002","152","12002","11","12","13","14","15","16","17","18","19","20","21")
        ));

        String json = new Gson().toJson(foods);
        Log.d("JSONINFO",json);
        Foods foodsFromJSON = new Gson().fromJson(json,Foods.class);

        fooddb = Room.databaseBuilder(this,FoodDatabase.class,"food.db")
                .allowMainThreadQueries()
                .build();
        foodDao = fooddb.daoAccess();
        foodDao.insertFoodsWithAllTheFoodUnitsDataEntityChildren(foodsFromJSON);
    }
}

运行应用程序后的结果

日志包括:-

D/JSONINFO: {"carb_percent":"10.345","fat_percent":"15.234","food_description":"The Food","food_id":"MyFood","food_image":"The Food Image","food_kcal":"120","food_name":"The Food","food_url":"URL for the Food","protein_percent":"16.234","units":[{"amount":"15","calcium":"11","calory":"1200","carbohydrt":"13","cholestrl":"12","fiber_td":"14","iron":"15","lipid_tot":"16","potassium":"17","protein":"18","sodium":"19","unit":"100","vit_a_iu":"20","vit_c":"21"},{"amount":"151","calcium":"11","calory":"12001","carbohydrt":"13","cholestrl":"12","fiber_td":"14","iron":"15","lipid_tot":"16","potassium":"17","protein":"18","sodium":"19","unit":"1001","vit_a_iu":"20","vit_c":"21"},{"amount":"152","calcium":"11","calory":"12002","carbohydrt":"13","cholestrl":"12","fiber_td":"14","iron":"15","lipid_tot":"16","potassium":"17","protein":"18","sodium":"19","unit":"1002","vit_a_iu":"20","vit_c":"21"}]}

使用应用检查(数据库检查器):-

在此处输入图像描述

在此处输入图像描述

于 2022-01-22T20:32:39.913 回答
0

将它们分成 2 个实体,然后创建一个关系类。这个关系类使用 FoodListModel 作为嵌入属性,它与作为 List 的 UnitList 有关系。

于 2022-01-22T18:49:27.693 回答