Room SELECT 查询 1-n hashmap 关系

Room SELECT Query for 1-n hashmap relationship

我有以下 ORM 模型:

基本上我想跟踪一个州的所有 public 设施。所以我与 public 设施有 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;
}

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

我认为您可能因为处理人际关系的方式而让自己感到焦虑。

正在考虑 StateWithFacilities

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

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

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

例子

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

State class(实体,因此 Table)是层次结构的顶部。

@Entity
public class State {
    @PrimaryKey
    Long stateId;
    String stateName;
    // etc //

    public State(){}
    @Ignore
    public State(String stateName){
        this.stateName = stateName;
    }
    .... getters and setters
}

学校 class(实体,因此 Table)将属于一个州。

@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 列上的索引,但如果定义了外键,则在索引不存在时 Room 会发出警告。

Location class(实体,因此 Table)将属于一个学校(一个 Scholl 可以有多个位置)。

@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
}
  • 为了使演示更易于阅读,已为位置命名。

为了满足检索父项及其子项的需要,使用了以下 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);
}

一个数据库classTheDatabase

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

最后和 activity 进行演示(运行 在主线程上):-

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

I want to have the implemented, rather than creating a new table/entity class unless I don't have another option.

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



额外重新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),调试 window 是 :-

第二个断点:-