Ваш код, по-видимому, предполагает, что запуск транзакций дает вам блокировку местоположения, но это не так, как работают транзакции в 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"
}
}
}
}
Это позволяет выполнять запись, если либо еще нет значения (слот не имеетбыло заявлено), или если пользователь пишет тот, кто требовал слот раньше (что позволило бы им очистить слот).