【问题标题】:Room SELECT Query for 1-n hashmap relationship1-n hashmap 关系的 Room SELECT 查询
【发布时间】:2021-06-12 19:26:13
【问题描述】:

我有以下 ORM 模型:

基本上我想跟踪一个州的所有公共设施。所以我与公共设施有 1-n 关系,例如学校:

public class StateWithFacilities {

    @Embedded
    State  state;
    @Relation(entity = School.class,parentColumn = "schoolId",entityColumn = "id")
    ArrayList<School> allSchools;
}

问题是,我不太确定如何获取属于学校的所有位置,因为学校有一个位置的 HashMap,其中值代表建筑物的成本。

我的 1) 想法如下所示:

public class StateWithFacilities {

    @Embedded
    State  state;
    @Relation(entity = School.class,parentColumn = "schoolId",entityColumn = "id")
    ArrayList<SchoolWithLocations> allSchools;
}

public class SchoolWithLocations {
    @Embedded
    School school;
    @Relation(entity = Location.class,parentColumn = "locationId",entityColumn = "id")
    HashMap<Location, float> alllocationsWithCost;
}

提议的想法行不通,因为我无法跟踪建筑的成本。最好的解决方案是什么?我想要实现,而不是创建一个新的表/实体类,除非我没有其他选择。

【问题讨论】:

    标签: sqlite android-sqlite android-room sqliteopenhelper android-room-relation


    【解决方案1】:

    我认为你可能会因为你处理人际关系的方式而引起自己的焦虑。

    考虑到StateWithFacilities

    您似乎是在说获取 id 等于该州的 schoolId 列的学校。

    虽然关系应该来自 School 中存储适当 StateId 的列。

    您似乎在使用相反的方法。

    示例

    也许根据您似乎想要做的事情来考虑这个例子:(州、学校和地点已被赋予一个名称列,以使输出更容易理解)-

    State 类(实体和表),它是层次结构的顶部。

    @Entity
    public class State {
        @PrimaryKey
        Long stateId;
        String stateName;
        // etc //
    
        public State(){}
        @Ignore
        public State(String stateName){
            this.stateName = stateName;
        }
        .... getters and setters
    }
    

    School 类(实体和表格),将属于一个州。

    @Entity(
            foreignKeys = {
                    @ForeignKey(entity = State.class,parentColumns = "stateId",childColumns = "stateIdMap"),
            },
            indices = {@Index("stateIdMap")}
    )
    public class School {
        @PrimaryKey
        Long schoolId;
        Long stateIdMap;
        String schoolName;
        // etc
    
        public School(){}
        @Ignore
        public School(String schoolName, long stateId) {
            this.schoolName = schoolName;
            this.stateIdMap = stateId;
    
        }
        .... getters and setters
    }
    
    • 外键不是必需的,但可以帮助维护参照完整性。
    • 同样,stateIdMap 列上的索引不是必需的,但如果定义了 ForeignKeys,Room 会在索引不存在时发出警告。

    Location 类(实体和表格),属于 School(一个 Scholl 可以有多个 Locations)。

    @Entity(
            foreignKeys = {
                    @ForeignKey(entity = School.class,parentColumns = "schoolId",childColumns = "schoolIdMap")
            },
            indices = {@Index("schoolIdMap")}
    )
    public class Location {
        @PrimaryKey
        Long locationId;
        Long schoolIdMap;
        String locationName;
        float x1;
        float y1;
        float x2;
        float y2;
        // etc
    
        public Location(){}
        @Ignore
        public Location(String locationName,long schoolId, float x1, float y1, float x2, float y2) {
            this.locationName = locationName;
            this.schoolIdMap = schoolId;
            this.x1 = x1;
            this.y1 = y1;
            this.x2 = x2;
            this.y2 = y2;
        }
        .... getters and setters
    }
    
    • 为使演示更易于阅读,已为 Location 命名。

    为了满足用它的孩子检索父母的需求,使用了以下 POJO:-

    SchoolWithLocations

    public class SchoolWithLocations {
    
        @Embedded
        School school;
        @Relation(entity = Location.class,parentColumn = "schoolId",entityColumn = "schoolIdMap")
        List<Location> locationList;
    }
    

    StateWithSchoolsWithLocations

    public class StateWithSchoolsWithLocations {
    
        @Embedded
        State state;
        @Relation(entity = School.class, parentColumn = "stateId",entityColumn = "stateIdMap")
        List<SchoolWithLocations> schoolWithLocationsList;
    }
    

    A Dao AllDao 和一些常用的 Dao :-

    @Dao
    interface AllDao {
    
        @Insert
        long insert(State state);
        @Insert
        long[] insert(State...states);
        @Insert
        long insert(Location location);
        @Insert
        long[] insert(Location...locations);
        @Insert
        long insert(School school);
        @Insert
        long[] insert(School...schools);
    
        @Query("SELECT * FROM State")
        List<State> getAllStates();
        @Query("SELECT * FROM State WHERE stateId=:stateId")
        State getStateById(long stateId);
        @Query("SELECT * FROM Location")
        List<Location> getAllLocations();
        @Query("SELECT * FROM Location WHERE locationId=:locationId")
        Location getLocationById(long locationId);
        @Query("SELECT * FROM Location WHERE x1=:x1 AND y1=:y1 AND x2=:x2 AND y2=:y2")
        Location getLocationByCoords(float x1,float y1,float x2,float y2);
        @Query("SELECT * FROM School")
        List<School> getAllSchools();
    
        @Transaction
        @Query("SELECT * FROM State")
        List<StateWithSchoolsWithLocations> getStateWithSchoolsAndLocations();
    
        @Transaction
        @Query("SELECT * FROM State WHERE stateId=:stateId")
        List<StateWithSchoolsWithLocations> getStateByIdWithSchoolsAndLocations(long stateId);
    }
    

    一个数据库类TheDatabase

    @Database(entities = {State.class,Location.class,School.class},exportSchema = false,version = 1)
    abstract class TheDatabase extends RoomDatabase {
        abstract AllDao getAllDao();
    
        private static volatile TheDatabase instance;
    
        public static TheDatabase getInstance(Context context) {
            if (instance == null) {
                instance = Room.databaseBuilder(
                        context,
                        TheDatabase.class,
                        "state.db"
                )
                        .allowMainThreadQueries()
                        .build();
            }
            return instance;
        }
    }
    

    最后是要演示的活动(在主线程上运行):-

    public class MainActivity extends AppCompatActivity {
    
        TheDatabase db;
        AllDao dao;
        static final String TAG = "StateINFO";
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            //Instantiate Database and get dao
            db = TheDatabase.getInstance(this);
            dao = db.getAllDao();
            // Add 3 states
            long s1Id = dao.insert(new State("State1"));
            long s2Id = dao.insert(new State("State2"));
            // Add 2 Schools (in State1)
            long sc1 = dao.insert(new School("School1 in State1",s1Id));
            long sc2 = dao.insert(new School("School2 in State1",s1Id));
            // Add 4 Locations
            long l1Id = dao.insert(new Location("Loc1",sc1,1f,1f,2f,2f));
            long l2Id = dao.insert(new Location("Loc2",sc1,2f,2f,3f,3f));
            long l3Id = dao.insert(new Location("Loc3",sc1,3f,3f,4f,4f));
            long l4Id = dao.insert(new Location("Loc4",sc2,4f,4f,5f,5f));
    
            // Get Everything via State
            for (StateWithSchoolsWithLocations swswl: dao.getStateWithSchoolsAndLocations() ) {
                Log.d(TAG,"State is " + swswl.state.stateName);
                for (SchoolWithLocations s: swswl.schoolWithLocationsList) {
                    Log.d(TAG,"\tSchool is " + s.school.schoolName);
                    for (Location l: s.locationList) {
                        Log.d(TAG,"\t\tLocation is " + l.locationName + " XYvalues are X1=" + l.x1 + " Y1=" + l.y2 + " X2=" + l.x2 + " Y2=" + l.y2);
                    }
                }
            }
        }
    }
    

    结果

    可以看出,检索所有位置和 x1..Y2 值很容易。运行上述日志时,日志包括:-

    2021-06-13 08:53:40.748 D/StateINFO: State is State1
    2021-06-13 08:53:40.748 D/StateINFO:    School is School1 in State1
    2021-06-13 08:53:40.748 D/StateINFO:        Location is Loc1 XYvalues are X1=1.0 Y1=2.0 X2=2.0 Y2=2.0
    2021-06-13 08:53:40.748 D/StateINFO:        Location is Loc2 XYvalues are X1=2.0 Y1=3.0 X2=3.0 Y2=3.0
    2021-06-13 08:53:40.748 D/StateINFO:        Location is Loc3 XYvalues are X1=3.0 Y1=4.0 X2=4.0 Y2=4.0
    2021-06-13 08:53:40.748 D/StateINFO:    School is School2 in State1
    2021-06-13 08:53:40.748 D/StateINFO:        Location is Loc4 XYvalues are X1=4.0 Y1=5.0 X2=5.0 Y2=5.0
    2021-06-13 08:53:40.748 D/StateINFO: State is State2
    

    我想要实现,而不是创建一个新的表/实体类,除非我没有其他选择。

    上述内容是否可用于进行适当的更正以保留当前表格是您必须确定的。



    额外的 HashMaps

    SchoolWithLocations POJO 中添加的方法:-

    public HashMap<String,Float> getLocationsAsHashMap() {
        HashMap<String,Float> rv = new HashMap<>();
        for (Location l: locationList) {
            String basekey = this.getClass().getSimpleName() + (rv.size() + 1);
            rv.put(basekey+"x1",l.x1);
            rv.put(basekey+ "y1",l.y1);
            rv.put(basekey+"x2",l.x2);
            rv.put(basekey+"y2",l.y2);
        }
        return rv;
    }
    

    添加到学校

    的方法
    public HashMap<String,Float> getLocationsAsHashMap(AllDao dao) {
        HashMap<String,Float> rv = new HashMap<>();
        for(Location l: dao.getLocationsBySchool(schoolId)) {
            String basekey = this.getClass().getSimpleName() + (rv.size() + 1);
            rv.put(basekey+"x1",l.x1);
            rv.put(basekey+ "y1",l.y1);
            rv.put(basekey+"x2",l.x2);
            rv.put(basekey+"y2",l.y2);
        }
        return rv;
    }
    
    • 注意细微差别。由于 School 对象不包含位置,因此需要从数据库中检索这些位置。因此,它需要一个 AllDao 的实例,因为它使用一个 dao 来获取位置。

    AllDao

    以下内容已添加到 AllDao 以方便获取学校的适用地点:-

    @Query("SELECT * FROM location WHERE schoolIdMap=:schoolId")
    List<Location> getLocationsBySchool(long schoolId);
    

    遍历检索到的 StateWithSchoolsWithLocations 列表

    的修正循环
        // Get Everything via State
        HashMap<String,Float> locations = new HashMap<>(); //<<<<< ADDED
        HashMap<String,Float> locationsFromSchool = new HashMap<>(); //<<<<<ADDDED
        for (StateWithSchoolsWithLocations swswl: dao.getStateWithSchoolsAndLocations() ) {
            Log.d(TAG,"State is " + swswl.state.stateName);
            for (SchoolWithLocations s: swswl.schoolWithLocationsList) {
                Log.d(TAG,"\tSchool is " + s.school.schoolName);
                for (Location l: s.locationList) {
                    Log.d(TAG,"\t\tLocation is " + l.locationName + " XYvalues are X1=" + l.x1 + " Y1=" + l.y2 + " X2=" + l.x2 + " Y2=" + l.y2);
                }
                /* ADDED get HashMap of Locations */
                locations = s.getLocationsAsHashMap();
                /* OR */
                locationsFromSchool = s.school.getLocationsAsHashMap(dao);
                Float value = 99.99999F; //<<<<< ADDED for setting a breakpoint
            }
        }
    

    修改后的代码结果

    Float value = 99.99999F 行添加断点并在调试模式下运行。

    当断点第一次被命中(第一个 StateWithSchoolsAndWithLocations)时,调试窗口是:-

    第二个断点:-

    【讨论】:

    • 感谢您的详细回答。如果我没有哈希图(请参阅我的问题中的图表)而不是位置列表,您的解决方案将起作用。这就是我的担心。如果学校有 HashMap allLocations,你将如何管理?还需要另一个 POJO/Entity 类吗?
    • 要存储哈希映射,您需要类型转换器。 hashmap 不能直接存储到 Room 中。不需要另一个仅实体/表的类型转换器。您可以在 SchoolWithLocations 和或 School 中拥有返回哈希图的方法。答案包括两个和一个修改过的活动来演示但是,我会使用原始选项的简单选项,它可以满足检索数据的需要,而不会增加处理哈希图的复杂性。
    • 哦,我明白了,谢谢。尽管将 hashmap 存储为 json 会很糟糕,以防某个位置被删除,因为 hashmap 键值对不会在数据库中被删除,因为它是一个普通的 json 字符串
    猜你喜欢
    • 2022-01-13
    • 1970-01-01
    • 1970-01-01
    • 2018-08-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多