android - Firebase 实时数据库同时预订一个时隙(覆盖问题)
问题描述
我正在尝试制作一个使用实时数据库预订时间段的应用程序。问题是如果两个用户选择了当前空闲的某个时间段,并且他们都同时点击了书,数据被覆盖了。
我的预订技术是插入一个键 = 时间戳和值 = 用户 ID 的节点。
因此,双方都认为他们为自己保留了插槽,而实际上其中一个预订请求覆盖了数据库节点上的另一个。
我尝试使用Firebase 文档中的Save data as transactions 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);
}
});
我在这里做错了吗?
解决方案
您的代码似乎假设启动事务可以锁定某个位置,但这不是 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"
}
}
}
}
如果尚无任何值(尚未声明该插槽),或者如果用户写入是之前声明该插槽的用户(这将允许他们清除该插槽),则这将允许写入。
推荐阅读
- vba - 根据持续时间和缩进删除任务
- r - R:write_xlsx 参数中有评估问题
- python - 读取 .dat 文件并包含空格
- javascript - JavaScript:根据输入字段值将查询字符串添加到 URL(如果已设置)?
- sql-server - 连接四个时计算两个表中的值
- bcp - BCP -w 复制 0 行而没有错误消息
- php - 使用 JWT 连接到 DocuSign API 的后端计划脚本
- spring-boot - Hikari 连接没有超时
- ethereum - 无法通过 Remix 部署简单的 Solidity 合约
- python - Pyspark UDF - TypeError:“模块”对象不可调用