如何在Android中使用嵌套对象数据存储数据?

问题描述 投票:0回答:1

我有一个 JSON 结构的高维数据集。我通过 Volley 图书馆阅读了它们。我想将这些数据保存在数据库中并访问它。我该怎么办?

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"
     }
   ]
 }
]
}
java android sqlite realm android-sqlite
1个回答
1
投票

对于 SQLite

您要做的第一件事是确定数据库的schema

查看您的数据,您有很多 MyData 对象,每个对象都有 0 个或多个 Unit 对象。

因此,您可以拥有一个用于 MyData 对象的表和一个用于 Unit 对象的表。每个单元都有一个父 MyData 对象。因此,除了每个单元的数据之外,您还可以有一列引用(映射、关联)MyData(可能是 food_id,假设它唯一标识 MyData)。

使用 MyData 的缩短版本(仅 food_id、food_name)和 Unit 的缩短版本(单位、数量和卡路里)来演示,然后您可以有两个类:-

class MyData {
    public static final String TABLE_NAME = "_mydata";
    public static final String COLUMN_FOOD_ID = "_food_id";
    public static final String COLUMN_FOOD_NAME = "_food_name";
    long food_id;
    String food_name;
    Unit[] units;

    public MyData(long food_id, String food_name, Unit[] units) {
        this.food_id = food_id;
        this.food_name = food_name;
        this.units = units;
    }
}

class Unit {
    public static final String TABLE_NAME = "_unit";
    public static final String COLUMN_UNIT = "_unit";
    public static final String COLUMN_AMOUNT = "_amount";
    public static final String COLUMN_CALORY = "_calory";
    public static final String COLUMN_FOOD_ID_PARENT ="parent_food_id";
    String unit;
    double amount;
    double calory;

    public Unit(String unit, double amount, double calory) {
        this.unit = unit;
        this.amount = amount;
        this.calory = calory;
    }
}

注意将用于表的常量,即模式。为了简洁起见,将它们放入类中,它们可以在其他地方编码。

注意附加列parent_food_id,这将用于引用父food_id。

所以架构将(可能)看起来像

  1. Table _mydata 有 2 列(易于添加更多列)
    1. food_id(这将是唯一的(主键),并且该值是整数,如果是 SQLite,则为 INTEGER 类型),并且
    2. food_name(因为它是一个 String,然后是 SQLite 中的 TEXT 类型)。
  2. _unit 有 4 列
    1. _unit(因为它是 SQLite 中的 String 和 TEXT 类型)
    2. _amount(因为它是浮点类型,然后是 SQLite 中的 REAL)
    3. _calory(因为它是浮点类型,然后是 SQLite 中的 REAL)
    4. parent_food_id,因为它指的是 food_id,它是 INTEGER,那么它将是 INTEGER。
      1. 由于它是创建两个表之间关系的原因,因此应该要求它确实引用 food_id。虽然不是必需的,但最好强制执行引用的完整性(引用完整性),为此可以添加外键约束。
    • 拥有主键总是明智的,但由于其他 MyData 对象可能具有相同的单位值和/或数量和/或卡路里值,因此所有三个的组合可能不太可能相同。所以主键是所有 3 列。

用 SQLite 术语来说,创建上述内容的 SQL(每个表一个)可以是:-

  • CREATE TABLE _mydata(_food_id INTEGER PRIMARY KEY,_food_name TEXT)
    ,以及
  • CREATE TABLE _unit(_unit TEXT,_amount REAL,_calory REAL,parent_food_id INTEGER REFERENCES _mydata(_food_id) ON DELETE CASCADE ON UPDATE CASCADE,  PRIMARY KEY(_unit,_amount,_calory))

处理数据库

对于 Android 和 SQLite,典型的方法是使用扩展 SQLiteOpenHelper 的 DatabaseHelper。 SQLiteOpenHelpr 需要 2 个重写方法 onCreateonUpgrae

  • onCreate 在创建数据库时运行。
    • 它仅在数据库存在时运行一次,该数据库持续存在,即它仅在应用程序首次运行时运行。
    • 此方法通常用于定义实体/组件,例如表。
  • 传递给 SQLiteOpenHelper 的数据库版本的 onUpgrade 运行大于从数据库本身提取的版本号。

通常是用于访问数据库的代码,例如添加、删除、更新、删除或提取数据。

为了演示,数据库将仅具有将数据插入表中的方法。另外还有一个方法,它将采用 JSON 字符串(相当于文件)并填充表。

所以 DatabaseHelper 类可能是:-

class DatabaseHelper extends SQLiteOpenHelper {

    public static final String DATABASE_NAME = "my_database.db";
    public static final int DATABASE_VERSION = 1;

    private SQLiteDatabase db;
    private static volatile DatabaseHelper instance = null;
    private DatabaseHelper(Context context) {
        super(context,DATABASE_NAME,null,DATABASE_VERSION);
        db = this.getWritableDatabase();
    }

    /* Use a singleton approach */
    public static DatabaseHelper getInstance(Context context) {
        if (instance == null) {
            instance = new DatabaseHelper(context);
        }
        return instance;
    }

    @Override
    public void onCreate(SQLiteDatabase db) {
        db.execSQL(
                "CREATE TABLE IF NOT EXISTS " + MyData.TABLE_NAME + "(" +
                        MyData.COLUMN_FOOD_ID + " INTEGER PRIMARY KEY," +
                        MyData.COLUMN_FOOD_NAME + " TEXT" +
                        ")"
        );

        db.execSQL(
                "CREATE TABLE IF NOT EXISTS " + Unit.TABLE_NAME + "(" +
                        Unit.COLUMN_UNIT + " TEXT," +
                        Unit.COLUMN_AMOUNT + " REAL," +
                        Unit.COLUMN_CALORY + " REAL," +
                        Unit.COLUMN_FOOD_ID_PARENT +
                        /* Foreign Key Constraint to enforce referential integrity*/
                        " INTEGER REFERENCES " + MyData.TABLE_NAME + "(" + MyData.COLUMN_FOOD_ID + ") " +
                        /* These make maintaining referential integrity easier */
                        "ON DELETE CASCADE " +
                        "ON UPDATE CASCADE, " +
                        /* define the primary key */
                        " PRIMARY KEY(" +
                        Unit.COLUMN_UNIT + "," + Unit.COLUMN_AMOUNT + "," + Unit.COLUMN_CALORY +
                        ")" +
                        ")"
        );

    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int i, int i1) {
        /* not expecting to increase the database version so leave this to do nothing */
    }

    public long insertMyData(MyData myData) {
        ContentValues cv = new ContentValues();
        cv.put(MyData.COLUMN_FOOD_ID,myData.food_id);
        cv.put(MyData.COLUMN_FOOD_NAME,myData.food_name);
        return db.insert(MyData.TABLE_NAME,null,cv);
    }

    public long insertUnit(Unit unit, long parentId) {
        ContentValues cv = new ContentValues();
        cv.put(Unit.COLUMN_UNIT,unit.unit);
        cv.put(Unit.COLUMN_AMOUNT,unit.amount);
        cv.put(Unit.COLUMN_CALORY,unit.calory);
        cv.put(Unit.COLUMN_FOOD_ID_PARENT,parentId);
        return db.insert(Unit.TABLE_NAME,null,cv);
    }

    public void massInsert(String jsonString) {
        MyData[] extracted = new Gson().fromJson(jsonString,MyData[].class);
        db.beginTransaction();
        for(MyData m: new Gson().fromJson(jsonString,MyData[].class)) {
            long food_id = insertMyData(m);
            if (food_id > 0) {
                for(Unit u: m.units) {
                    insertUnit(u,food_id);
                }
            }
        }
        db.setTransactionSuccessful();
        db.endTransaction();
    }
}

示范

这里是在 Activity 中使用上述内容的演示,即 MainActivity:-

public class MainActivity extends AppCompatActivity {

    MyData[] TESTDATA = {
            new MyData(
                    1,
                    "Bat",
                    new Unit[]{
                            new Unit("Unit A",15.0000,32.4877372383),
                            new Unit("Unit C",110.0000,238.243404414),
                            new Unit("Unit F",1.0000,2.16584914922)
                    }
                    ),
            new MyData(
                    2,
                    "Another",
                    new Unit[]{
                            new Unit("Unit A",17.0000,3.4877372383),
                            new Unit("Unit C",10.0000,382.243404414),
                            new Unit("Unit F",3.0000,5.16584914922)
                    }
            ),
    };

    DatabaseHelper dbHelper;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        String test = new Gson().toJson(TESTDATA);
        Log.d("APPINFO","TESTDATA as JSON is\n\t" + test);
        //MyData[] extracted = new Gson().fromJson(test,MyData[].class);

        dbHelper = DatabaseHelper.getInstance(this); // Prepare to use the database
        dbHelper.massInsert(test); // Actually use the database to insert the data

        /* Example of extracting data using a JOIN to combine the MyData and the related Units */
        Cursor csr =  dbHelper.getWritableDatabase().query(
                MyData.TABLE_NAME + " JOIN " + Unit.TABLE_NAME + " ON " + Unit.COLUMN_FOOD_ID_PARENT + "=" + MyData.COLUMN_FOOD_ID,
                null,null,null,null,null,null
                );
        DatabaseUtils.dumpCursor(csr);
        csr.close();

    }
}

结果

运行时日志包括:-

2022-01-08 07:32:14.666  D/APPINFO: TESTDATA as JSON is
        [{"food_id":1,"food_name":"Bat","units":[{"amount":15.0,"calory":32.4877372383,"unit":"Unit A"},{"amount":110.0,"calory":238.243404414,"unit":"Unit C"},{"amount":1.0,"calory":2.16584914922,"unit":"Unit F"}]},{"food_id":2,"food_name":"Another","units":[{"amount":17.0,"calory":3.4877372383,"unit":"Unit A"},{"amount":10.0,"calory":382.243404414,"unit":"Unit C"},{"amount":3.0,"calory":5.16584914922,"unit":"Unit F"}]}]

2022-01-08 07:32:14.698  I/System.out: >>>>> Dumping cursor android.database.sqlite.SQLiteCursor@f302fce
2022-01-08 07:32:14.699  I/System.out: 0 {
2022-01-08 07:32:14.699  I/System.out:    _food_id=1
2022-01-08 07:32:14.699  I/System.out:    _food_name=Bat
2022-01-08 07:32:14.699  I/System.out:    _unit=Unit A
2022-01-08 07:32:14.699  I/System.out:    _amount=15
2022-01-08 07:32:14.699  I/System.out:    _calory=32.4877
2022-01-08 07:32:14.699  I/System.out:    parent_food_id=1
2022-01-08 07:32:14.699  I/System.out: }
2022-01-08 07:32:14.699  I/System.out: 1 {
2022-01-08 07:32:14.699  I/System.out:    _food_id=1
2022-01-08 07:32:14.699  I/System.out:    _food_name=Bat
2022-01-08 07:32:14.700  I/System.out:    _unit=Unit C
2022-01-08 07:32:14.701  I/System.out:    _amount=110
2022-01-08 07:32:14.701  I/System.out:    _calory=238.243
2022-01-08 07:32:14.701  I/System.out:    parent_food_id=1
2022-01-08 07:32:14.701  I/System.out: }
2022-01-08 07:32:14.701  I/System.out: 2 {
2022-01-08 07:32:14.701  I/System.out:    _food_id=1
2022-01-08 07:32:14.701  I/System.out:    _food_name=Bat
2022-01-08 07:32:14.701  I/System.out:    _unit=Unit F
2022-01-08 07:32:14.701  I/System.out:    _amount=1
2022-01-08 07:32:14.701  I/System.out:    _calory=2.16585
2022-01-08 07:32:14.701  I/System.out:    parent_food_id=1
2022-01-08 07:32:14.701  I/System.out: }
2022-01-08 07:32:14.702  I/System.out: 3 {
2022-01-08 07:32:14.702  I/System.out:    _food_id=2
2022-01-08 07:32:14.702  I/System.out:    _food_name=Another
2022-01-08 07:32:14.702  I/System.out:    _unit=Unit A
2022-01-08 07:32:14.702  I/System.out:    _amount=17
2022-01-08 07:32:14.702  I/System.out:    _calory=3.48774
2022-01-08 07:32:14.702  I/System.out:    parent_food_id=2
2022-01-08 07:32:14.702  I/System.out: }
2022-01-08 07:32:14.702  I/System.out: 4 {
2022-01-08 07:32:14.702  I/System.out:    _food_id=2
2022-01-08 07:32:14.703  I/System.out:    _food_name=Another
2022-01-08 07:32:14.703  I/System.out:    _unit=Unit C
2022-01-08 07:32:14.703  I/System.out:    _amount=10
2022-01-08 07:32:14.703  I/System.out:    _calory=382.243
2022-01-08 07:32:14.703  I/System.out:    parent_food_id=2
2022-01-08 07:32:14.703  I/System.out: }
2022-01-08 07:32:14.703  I/System.out: 5 {
2022-01-08 07:32:14.703  I/System.out:    _food_id=2
2022-01-08 07:32:14.703  I/System.out:    _food_name=Another
2022-01-08 07:32:14.703  I/System.out:    _unit=Unit F
2022-01-08 07:32:14.703  I/System.out:    _amount=3
2022-01-08 07:32:14.704  I/System.out:    _calory=5.16585
2022-01-08 07:32:14.704  I/System.out:    parent_food_id=2
2022-01-08 07:32:14.704  I/System.out: }
2022-01-08 07:32:14.704  I/System.out: <<<<<

使用 Android Studio 的 App Inspection 数据库看起来像:-

enter image description here

enter image description here

© www.soinside.com 2019 - 2024. All rights reserved.