当前位置: 首页 > 知识库问答 >
问题:

我如何在房间Android中建立多对多关系?

公良鸿禧
2023-03-14

我想创建一个如下所示的数据库。我阅读了文档,但没有找到任何类似的例子。你能帮助我吗?有几天,在这几天内,每天将有多顿饭和每天的地下水位

一旦应用程序启动,就会创建一个日期数据库,然后无论它在哪一天(可以手动更改日期),都会将每日水和每日膳食添加到那一天

            "days":
            [
                {
                    "id": "Integer",
                    "date": "String",
                    "weight": "Float",
                    "height": "Integer",
                    "waist_circumference": "Integer",
                    "neck_circumference": "Integer",
                    "hip_circumference": "Integer",
                    "steps": "Long",
                    "taken_kcal": "Float",
                    "burned_kcal": "Float",
                    "carb": "Float",
                    "protein": "Float",
                    "fat": "Float",
                    "water_ml": "Integer",
                    "water_percent": "Float",
                    "meals":
                    [
                        {
                            "day_id": "Integer",
                            "id": "Integer",
                            "food_meal_time": "String",
                            "food_name": "String",
                            "food_image": "String",
                            "food_kcal": "Float",
                            "food_portion": "String",
                            "food_carb": "Float",
                            "food_protein": "Float",
                            "food_fat": "Float"
                        },
                        {
                            "day_id": "Integer",
                            "id": "Integer",
                            "food_meal_time": "String",
                            "food_name": "String",
                            "food_image": "String",
                            "food_kcal": "Float",
                            "food_portion": "String",
                            "food_carb": "Float",
                            "food_protein": "Float",
                            "food_fat": "Float"
                        }
                    ],
                    "water":
                    [
                        {
                            "day_id": "Integer",
                            "id": "Integer",
                            "water_ml": "Float",
                            "water_percent": "Float",
                            "water_time": "Long"
                        },
                        {
                            "day_id": "Integer",
                            "id": "Integer",
                            "water_ml": "Float",
                            "water_percent": "Float",
                            "water_time": "Long"
                        },
                        {
                            "day_id": "Integer",
                            "id": "Integer",
                            "water_ml": "Float",
                            "water_percent": "Float",
                            "water_time": "Long"
                        }
                    ]
                }
    ]

共有1个答案

陈翰林
2023-03-14

您有3个核心表(在数据库注释的实体列表中定义的带注释的类)、天、餐和水。

如果(请参阅下文)您希望在天和餐之间以及天和水之间建立多对多关系,那么您将为每个多对多关系创建一个关联/映射/引用(和其他术语)表。这样的表有两列,一列唯一关联/引用/映射到天,另一列唯一关联/引用/映射到餐,用于天-餐,或者在天-水关联/引用/映射到水的情况下。通常关联/引用/映射到id列。

所以在你的情况下(如果按照标题),你会有两个这样的表。

为了提取相关数据(与相关膳食和相关水域的天数),您有一个POJO,其中嵌入了日期和与膳食的关系,使用关联注释和连接注释定义关联/参考/映射表,同样也定义了与水域的关系。

但是,您的JSON描述了两个一对多关系,而不是两个多对多关系。也就是说,Meals引用了父天,同样,Waters引用了父天。

在这种情况下,您只需要3个核心表。用于提取具有相关Meals和相关Waters的Day。您可以对Day使用@Embedded注释,对Meals和Waters使用@Relation,而没有@Associate@Juntion,因为没有关联/引用/映射表。

工作示例此工作示例使用

  • 1(天)到许多(膳食和水),和
  • 多-多(通过映射表)允许许多天使用相同的膳食,也同样允许许多天使用相同的水,能够有许多。

所以这3个核心表(实体)天,水和饭。

Day(请注意,一行是Day,因此通常是Day表而不是Day表):-

@Entity
public class Day {
    @PrimaryKey
    Long dayId = null;
    String date;
    Float weight;
    /* etc */

    Day(){} /* Room will use this constructor */
    @Ignore /* Tell Room to ignore this constructor, but still usable outside of Room, so makes life easier */
    Day(String date, Float weight) {
        this.dayId = null; /* not needed but effectively what happens */
        this.date = date;
        this.weight = weight;
    }
    /* No getters and setters required as members aren't private/protected */
}

一餐

@Entity(
        /* Optional but suggested */
        foreignKeys = {
                @ForeignKey(
                        entity = Day.class,
                        parentColumns = {"dayId"},
                        childColumns = {"dayId_Meal_Map"}
                        /* Optional but can be useful */
                        , onDelete = ForeignKey.CASCADE
                        , onUpdate = ForeignKey.CASCADE
                )
        }
)
class Meal {
    @PrimaryKey
    Long mealId=null;
    @ColumnInfo(index = true)
    long dayId_Meal_Map;
    String food_meal_time;
    /* etc */

    Meal(){}
    @Ignore
    Meal(long parentDayId, String food_meal_time) {
        this.dayId_Meal_Map = parentDayId;
        this.food_meal_time = food_meal_time;
    }
    @Ignore
    Meal(String food_meal_time) {
        this.food_meal_time = food_meal_time;
    }
}
  • 注意:对于M2M,您将不会有对父日的引用,因为这是在映射表中保存的,因此您也不会有外键定义,也不会有引用列上的索引
  • 出于类似的原因,两个@忽略的构造函数,其中一个就足够了,这取决于使用的是1-M还是M-M

@Entity(
        /* Optional but suggested */
        foreignKeys = {
                @ForeignKey(
                        entity = Day.class,
                        parentColumns = {"dayId"},
                        childColumns = {"dayId_Water_Map"}
                        /* Optional but can be useful */
                        , onDelete = ForeignKey.CASCADE
                        , onUpdate = ForeignKey.CASCADE
                )
        }
)
class Water {
    @PrimaryKey
    Long waterId=null;
    @ColumnInfo(index = true)
    long dayId_Water_Map;
    Float water_ml;
    /* etc */

    Water(){}
    @Ignore
    Water(long parentDayId, Float water_ml) {
        this.dayId_Water_Map = parentDayId;
        this.water_ml = water_ml;
    }
    /* Constructor for m2m i.e. reference to dayId is not needed as in the mapping table */
    @Ignore
    Water(Float water_ml) {
        this.water_ml = water_ml;
    }
}

对于多-多关系,然后2个映射表DayMealMap和DayWaterMap

DayMealMap公司

@Entity(
        primaryKeys = {"dayMealMap_dayId_Reference","dayMealMap_mealId_Reference"},
        /* Optional but suggested */
        foreignKeys = {
                @ForeignKey(
                        entity = Day.class,
                        parentColumns = {"dayId"},
                        childColumns = {"dayMealMap_dayId_Reference"}
                        /* Optional but helpful */
                        , onDelete = ForeignKey.CASCADE
                        , onUpdate = ForeignKey.CASCADE

                ),
                @ForeignKey(
                        entity = Meal.class,
                        parentColumns = "mealId",
                        childColumns = "dayMealMap_mealId_Reference"
                        , onDelete = ForeignKey.CASCADE
                        , onUpdate = ForeignKey.CASCADE
                )
        }
)
class DayMealMap {
    long dayMealMap_dayId_Reference;
    /* Index optional but would likely improve efficiency */
    /* Also ROOM will issue warning if not indexed */
    @ColumnInfo(index = true)
    long dayMealMap_mealId_Reference;


    DayMealMap(){}
    @Ignore
    DayMealMap(long dayIdMap, long mealIdMap) {
        this.dayMealMap_dayId_Reference = dayIdMap;
        this.dayMealMap_mealId_Reference = mealIdMap;
    }
}

水务地图

@Entity(
        primaryKeys = {"dayWaterMap_dayId_Reference","dayWaterMap_waterId_Reference"},
        foreignKeys = {
                @ForeignKey(
                        entity = Day.class,
                        parentColumns = "dayId",
                        childColumns = "dayWaterMap_dayId_Reference"
                        , onDelete = ForeignKey.CASCADE
                        , onUpdate = ForeignKey.CASCADE
                ),
                @ForeignKey(
                        entity = Water.class,
                        parentColumns = "waterId",
                        childColumns = "dayWaterMap_waterId_Reference"
                        ,onDelete = ForeignKey.CASCADE
                        ,onUpdate = ForeignKey.CASCADE
                )
        }
)
class DayWaterMap {
    long dayWaterMap_dayId_Reference;
    @ColumnInfo(index = true)
    long dayWaterMap_waterId_Reference;

    DayWaterMap(){}
    @Ignore
    DayWaterMap(long dayIdMap, long waterIdMap) {
        this.dayWaterMap_dayId_Reference = dayIdMap;
        this.dayWaterMap_waterId_Reference = waterIdMap;
    }
}

通过1-M关系提取相关膳食和水的天数,然后使用POJO

class DayWithRelatedMealsAlsoWithRelatedWaters {
   @Embedded
   Day day;
   @Relation(
           entity = Meal.class,
           parentColumn = "dayId",
           entityColumn = "dayId_Meal_Map"
   )
   List<Meal> mealList;
   @Relation(
           entity = Water.class,
           parentColumn = "dayId",
           entityColumn = "dayId_Water_Map"
   )
   List<Water> waterList;
}

对于Many-Many,为了获得相同的相关数据,它是POJO(通过映射表(也称为关联表))

class M2MDayWithRelatedMealsAlsoWithRealtedWaters {
   @Embedded
   Day day;
   @Relation(
           entity = Meal.class,
           parentColumn = "dayId",
           entityColumn = "mealId",
           associateBy = @Junction(
                   value = DayMealMap.class,
                   parentColumn = "dayMealMap_dayId_Reference",
                   entityColumn = "dayMealMap_mealId_Reference"
           )
   )
   List<Meal> mealList;
   @Relation(
           entity = Water.class,
           parentColumn = "dayId",
           entityColumn = "waterId",
           associateBy = @Junction(
                   value = DayWaterMap.class,
                   parentColumn = "dayWaterMap_dayId_Reference",
                   entityColumn = "dayWaterMap_waterId_Reference"
           )
   )
   List<Water> waterList;

}

一个带注释的抽象类(可以是一个接口,但我更喜欢抽象类,因为它更灵活(原因超越了这个问题))

AllDao(即为了方便起见,将所有DAO方法放在一起):-

@Dao
abstract class AllDao {

    @Insert(onConflict = OnConflictStrategy.IGNORE)
    abstract long insert(Day day);
    @Insert(onConflict = OnConflictStrategy.IGNORE)
    abstract long insert(Meal meal);
    @Insert(onConflict = OnConflictStrategy.IGNORE)
    abstract long insert(Water water);

    /* For many-many mapping table inserts */
    @Insert(onConflict = OnConflictStrategy.IGNORE)
    abstract long insert(DayMealMap dayMealMap);
    @Insert(onConflict = OnConflictStrategy.IGNORE)
    abstract long insert(DayWaterMap dayWaterMap);

    @Transaction
    @Query("SELECT * FROM day")
    abstract List<DayWithRelatedMealsAlsoWithRelatedWaters> getListOfDaysWithRelatedMealsAndRelatedWaters();

    @Transaction
    @Query("SELECT * FROM day")
    abstract List<M2MDayWithRelatedMealsAlsoWithRealtedWaters> getM2mListOfDaysWithRelatedMealsAlsoWithRelatedWaters();

}
  • 显然,你只会使用一个得到。。。。。取决于您处理关系类型的方式

从Room的角度开始将所有这些放在一起,即@Database注释类。

@Database(
        entities = {
                /* Either 1-many or many-many use the 3 core tables */
                Day.class,
                Meal.class,
                Water.class
                /* for many-many then the 2 mapping tables */
                , DayMealMap.class
                , DayWaterMap.class
        },

        version = 1,
        exportSchema = false
)
abstract class TheDatabase extends RoomDatabase {
    abstract AllDao getAllDao();

    private volatile static TheDatabase INSTANCE= null;

    public static TheDatabase getINSTANCE(Context context) {
        if (INSTANCE==null) {
            INSTANCE = Room.databaseBuilder(context,TheDatabase.class,"the_database.db")
                    .allowMainThreadQueries()
                    .build();
        }
        return INSTANCE;
    }
}
  • 。allowMainThreadQueries用于方便和简洁/清晰

实际上使用上述所有内容来添加一些数据,然后提取两种类型的房地产的数据:-

主要活动

public class MainActivity extends AppCompatActivity {

    TheDatabase mDB;
    AllDao mDao;

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

        mDB = TheDatabase.getINSTANCE(this);
        mDao = mDB.getAllDao();

        /* 1-many */
        long day001Id = mDao.insert(new Day("2022-07-08",10.10F));
        long day002Id = mDao.insert(new Day("2022-07-09",11.11F));
        long day003Id = mDao.insert(new Day("2022-07-09",12.12F));

        /* Add some related Meals (1-M) */
        long m011Id = mDao.insert(new Meal(day001Id,"09:00"));
        long m012Id = mDao.insert(new Meal(day001Id,"11:00"));
        long m013Id = mDao.insert(new Meal(day001Id,"13:00"));
        long m014Id = mDao.insert(new Meal(day001Id,"18:00"));
        long m015Id = mDao.insert(new Meal(day001Id,"21:00"));

        long m021Id = mDao.insert(new Meal(day002Id,"09:30"));
        long m022Id = mDao.insert(new Meal(day002Id,"10:15"));
        long m023Id = mDao.insert(new Meal(day002Id,"12:45"));
        long m024Id = mDao.insert(new Meal(day002Id,"17:30"));
        long m025Id = mDao.insert(new Meal(day002Id,"20:30"));

        /* Add some Waters (1-M) */
        long w011Id = mDao.insert(new Water(day001Id,100.100F));
        long w012Id = mDao.insert(new Water(day001Id,200.200F));

        long w021Id = mDao.insert(new Water(day002Id, 120.12F));
        long w022Id = mDao.insert(new Water(day002Id, 220.22F));



        /* Use the above Days, Meals and Waters to add M2M stuff */

        /* Day 1 just a subset of the 1-m's */
        mDao.insert(new DayMealMap(day001Id,m011Id));
        mDao.insert(new DayMealMap(day001Id,m013Id));
        mDao.insert(new DayMealMap(day001Id,m015Id));
        /* Day 2 just a subset of 1-m's */
        mDao.insert(new DayMealMap(day002Id, m022Id));
        mDao.insert(new DayMealMap(day002Id,m024Id));

        /* Note with 1-M day 3 was empty, this relates existing meals from day 1 and day 2 to day 3 */
        mDao.insert(new DayMealMap(day003Id,m011Id));
        mDao.insert(new DayMealMap(day003Id,m021Id));


        for (DayWithRelatedMealsAlsoWithRelatedWaters d: mDao.getListOfDaysWithRelatedMealsAndRelatedWaters()) {
            logADayWithRelatedMealsAndWaters(d,"_1-M");
        }
        for (M2MDayWithRelatedMealsAlsoWithRealtedWaters m: mDao.getM2mListOfDaysWithRelatedMealsAlsoWithRelatedWaters()) {
            logM2MDayWithRelatedMealsAndWaters(m,"_M-M");
        }

    }

    private void logADayWithRelatedMealsAndWaters(DayWithRelatedMealsAlsoWithRelatedWaters dwrmawrw, String suffix) {
        Log.d(
                "DAYINFO" + suffix,
                "Day is " + dwrmawrw.day.date +
                        " Weight is " + dwrmawrw.day.weight +
                        " ID is " + dwrmawrw.day.dayId
                + " The Day has " + dwrmawrw.mealList.size() + " meal(s) "
                + " and it has " + dwrmawrw.waterList.size() + " water(s)."
        );
        for (Meal m: dwrmawrw.mealList) {
            Log.d(
                    "DAYINFO" + suffix,
                    "\tMeal is " + m.food_meal_time + " it's parent Day has the ID " + m.dayId_Meal_Map
            );
        }
        for (Water w: dwrmawrw.waterList) {
            Log.d(
                    "DAYINFO" + suffix,
                    "\tWater is " + w.water_ml + " it's parent Day has the ID " + w.dayId_Water_Map
            );
        }
    }

    private void logM2MDayWithRelatedMealsAndWaters(M2MDayWithRelatedMealsAlsoWithRealtedWaters mdwrmawrw, String suffix) {
        Log.d(
                "DAYINFO" + suffix,
                "Day is " + mdwrmawrw.day.date +
                        " Weight is " + mdwrmawrw.day.weight +
                        " ID is " + mdwrmawrw.day.dayId
                        + " The Day has " + mdwrmawrw.mealList.size() + " meal(s) "
                        + " and it has " + mdwrmawrw.waterList.size() + " water(s)."
        );
        for (Meal m: mdwrmawrw.mealList) {
            Log.d(
                    "DAYINFO" + suffix,
                    "\tMeal is " + m.food_meal_time + " it's parent Day has the ID " + m.dayId_Meal_Map
            );
        }
        for (Water w: mdwrmawrw.waterList) {
            Log.d(
                    "DAYINFO" + suffix,
                    "\tWater is " + w.water_ml + " it's parent Day has the ID " + w.dayId_Water_Map
            );
        }
    }
}
  • 注:以上仅设计为运行一次作为演示

结果(即写入日志的输出,添加空格以分割两种类型的关系):-

2022-07-08 12:20:01.522 D/DAYINFO_1-M: Day is 2022-07-08 Weight is 10.1 ID is 1 The Day has 5 meal(s)  and it has 2 water(s).
2022-07-08 12:20:01.522 D/DAYINFO_1-M:  Meal is 09:00 it's parent Day has the ID 1
2022-07-08 12:20:01.522 D/DAYINFO_1-M:  Meal is 11:00 it's parent Day has the ID 1
2022-07-08 12:20:01.522 D/DAYINFO_1-M:  Meal is 13:00 it's parent Day has the ID 1
2022-07-08 12:20:01.522 D/DAYINFO_1-M:  Meal is 18:00 it's parent Day has the ID 1
2022-07-08 12:20:01.522 D/DAYINFO_1-M:  Meal is 21:00 it's parent Day has the ID 1
2022-07-08 12:20:01.522 D/DAYINFO_1-M:  Water is 100.1 it's parent Day has the ID 1
2022-07-08 12:20:01.523 D/DAYINFO_1-M:  Water is 200.2 it's parent Day has the ID 1
2022-07-08 12:20:01.523 D/DAYINFO_1-M: Day is 2022-07-09 Weight is 11.11 ID is 2 The Day has 5 meal(s)  and it has 2 water(s).
2022-07-08 12:20:01.523 D/DAYINFO_1-M:  Meal is 09:30 it's parent Day has the ID 2
2022-07-08 12:20:01.523 D/DAYINFO_1-M:  Meal is 10:15 it's parent Day has the ID 2
2022-07-08 12:20:01.523 D/DAYINFO_1-M:  Meal is 12:45 it's parent Day has the ID 2
2022-07-08 12:20:01.523 D/DAYINFO_1-M:  Meal is 17:30 it's parent Day has the ID 2
2022-07-08 12:20:01.523 D/DAYINFO_1-M:  Meal is 20:30 it's parent Day has the ID 2
2022-07-08 12:20:01.523 D/DAYINFO_1-M:  Water is 120.12 it's parent Day has the ID 2
2022-07-08 12:20:01.523 D/DAYINFO_1-M:  Water is 220.22 it's parent Day has the ID 2
2022-07-08 12:20:01.523 D/DAYINFO_1-M: Day is 2022-07-09 Weight is 12.12 ID is 3 The Day has 0 meal(s)  and it has 0 water(s).



2022-07-08 12:20:01.527 D/DAYINFO_M-M: Day is 2022-07-08 Weight is 10.1 ID is 1 The Day has 3 meal(s)  and it has 0 water(s).
2022-07-08 12:20:01.527 D/DAYINFO_M-M:  Meal is 09:00 it's parent Day has the ID 1
2022-07-08 12:20:01.527 D/DAYINFO_M-M:  Meal is 13:00 it's parent Day has the ID 1
2022-07-08 12:20:01.527 D/DAYINFO_M-M:  Meal is 21:00 it's parent Day has the ID 1
2022-07-08 12:20:01.527 D/DAYINFO_M-M: Day is 2022-07-09 Weight is 11.11 ID is 2 The Day has 2 meal(s)  and it has 0 water(s).
2022-07-08 12:20:01.527 D/DAYINFO_M-M:  Meal is 10:15 it's parent Day has the ID 2
2022-07-08 12:20:01.527 D/DAYINFO_M-M:  Meal is 17:30 it's parent Day has the ID 2
2022-07-08 12:20:01.528 D/DAYINFO_M-M: Day is 2022-07-09 Weight is 12.12 ID is 3 The Day has 2 meal(s)  and it has 0 water(s).
2022-07-08 12:20:01.528 D/DAYINFO_M-M:  Meal is 09:00 it's parent Day has the ID 1
2022-07-08 12:20:01.528 D/DAYINFO_M-M:  Meal is 09:30 it's parent Day has the ID 2
  • 可以看出,相关数据取决于关系类型,并且有目的地应用数据来突出差异。

 类似资料:
  • 问题内容: 在关系数据库中,我有一个用户表,一个类别表和一个用户类别表,它们之间存在多对多关系。在Redis中具有这种结构的更好形式是什么? 问题答案: 使用Redis,关系通常由集合表示。一组可用于表示单向关系,因此每个对象需要一组以表示多对多关系。 尝试将关系数据库模型与Redis数据结构进行比较是毫无用处的。使用Redis,所有内容均以非规范化方式存储。 例: 一旦有了此数据结构,就可以使用

  • 问题内容: 我有以下实体关系问题。一个“游戏”必须有两个(只有两个)“团队”对象。一个“团队”可以有很多“游戏” 据我所知,这是一对多关系。但是…我不知道如何在JPA中对此建模。例如,我打算做这样的事情… 但是,正如您所看到的,我不确定如何从注释侧将表链接在一起。有人曾经做过这样的事情吗?有什么想法吗? 非常感谢! 问题答案: 我希望有人能提出一个很棒的解决方案,但是这是一个棘手的情况,我从未能找

  • 问题内容: 我有2张桌子: 电影:movieID 用户:userID 这些表通过Queue表具有多对多关系,并带有一个附加属性listOrder: 队列:movieID,userID,listOrder 我正在尝试使用EclipseLink对此模型建模,但是却收到“不兼容映射”错误。这是我的代码的示例: QueueItemPK的目的是使我可以拥有movieID和userID的复合主键。我不确定这是

  • 首先,我的英语很差,如果我的写作让人困惑,请原谅。 我试图在实例之间创建以下关系:如果,和,那么。在我的例子中,我想指定如果一个“管理”一名员工,并且与有相同的工作,那么他也管理同一名员工。 我尝试使用链属性来实现这一点,但当我激活它时,推理器(事实1.6.5)不起作用(日志显示使用了一个非简单属性)。我认为问题在于属性“manages”是不对称的和不可伸缩的,而属性“sameJob”是可传递的和

  • 基础回购 和存储库类 现在我想使用带有谓词查询的Repo。我需要形成一个谓词,在这个谓词中,我可以根据给定的Bs加载

  • 我需要创建一个学生管理系统,它可以帮助多个老师教多个学生,多个学生可以有多个老师。现在我已经在下面创建了一个代码。另外,如果你认为有更好的方法来实现我的目标,请指导,我对多对多的关系是新的,互联网上有这么多方法,这只是让人困惑: 错误是: