【问题标题】:Firebase Real Time database simultaneous booking of a time slot (Overwritting problem)Firebase 实时数据库同时预订一个时隙(覆盖问题)
【发布时间】:2019-03-30 22:37:39
【问题描述】:

我正在尝试制作一个使用实时数据库预订时间段的应用程序。问题是如果两个用户选择了当前空闲的某个时间段,并且他们都同时点击了书,数据被覆盖了。

我的预订技巧是插入一个键 = 时间戳和值 = 用户 ID 的节点。

因此,双方都认为他们为自己保留了空位,而实际上其中一个预订请求覆盖了数据库节点上的另一个。

我尝试使用 firebase 文档 Here 中的将数据另存为事务。但是,插槽预订仍然会相互覆盖。

这是我的代码:

                dbRef.child(String.valueOf(selectedDate.getTime())).runTransaction(new Transaction.Handler() {
            @Override
            public Transaction.Result doTransaction(MutableData mutableData) {
                BookingSlot s = mutableData.getValue(BookingSlot.class);
                if (s == null) {
                    //Upload new BookingSlot
                    dbRef.child(String.valueOf(selectedDate.getTime())).setValue(s);

                          );
                    }
                    return Transaction.success(mutableData);
                } else {
                    Toast.makeText(BookingActivity.this,"Slot has just been booked!",Toast.LENGTH_LONG).show();
                    //The chosen time has just been booked,

                }
                return Transaction.abort();
            }

            @Override
            public void onComplete(DatabaseError databaseError, boolean b,
                                   DataSnapshot dataSnapshot) {
                // Transaction completed
                Log.e("Booking", "postTransaction:onComplete:" + databaseError);
            }
        });

我在这里做错了吗?

【问题讨论】:

    标签: android firebase firebase-realtime-database


    【解决方案1】:

    您的代码似乎假设启动事务会为您锁定某个位置,但这不是 Firebase 中事务的工作方式。相反,Firebase 中的事务使用比较和设置逻辑:客户端告诉您(它认为)当前值是什么,然后您通过返回新值告诉它在这种情况下新值变成什么。

    因此,您应该在MutableData 中返回s,而不是在该位置上调用.setValue(s)

    dbRef.child(String.valueOf(selectedDate.getTime())).runTransaction(new Transaction.Handler() {
        @Override
        public Transaction.Result doTransaction(MutableData mutableData) {
            BookingSlot s = mutableData.getValue(BookingSlot.class);
            if (s == null) {
                mutableData.setValue(uid); // TODO: pass in the UID of the user who's claiming this slot
                return Transaction.success(mutableData);
            } else {
                Toast.makeText(BookingActivity.this,"Slot has just been booked!",Toast.LENGTH_LONG).show();
                return Transaction.abort();
            }
        }
    
        @Override
        public void onComplete(DatabaseError databaseError, boolean b, DataSnapshot dataSnapshot) {
            Log.e("Booking", "postTransaction:onComplete:" + databaseError);
        }
    });
    

    使用上面的代码,客户端不会覆盖彼此的值。

    但使用 Firebase,您始终必须考虑到恶意用户可能会针对您的数据库编写自己的代码,因为他们可以在您应用的 APK 中找到配置数据。因此,您还应该在 Firebase 的服务器端安全规则中强制每个插槽只能被声明一次。

    如果预订存储在/bookings 下,可以通过以下方式完成:

    {
      "rules": {
        "bookings": {
          "$timeslot": {
            ".write": "data.val() === null || data.val() === auth.uid"
          }
        }
      }
    }
    

    如果还没有值(插槽尚未被声明),或者写入的用户是之前声明过该插槽的用户(这将允许他们清除该插槽),这将允许写入。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-07-01
      • 2021-04-09
      • 1970-01-01
      • 2023-03-06
      • 1970-01-01
      • 2022-10-14
      相关资源
      最近更新 更多