日日操夜夜添-日日操影院-日日草夜夜操-日日干干-精品一区二区三区波多野结衣-精品一区二区三区高清免费不卡

公告:魔扣目錄網為廣大站長提供免費收錄網站服務,提交前請做好本站友鏈:【 網站目錄:http://www.ylptlb.cn 】, 免友鏈快審服務(50元/站),

點擊這里在線咨詢客服
新站提交
  • 網站:51998
  • 待審:31
  • 小程序:12
  • 文章:1030137
  • 會員:747

本文介紹了如何將嵌套的JSON數據存儲在Room Database中?[房間]的處理方法,對大家解決問題具有一定的參考價值,需要的朋友們下面隨著小編來一起學習吧!

問題描述

有如下JSON結構。我還想在Retrofit的幫助下解析后將此數據存儲在Room中。當使用相同的實體類時,它會給出錯誤。如果您能分享一個嵌套數據的示例,我將不勝感激。(請共享用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"
      }
    ]
  }
]
}

推薦答案

不能將列表/數組作為列。

private List<FoodUnitsData> units = null;

這與您上一個問題How can I store data with nested object data in Android?中討論的問題相同。

所以您需要將數據轉換為可處理的列或具有相關的表。

要使用轉換后的數據以便可以將其存儲在列中,您可以有一個具有列表的類,例如

class FoodUnitsDataListHolder {
    List<FoodUnitsData> unitsList;
}

并將該列設置為(不確定這將如何與Retrofit一起使用):-

private FoodUnitsDataListHolder units = null;

然后,您需要TypeConverters將FoodUnitsDataListHolder轉換為可以存儲的類型(可能是字符串),或者從可以存儲的類型(可能是字符串)進行轉換。通常,這將指向對象的JSON字符串表示形式。

例如

@TypeConverter
String fromFoodUnitsDataListHolder(FoodUnitsDataListHolder fudlh): String {
    return Gson().toJson(fudlh)
}
@TypeConverter
FoodUnitsDataListHolder toFoodUnitsDataListHolder(String json) {
    return Gson().fromJson(json,FoodUnitsDataListHolderclass.java)
}

您需要使用具有合適作用域的[@TypeConverters][1]批注(我建議使用@Database級別)。

How can I store data with nested object data in Android?的答案解釋了如何擁有相關的表,您只需要使用合適的實體(每個表使用@Entity注釋的類)通過空間來實現這一點,而不是使用SQL來創建表以及@Dao注釋的類中用于訪問數據的合適的方法。

添加備注

用sqlite做這類工作既復雜又不夠。所以我必須為房間找一個例子。

調整代碼并不那么困難。以下內容將帶您一覽無余。

首先考慮Foods類。

正如最初建議的那樣,您遇到的問題是成員變量private List<FoodUnitsData> units = null;。列不能是列表/數組。

如前所述,有兩個選項將列表存儲為單個對象,該對象將轉換為Room可以管理的類型。這通常是一個JSON字符串。但是,JSON字符串不容易通過SQL操作/訪問,并且會額外膨脹。

您可能很想做一些事情,例如查找低/高卡路里(卡路里)值的食物。那么,從數據庫的角度來看,試圖從JSON字符串確定這一點將是困難和低效的。因此,強烈建議將單位(嵌入/嵌套數據)存儲在表中。

記住這一點,那么第一件事就是從Foods中排除該單元,但是為了JSON,允許它存在于Foods類中。這可以通過使用Room的@Ignore批注來實現。

下一步是添加所需的@Entity@PrimaryKey注釋。foodId成員變量對于每個Foods對象似乎是唯一的,因此可能是此對象的候選變量。

由于foodId是字符串,并且Room將堅持它不能為空,因此應該另外使用@NonNull批注對其進行批注。

因此食品可以是:-

@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;
    }

}

查看評論
Room將@Entity批注類(如果在@Database批注的Entities參數中定義)視為。這就是我們介紹的Foods類,它既可以從JSON字符串中提取,也可以作為表提取。

請注意,如果僅從表中提取Foods,則不會填充FoodUnitsData列表。

將FoodUnitsData作為表進行第二次

FoodUnitsData類在沒有:-

的情況下不會使其自身成為表(@Entity批注)
a)主鍵,
b)沒有將其關聯(映射/引用)到擁有它的食品(其父項)的方式。

這樣的類將被創建嵌入FoodUnitsData類,但包含a)和b)的附加成員變量。

由于b)將是所謂的外鍵,建議添加一個外鍵約束(規則)來實施引用完整性(這樣您就不能讓孤立的(無用的)FoodUnitsData使數據庫膨脹,甚至可能導致崩潰)。

為進一步簡化引用完整性的維護,外鍵約束將包括在刪除或更新父Foods以影響關系(foodId更改)時刪除或更新FoodUnitsData的特殊操作。

出于演示目的,該類將命名為FoodUnitsDataEntity

為便于從FoodUnitsData對象構造FoodUnitsDataEntity,將添加一個具有2個參數的構造函數:-

    FoodUnitsData對象和
    與父Foods的關系的foodId。

將自動生成主鍵(也稱為id)(但不使用低效的AUTOINCREMENT(autogenerate = true在Room中的PrimaryKey注釋中)。因此,使用@PrimaryKey注釋的Long the_column/member_name = null;(因此,SQLite行ID的別名Room將把列定義為整數主鍵,這是允許自動生成ID的特殊情況)。

映射到foodId的成員變量必須是相同類型(在數據庫方面,因此在中您可以逃脫,例如,將int映射到long,只要兩者都存儲為整數類型)。因此,foodId應該是String foodId;,因為房間希望索引外鍵(如果沒有,則發出警告),則使用@ColumnInfo(index = true)注釋。

若要嵌入FoodUnitsData并獲取所有成員變量作為列,則使用@Embedded對FoodUnitsData對象進行編碼和批注。

應用上述內容FoodUnitsDataEntity可以是:-

/*
    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             //<<<<< a)
    Long foodUnitId = null; //<<<<< a) 
    @ColumnInfo(index = true) //<<<<< b) not essential but room will warn if not indexed
    String foodIdMap; //<<<<< b)
    @Embedded    // Tells Room to include the following object with it's member variables as columns in the table
    FoodUnitsData foodUnitsData;

    FoodUnitsDataEntity(){} // Empty Constructor for Rooms creation of FoodUnitsEntity objects
    // constructor for converting a FoodUnitsData to a FoodUnitsDataEntity 
    FoodUnitsDataEntity(FoodUnitsData fud, String foodId) {
        this.foodUnitsData = fud;
        this.foodIdMap = foodId;
        this.foodUnitId = null;
    }
}

@Database

在此階段,如果您使用定義的兩個實體創建了合適的@Database帶注釋的類,則可以編譯(生成)項目(而不是運行應用程序),而Room將生成底層Java代碼,同時還會生成突出顯示問題的日志。

所以您可以:-

@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
}

請注意DAoAccess的注釋(因為在將此添加到答案之前已進行了全面測試)。
編譯后可以訪問生成的Java。這將位于生成的java的FoodDatabase_Impl類中(使用Android Studio Android View查看生成的java)。createAllTables方法是最有可能感興趣的方法,因為它包含將用于創建表、索引等的SQL。

DAoAccess

由于可能需要使用建議的模式(外鍵約束)從JSON填充數據庫,因此需要a)將父級插入到Foods表中,然后b)插入FoodUnitsData的相應行。將需要具有主體的方法。因此,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("")
    @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;
    }
}

請注意,這些方法返回的不是void,而是long(插入)或int(刪除/更新)。

對于INSERTS,LONG將是插入行的行ID(rowid是所有房間表(盡管不是FTS的虛擬表)將具有的隱藏列),或者如果該行沒有插入但沒有導致錯誤,則為-1(@INSERT使用INSERT或IGNORE,因此忽略約束沖突,如UNIQUE,但不插入該行)。但是,違反外鍵將導致失敗。

對于更新和刪除,int返回的是受影響的行數(已刪除或已更新)。

沒有正文的方法需要是抽象的,有正文的方法不應該是抽象的。

了解insertFoodsWithAllTheFoodUnitsDataEntityChildren方法如何調用其他方法,以及它如何遵循上面的a)和b)。這是從JSON添加的關鍵,JSON在Foods對象中構建嵌入式FoodUnitsData對象。

使用了@query(&quot;&quot;),它顯然不執行任何操作,以允許使用@Transaction。因此,所有插入都將在內存/臨時文件中完成,并且只有在最后才會將更改應用到磁盤。這降低了I/O開銷。

重新訪問FoodDatabase

添加方法以檢索DaoAccess實例,并再次編譯并檢查日志(警告可以注意或忽略,但至少在使用Room 2.4.1版時不應該有警告)。

向FoodUnitsData添加方便的構造函數

這不是必需的,但此答案的后續部分會使用它,并建議使用。

不必編寫代碼來構造空的FoodUnitsData對象,然后使用setter來設置值,如果添加構造函數以允許傳遞值,則可以簡化編碼。

因此,可以添加以下構造函數(我還建議使用空構造函數)。以下是兩者的代碼:-

/* 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;

}

測試/演示

以下MainActivity代碼用于測試/演示:-

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);
    }
}

測試/演示結果

使用應用檢查(數據庫檢查器)可以看到數據庫是:-

和Food_unit表:-

這篇關于如何將嵌套的JSON數據存儲在Room Database中?[房間]的文章就介紹到這了,希望我們推薦的答案對大家有所幫助,

分享到:
標簽:database JSON Room 如何將 嵌套 房間 數據存儲
用戶無頭像

網友整理

注冊時間:

網站:5 個   小程序:0 個  文章:12 篇

  • 51998

    網站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會員

趕快注冊賬號,推廣您的網站吧!
最新入駐小程序

數獨大挑戰2018-06-03

數獨一種數學游戲,玩家需要根據9

答題星2018-06-03

您可以通過答題星輕松地創建試卷

全階人生考試2018-06-03

各種考試題,題庫,初中,高中,大學四六

運動步數有氧達人2018-06-03

記錄運動步數,積累氧氣值。還可偷

每日養生app2018-06-03

每日養生,天天健康

體育訓練成績評定2018-06-03

通用課目體育訓練成績評定