You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Firebase实时数据库添加设备前校验存在性:事务实现异常

问题描述

Structure of firebase database

我希望在向Firebase实时数据库添加特定设备前,先检查该设备是否已存在。曾尝试使用ValueEventListener和SingleValueEventListener,但二者均存在竞态条件问题。改用Firebase事务后,方案仍未生效。

我的代码如下:

Devices devices=new Devices(devicename,timein,email,timeout);

myRef.child("Device-1").runTransaction(new Transaction.Handler() {
    @Override
    public Transaction.Result doTransaction(MutableData mutableData) {
        if (mutableData.getValue() != devicename) {
            myRef.child("Device-1").push().setValue(devices);
            // Data does not exist, add it
            return Transaction.success(mutableData);
        }
        return Transaction.abort();
    }

    @Override
    public void onComplete(DatabaseError error, boolean committed, DataSnapshot snapshot) {
        if (error != null) {
            // Handle error
        }
    }
});

原本预期数据库中已有相同devicename的条目时,第二次不会添加数据,但实际未达到该效果。


问题分析

代码存在两个核心问题:

  1. 事务逻辑失效:在事务内部直接调用myRef.child("Device-1").push().setValue(devices),相当于绕过事务的原子控制,在事务外部执行写入操作,完全失去了事务的意义。
  2. 存在性判断错误:mutableData.getValue() != devicename的逻辑不成立——mutableData是Device-1节点下的所有数据集合,直接与设备名字符串对比完全不匹配;且用push()生成随机ID节点后,无法通过Device-1根节点直接判断指定设备是否存在。

正确解决方案

方案1:用设备名作为节点键(推荐)

利用Firebase的原子写入特性,将设备名作为节点的唯一键,既能避免竞态条件,又能高效判断设备是否存在:

// 仅在设备不存在时写入,不覆盖已有数据
myRef.child("Device-1").child(devicename).runTransaction(new Transaction.Handler() {
    @Override
    public Transaction.Result doTransaction(MutableData mutableData) {
        if (mutableData.getValue() == null) {
            // 设备不存在,写入数据
            mutableData.setValue(devices);
            return Transaction.success(mutableData);
        }
        // 设备已存在,终止事务
        return Transaction.abort();
    }

    @Override
    public void onComplete(DatabaseError error, boolean committed, DataSnapshot snapshot) {
        if (error != null) {
            // 处理错误
        } else if (committed) {
            // 设备成功添加
        } else {
            // 设备已存在,未执行添加
        }
    }
});

如果允许覆盖已有设备数据,可直接用原子写入,无需事务:

myRef.child("Device-1").child(devicename).setValue(devices, new DatabaseReference.CompletionListener() {
    @Override
    public void onComplete(DatabaseError error, DatabaseReference ref) {
        if (error != null) {
            // 写入失败处理
        } else {
            // 写入成功,可通过监听判断是否为新添加设备
            ref.addListenerForSingleValueEvent(new ValueEventListener() {
                @Override
                public void onDataChange(DataSnapshot snapshot) {
                    boolean isNewDevice = snapshot.getChildrenCount() == 1;
                    // 根据业务逻辑处理
                }

                @Override
                public void onCancelled(DatabaseError error) {
                    // 错误处理
                }
            });
        }
    }
});

方案2:保留push()随机ID的场景

若业务必须使用随机ID节点,需在事务内遍历所有子节点检查设备名:

myRef.child("Device-1").runTransaction(new Transaction.Handler() {
    @Override
    public Transaction.Result doTransaction(MutableData mutableData) {
        boolean deviceExists = false;
        // 遍历子节点检查是否存在同名设备
        for (MutableData child : mutableData.getChildren()) {
            Devices existingDevice = child.getValue(Devices.class);
            if (existingDevice != null && existingDevice.getDevicename().equals(devicename)) {
                deviceExists = true;
                break;
            }
        }
        if (!deviceExists) {
            // 生成随机ID并添加新设备
            String newKey = myRef.child("Device-1").push().getKey();
            mutableData.child(newKey).setValue(devices);
            return Transaction.success(mutableData);
        }
        return Transaction.abort();
    }

    @Override
    public void onComplete(DatabaseError error, boolean committed, DataSnapshot snapshot) {
        if (error != null) {
            // 错误处理
        } else if (committed) {
            // 添加成功
        } else {
            // 设备已存在
        }
    }
});

注意:此方案在Device-1节点数据量较大时性能会下降,优先推荐方案1。

内容的提问来源于stack exchange,提问作者laos

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.08 09:05:16