When the user clicks a button it's going to check if the Document in Firestore is exists or not, if it is, then just update (increase) some value, and if it isn't create a new document with the new value.
I could just do the normal documentRef.get(), and documentRef.set(), but when I tested it with 2 user press the button at the same time. Its just put the new value of one of them. and so I used transaction.
In this transaction, first I get the documentsnapshot, and I get the value.
and then I put if statement to check if the document is exist, the first statement which is document is exist is working fine, but when I deleted the document, the else statement didn't do anything.
Is it even possible to use if statement inside Firebase Firestore transaction?
Map<String, Object> newDocumentSet = new HashMap<>();
newDocumentSet.put("sold", 1);
newDocumentSet.put("price", myProductPrice);
mDb.runTransaction(new Transaction.Function<Void>() {
#Nullable
#Override
public Void apply(#NonNull Transaction transaction) throws FirebaseFirestoreException {
DocumentSnapshot myDocumentSnapshot = transaction.get(myDocumentRef);
long newAddSold = myDocumentSnapshot.getLong("sold") + 1;
long newAddPrice = myDocumentSnapshot.getLong("price") + myProductPrice;
if(myDocumentSnapshot.exists()){
transaction.update(myDocumentRef, "sold", newAddSold);
transaction.update(myDocumentRef, "price", newAddPrice);
}else {
transaction.set(myDocumentRef, newDocumentSet);
}
return null;
}
});
I don't know what's happening, it didn't show any error, please tell me if I made some mistake or there is another way of doing this.
or there is another way of doing this..
Yes there is, even a simpler one which in fact is more efficient than a transaction because it does not require a round trip between the client and Firebase servers. Assuming that the mDb points to the correct document, to increment the sold property by 1, please use the following line of code:
mDb.update("sold", FieldValue.increment(1));
All you need is to return a Map<String, dynamic> value from the runTransaction function and inside the run transaction make sure you return a map value like
if (a) {
return "value":true
} else {
return "value":false
}
Then in the return statement of your function assign mapDynamic["value"];. Then you will now which part of your code was executed. mapDynamic is just a variable name.
Related
Pardon me as I'm quite a beginner in coding. I have tried researching for ways to add some missing record into the lists but still can't seem to fit it correctly into my code.
I have two ArrayLists with different resultsets. Say, the first one is derived in other method and stored in abcList. This list is then used in my current fixChartStats method as a param.
In my code, I will check for the corresponding record in abcList with the second list I derive from the hql query in fixChartStats method.
If the record corresponds, then I'll do the necessary action as shown below to update the ApprovedCount number etc, else i set it to 0.
How do I go about adding the records that are missing in second list I got into the first arraylist (i.e. abcList)? Can anyone here shed some light? Do let me know if my questions are unclear. Thanks in advance, guys!
private void fixChartStats(List<TAbcModel> abcList, Map<String, Object> param, List<IssueModel> issueList, List<DestModel> destList) throws Exception {
//initialize the hql query
//translate all fields from Object[] into individual variable
firstRow = true;
for (TAbcModel abc : abcList) {
if (abc.getId().getAbcYear() = abcYear &&
abc.getId().getAbcMonthId() = abcMonthId &&
abc.getId().getAbcApplAccnId().getAccnId().equalsIgnoreCase(abcApplAccnId.getAccnId()) {
if (firstRow) {
abc.setApprovedCount(abcApprovedCount);
abc.setCancelledCount(abcCancelledCount);
firstRow = false;
} else {
abc.setApprovedCount(0);
abc.setCancelledCount(0);
}
}else{
// How to do the necessary here
// Below is what I've tried
abcList.add(abc);
}
}
}
When I debug, I noticed that it was added into the list. But soon after it was added, ConcurrentModificationException was thrown.
Create a local list and add missing records to it then add all elements from the local list to the abcList
List<TAbcModel> temp = new ArrayList<>();
in your loop:
} else {
temp.add(abc);
}
after loop
abcList.addAll(temp);
Here is my sample code.In this example has only elementary types,no structure types has to set.But in the output no data exists in the table.
When I check the records in SAP it contains multiple records for this particular id.Can someone explain this to me?
public void invokeRFC(JCoDestination destination) {
JCoFunction function=null;
try
{
JCoFunctionTemplate functionTemplate = destination.getRepository().getFunctionTemplate("RFC_METHOD");
if (functionTemplate != null) {
function = functionTemplate.getFunction();
}
if (function == null)
throw new RuntimeException("Not found in SAP.");
//to fill elementary types and structures
configureImportParameters(function,"xxx", "abc");
//to fill table type parameters
configureTableParameters(function, "tblName",1,"100");
function.execute(destination);
} catch (JCoException e)
{
e.printStackTrace();
}
}
public void configureTableParameters(JCoFunction function, String table_name, int index, String id) {
JCoTable table = function.getTableParameterList().getTable("table_name");
table.appendRow();
table.setRow(index);
table.setValue("Partner", "100");
}
private void exportTable(JCoFunction jCoFunction, String tblName) {
JCoTable resultTable = jCoFunction.getTableParameterList().getTable(tblName);
int value = resultTable.getNumRows();
System.out.println(value);
}
private void configureImportParameters(JCoFunction function, String param1, String param2) {
JCoParameterList parameterList =
function.getImportParameterList();
parameterList.setValue("field1", param1);
parameterList.setValue("field2", param2);
}
UPDATED the code.
multiple problem can cause this.
if you setting "" or " " to fields. (when you set values better set if those has some values
if it says partner does not exist and if you sure its exist this mean your data does not pass properly. add debug points to where you set the data and make sure you pass correct name and correct values.
also you do not need to add(index) you can just table.appendRow(); // but this will not impact on your case
also when you setValue make sure its int filed. (normally not) in your given example its int
eg:
private void configureTableParameters(JCoParameterList tableParameters){
JCoTable jCoTable=tableParameters.getTable(key);
jCoTable.appendRow();
if(value!=null)
jCoTable.setValue(fieldKey,String.valueOf(value));
}
this is just psuda code and will not work
Test your ABAP remote function module with an SAP GUI via transaction code SE37 first.
If this test is successful and you get a different result if called from JCo with using the same parameter values, then I recommend to study SAP note 206068 for possible reasons.
Also check your method configureTableParameters. I guess, index shall be a field index and not a row count. Your implementation will create far too many unnecessary rows. I assume you wanted to call table.appendRow(); instead of table.appendRows(index);. Moreover, you maybe intended to fill the first field in the row with the value "100", for which you would have to pass the index value 0 instead of 1 in this case.
I am trying to use runTransaction() of Firebase database but it is not working. Here is the code I am using.
numQuestionRef.runTransaction(new Transaction.Handler() {
#Override
public Transaction.Result doTransaction(MutableData mutableData) {
String numQuestions = (String) mutableData.getValue();
long value = Long.parseLong(numQuestions, 16);
value++;
String incHex = Long.toHexString(value);
mutableData.setValue(incHex);
return Transaction.success(mutableData);
}
#Override
public void onComplete(FirebaseError firebaseError, boolean b, DataSnapshot dataSnapshot) {
}
});
This code is activated when I press a button within my app. When I press the button for the first time since launching the app, my database does not change. But when I press the button the second time since launching the app, it updates it to the next number. I don't understand what it wrong or why it only does it on the second button press.
You'll want to follow the pattern used in the Firebase documentation for handling transactions and handle the case where there's no current value your transaction callback:
public Transaction.Result doTransaction(MutableData currentData) {
long value = 0;
if(currentData.getValue() != null) {
String numQuestions = (String) currentData.getValue();
value = Long.parseLong(numQuestions, 16);
}
value++;
String incHex = Long.toHexString(value);
currentData.setValue(incHex);
return Transaction.success(currentData);
}
The reason you need to do this is that Firebase may (and often will) execute your transaction callback multiple times and you need to cater for that.
When you first call runTransaction() the Firebase client will immediately invoke your doTransaction() callback with its current guess for the current data. Quite often this will be null.
Your code returns the next value based on the current value. In the case above, if the current value was null the new value will be 1.
The Firebase client then sends both the assumed current value and the new value to the server.
If the actual stored value is the same as the assumed current value, the Firebase server writes the new value you specified.
If the actual stored values is different from the assumed current value, the Firebase server rejects the new value and sends the actual current value to the client.
At this stage the client goes back to step 1, with the now updated assumed current value.
If this does not explain the behavior you're seeing, you might want to check what values are passed into onComplete().
I have a table Users in MySQL includes 3 fields : ID (int), Name (varchar) and UpdateTime (TimeStamp).
I have a java web application and one function to get one record in table Users, after that, check if UpdateTime is before current time ---> update UpdateTime by current time plus 2 hours.
And my problem is when having about 100 users access to my app and request to that function to get the same record in the same time, all of them will update UpdateTime. But I want only first user update UpdateTime, latecomers will only get data without update UpdateTime.
How I can do it? I really have no idea.
Thanks.
P/s: Sorry about my English, it seems to be not good :(
Your update time code should be in synchronised block.
Take write lock on your object and if someone has consumed lock before going on update operation and at the same time another request comes should get the pre loaded/after update data.
Chech synchronised keyword in java also check read write lock mechanism. This might help you.
following code might help you.
import java.util.Map;
import java.util.HashMap;
public class Test {
boolean isWriteLockTaken;
Map<Integer,String> cachedUsers = new HashMap<Integer,String>();
public static void main(String[] args) throws Exception {
Test t = new Test();
for(int i=0;i<10;i++){
t.updateAndGetUser(1);
}
}
public String updateAndGetUser(Integer id) throws InterruptedException {
if(isWriteLockTaken && !cachedUsers.containsKey(id)){
this.wait();
}
if(!cachedUsers.containsKey(id)){
isWriteLockTaken = true;
synchronized(id){
if(id==1){
// do processing to retrive user
System.out.println("Updating & loading user");
cachedUsers.put(id, "Gaurav");
return "Gaurav";
}
}
isWriteLockTaken = false;
}
System.out.println("returning from cached list, not loading user details as it is already loaded");
return cachedUsers.get(id);
}
}
Thanks,
Gaurav
I'm attempting to create a method that will allow me to retrieve a value from the last row of a database, and then insert it into an EditText field. (This is a value that the user will not change all that often, so it would be helpful if when they do set it, it stays set when they come back to it).
My method, based on a similar method I have for getting the total of a specific column, is as such:
public String getBase() {
Cursor mCursor = mDb.rawQuery(
"SELECT base FROM table constants ORDER ID desc limit 1", null);
if (mCursor.moveToFirst()) {
return mCursor.getString(0);
}
return mCursor.getString(0);
Like I said, I based this on a similar method, which I found after searching around the Internet. I understand most of it, but I have no idea what the 0's in the return statements mean (or the moveToFirst method).
Anyway, in my OnClickListener (the button the user would press to save this value to db), I have the following (editBase is the EditText field I want to populate):
editBase.setText(cDbHelper.getBase());
If I run the program without this statement, it works fine and the value saves to the db. As soon as I try to run it with this, I get a force close. Any suggestions?
Thanks.
EDIT: Thanks for the responses. With some guidance from a friend, I ended up using "ORDER BY... desc limit 1" instead. This was the final method:
public double getBase() {
final double DEFAULT_BASE = 0;
Cursor mCursor = mDb.rawQuery(
"SELECT base FROM constants ORDER BY _id desc limit 1", null);
if (mCursor.getCount() == 0)
return DEFAULT_BASE;
else
mCursor.moveToFirst();
return mCursor.getDouble(0);
1) For querying the DB may be try this one:
https://developer.android.com/reference/android/database/sqlite/SQLiteDatabase.html
SQLiteDatabase has query(String table, String[] columns, String selection, String[] selectionArgs, String groupBy, String having, String orderBy, String limit) method.
At least with this one you don't need to construct your SQL manually.
2) Check the doc for Cursor:
http://developer.android.com/reference/android/database/Cursor.html#getString(int)
0 is a columnIndex.
You are probably trying to update a View when running a job on a separate thread, this almost always triggers a force close.
In this article, Painless Threading, you learn how to break of your long running work into another thread (and how you can "come back" to it). Basically you can tell the GUI thread (or event queue or ui thread) that you need a Runnable to run with access to your controls and views.
private void clicked() {
// called from a click of a button for instance
new Thread(new Runnable() {
public void run() {
// the long running job should be executed on a different
// thread as to not stall the ui thread
runLongWork();
}
}
}
private void runLongWork() {
// this is method running on separate thread
// so we should run a runnable on the ui thread instead to update our view's.
this.runOnUiThread(new Runnable() {
public void run() {
// Now we are back in the ui thread
editBase.setText(cDbHelper.getBase());
}
}
}
As for the other questions Arhimed already answered it partly. When you execute a query to the Sqlite database you get an instance of a class Cursor that sort of is a list of the results or rows that your query fetched. Using the moveToFirst method you position the cursor at the first row of results. You can then get the values from the columns in your query using the getXYZ() methods (for instance getString()) and pass an index of the column that you want. 0 in this case refers to base, the first column in your SQL query.
I you have more than one row you can use the moveToNext method to get the next row, and since it returns a bool stating that it actually "found" a next row, you can use it in a while loop to get all your rows.
while (cursor.moveToNext()) {
// next row of data, use getString, getInt or others
}
Your query altough will always return only one value, since you limit the results to 1, so no need to use the moveToNext. moveToFirst will, like moveToNext, return true if a row was found, and false if there are no more rows available.
Your statement is as follows; you execute a query, move to the first row, and if it was successful return the string from the first column in the query (index starts at zero). If it doesn't find a row, you return the first column as a string, which will probably fail though. You should perhaps return a default value or throw an exception in that case.