我正在尝试制作一个使用实时数据库预订时间段的应用程序。问题是,如果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);
}
});
我在这里做错什么了吗?
答案 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"
}
}
}
}
如果尚无值(尚未声明插槽),或者如果用户写入的是之前声明插槽的用户(这将允许他们清除插槽),则这将允许写入。
>