Firebase实时数据库同时预订一个时隙(覆盖问题)

时间:2019-03-30 22:37:40

标签: android firebase firebase-realtime-database

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

我的预订技术是插入一个键=时间戳和值= userID的节点。

因此,两个都认为他们为自己保留了该插槽,而实际上一个预订请求覆盖了数据库节点上的另一个请求。

我尝试使用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);
            }
        });

我在这里做错什么了吗?

1 个答案:

答案 0 :(得分:1)

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

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

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"
      }
    }
  }
}

如果尚无值(尚未声明插槽),或者如果用户写入的是之前声明插槽的用户(这将允许他们清除插槽),则这将允许写入。

>