Firebase Realtime Database无需HashMap实现实体类字段映射读取
问题描述
我通过带构造函数的类向Firebase Realtime Database写入数据,但读取时文档说明使用自定义类需要为每个元素创建HashMap。由于要写入255条以上的数据,我定义了User和User1两个类,每个类都编写HashMap过于繁琐。想请教两个问题:
- 如何让数据库变量名与类中变量名一致(例如类中
int b = 0对应数据库中b: 0) - 无需HashMap,怎么读取数据库中每个变量的值
写入数据代码
if (user != null) { if(database.child(getEmail)==null) { User newUser = new User(getEmail, coins, ....); User1 newUser1 = new User1(a256, a257, ....); database.child(getEmail).push().setValue(newUser1); database.child(getEmail).push().setValue(newUser); } }
读取数据代码
ValueEventListener valueEventListener = new ValueEventListener() { @Override public void onDataChange(@NonNull DataSnapshot snapshot) { for(DataSnapshot ds : snapshot.getChildren()){ User user = ds.getValue(User.class); //将读取的数据库信息保存到SharedPreferences PreferenceConfig.GetEmail(getApplicationContext(), getEmail); PreferenceConfig.GetCoin(getApplicationContext(), getEmail); PreferenceConfig.GetA256(getApplicationContext(), a256); ... } } }; database.addValueEventListener(valueEventListener);
数据库JSON结构
{ "User": { "mail@gmail:com": { "-NHTVinbEVUAqJwK8Umt": { "getEmail": "mail@gmail.com", "coins": 100, ..... }, "-NHTVinpCPOJ4UPZvgpN": { "a256":0, "a257":0 ............... } } } }
解决方案
一、实现数据库变量名与类变量名一致
Firebase Realtime Database的Java SDK默认支持自动映射,不需要手动写HashMap,只要遵循以下规则:
- 类必须包含无参构造函数:SDK通过反射实例化类时必须用到,哪怕是空实现也得写
- 字段设置为public,或提供标准getter方法:
- 若字段是
public int coins;,数据库键名自动对应coins - 若字段是私有,写
public int getCoins(),SDK会自动去掉get前缀并将首字母小写,对应数据库键coins
- 若字段是
- 避免冗余命名:比如你代码里的
getEmail字段,数据库会生成同名键;如果想让数据库键是email,直接把字段改成public String email;即可
以User类为例,正确的定义方式:
public class User { // 公共字段,直接映射数据库同名键 public String getEmail; public int coins; // 其他字段... // 必须存在的无参构造 public User() {} // 带参构造,用于写入数据时初始化 public User(String getEmail, int coins, ...) { this.getEmail = getEmail; this.coins = coins; // 初始化其他字段 } }
User1类按照同样规则定义即可。
二、无需HashMap直接读取数据
只要类符合上面的规则,直接用ds.getValue(YourClass.class)就能完成自动映射,无需HashMap。针对你的代码,需要调整读取逻辑:
优化读取逻辑
因为你在同一个节点下push了User和User1两种对象,读取时需要判断当前节点对应的类类型:
ValueEventListener valueEventListener = new ValueEventListener() { @Override public void onDataChange(@NonNull DataSnapshot snapshot) { for(DataSnapshot ds : snapshot.getChildren()){ // 先尝试解析为User类 User user = ds.getValue(User.class); if (user != null && user.getEmail != null) { // 保存User类字段到SharedPreferences PreferenceConfig.GetEmail(getApplicationContext(), user.getEmail); PreferenceConfig.GetCoin(getApplicationContext(), user.coins); continue; } // 再尝试解析为User1类 User1 user1 = ds.getValue(User1.class); if (user1 != null) { // 保存User1类字段 PreferenceConfig.GetA256(getApplicationContext(), user1.a256); PreferenceConfig.GetA257(getApplicationContext(), user1.a257); // 处理其他字段... } } } }; database.addValueEventListener(valueEventListener);
可选:优化写入逻辑
你代码里database.child(getEmail)==null的判断无效,Firebase的child()方法永远不会返回null,判断节点是否存在应该用exists()或异步监听。另外,push()会生成随机ID,如果你想区分User和User1的数据,建议用固定键名存储,避免循环判断类型:
if (user != null) { DatabaseReference userRef = database.child(getEmail); userRef.child("basicInfo").setValue(new User(getEmail, coins, ...)); userRef.child("extendedInfo").setValue(new User1(a256, a257, ...)); }
这样读取时可以直接定位到basicInfo和extendedInfo节点,逻辑更清晰高效。
内容的提问来源于stack exchange,提问作者Dmitriy Parkhomenko
相关产品推荐
相关产品推荐

