I am using Spring boot create a restful for save data to Database.
Now I have problem when you call this service in the same time my data its duplicated in database because previous request not finish yet.
I generate number like 00001 I need to running its from database
I get last from DB
then 00001+1 = 00002
save to Database
but when request same time in database save 00002 for 2 records.
#Transactional
public List<Object> saveData(String data) {
//validate data
//Get Last Data
//set prepare data
//save data
int idLatest = Integer.parseInt(getLatest("7", "8"));
List<Object> objects = autoGenarateEntity(idLatest);
Repository.save(Object);
}
public String getLatest(String idFirst, String idSecond){
Optional<Object> running = Repository.findByBIdStartingWithOrderByBIdDesc(idFirst, idSecond).stream().findFirst();
if(running.isPresent()){
String bId =running.get();
return bId.getBId();
}else {
return "70000000";
}
}
public List<Object> autoGenarateEntity(int idLatest){
List<Object> objects = new ArrayList<>();
IntStream.range(1, 5 + 1).forEach(i -> {
Object obj = new Object();
obj.setBId(Integer.toString(idLatest + i));
obj.add(Object);
});
return objects;
}
Following are the options that can be done in this case:
1.) have unique key constraint for the column having the value of 00001. So that, the second transaction which was trying to commit same value would roll-back.
And you could use Spring-Retry mechanism to execute the second transaction with updated value
2.) Use a custom sequence Generator to increment the value, instead of handling it yourself.
I have a solution for this
#Transactional(isolation = Isolation.SERIALIZABLE)
and use spring retry
Related
In my android application, I save some events that I must sent to my server. All events that are sent, I must mark as "sent".
The server, after I sent all events not marked (sent = 0), reply with an array of all ids that it has saved. Then, in android application, I execute an update query with all ids, but it don't work.
EventDao.java
#Dao
public interface EventDao {
#Query("UPDATE Event SET sent = 1 WHERE id IN (:ids)")
void updateSent(String ids)
}
MarkEvent.java
...
DatabaseClient.getInstance(context).getDb().eventDao().updateSent(response.join(","));
Log.d("MarkEvent", response.join(","));
...
"response" is a JSONArray with only integer.
When the Server reply, I can read this in my Logcat:
D/MarkEvent: 2,3,4,5,6,7,8
this number are the correct ids of record
You have to replace String (ids) with List.
So Your method will look like:
void updateSent(List<Integer> ids)
Someone already got similar problem:
https://stackoverflow.com/a/53632880/5612090
I read the "#Query" notation documentation better: Room Query Annotation
From documentation:
Room supports binding a list of parameters to the query. At runtime, Room will build the correct query to have matching number of bind arguments depending on the number of items in the method parameter.
So I have changed my Dao like that
#Dao
public interface EventDao {
#Query("UPDATE Event SET sent = 1 WHERE id IN (:ids)")
void updateSent(int[] ids)
}
and in my class MarkEvent.java I have added a static function (from this How to cast a JSONArray to int Array) and I have modified the call:
public static int[] JSonArray2IntArray(JSONArray jsonArray){
int[] intArray = new int[jsonArray.length()];
for (int i = 0; i < intArray.length; ++i) {
intArray[i] = jsonArray.optInt(i);
}
return intArray;
}
...
DatabaseClient.getInstance(context).getDb().eventDao().updateSent(JSonArray2IntArray(response));
...
Now Work! Thank You.
I have a little issues, I have make a request to my database (Firebase) to get a set of ids, these ids are live inside a subcollection. When these ids are returned, I will use these ids to get the objects they correspond to. Here is my code
List<DocumentSnapshot> temp = input.getDocuments();
for (DocumentSnapshot snapshot: temp) {
String journeyId = snapshot.get("id").toString();
//make another request to database
//get the result and add to returnobject
}
return returnObject;
Where input is of type QuerySnapshot. I would really like to not return the returnObject till all the objects have been returned from the database. I have a background with Promises in node and using Promise.map is there an equivalent in Android?
I have a database and I want to use a endpoint to get the data. But I want to filter the data so that only certain values are returned. What I want is to call the endpoint and then get the data that I wanted out of it. I have made two methods one for calling all the data and another for calling only 1 record in the database. they both working good, but I want to now get multiple records from the database. This is what I have so far:
//This get every record
#RequestMapping(
value = API_PREFIX_1_0 + ENDPOINT_coupon + "/getCoupon",
method = RequestMethod.GET)
public Collection<Coupon> couponGetAll()
{
return couponService.getAll();
}
//this get only one record based on the ID of the table
#RequestMapping(
value = API_PREFIX_1_0 + ENDPOINT_coupon + "/{id}",
method = RequestMethod.GET)
public Coupon couponGetById(#PathVariable(value = "id") final long id) {
return couponService.getById(id);
}
What I want to do is use an array or a list of id to get the data from the server.
Thank you for any help with this
The spring CrudRepository already provides a method to find items by a set of ids:
Iterable<T> findAll(Iterable<ID> ids)
this method should be triggered by your service if you are using a CrudRepository in your persistence layer.
Then you could add a request parameter to your couponGetAll() method where to get the ids and send it to your service.
#RequestMapping( value = API_PREFIX_1_0 + ENDPOINT_coupon + "/listOfCoupons", method = RequestMethod.GET)
public Iterable<Coupon> couponGetMine(#RequestParam(name="ids")String ids) {
Iterable<Long> longIds = convertToLong(ids);
return couponService.getAll(ids);
}
The client side url to call would look something like this:
.../listOfCoupons?ids=2,4,7,3
In the endpoint you extract the numbers out of the string. Maybe there is a nicer solution but this is what is in my mind in this short time.
Convert the id string e.g.:
public Iterable<Long> convertToLong(final String ids){
String[] idArray = ids.split(",");
List<Long> idsAsLong = new ArrayList<>();
for (int i = 0; i < idArray.length; i++) {
idsAsLong.add(Long.parseLong(idArray[i]));
}
return idsAsLong;
}
So I am a noob at Android, and I'm writing a simple app that uses Google Fit to store the users fitness sessions and step count and then retrieve them.
I have two methods, one that fetches all the sessions from a given date range from the cloud, the next method iterates through these and adds up the step count.
Problem is, that although I call the the fetching method first, the result doesn't come back until after I've added the steps up, so step count is always zero.
Here's my code:
private ArrayList<> results;
#Override
public ArrayList<IndividualSession> readAllSessions(Date dateFrom, Date dateTo) {
/* I haven't included the following code in this question just to keep things clean, but here there was
- The initialisation of the results ArrayList
- Creating the calendar and date objects
- Building the session read request
*/
Fitness.SessionsApi.readSession(mGoogleApiClient, readRequest).setResultCallback(new ResultCallback<SessionReadResult>() {
#Override
public void onResult(SessionReadResult sessionReadResult) {
for (Session session : sessionReadResult.getSessions()) {
List<DataSet> dataSets = sessionReadResult.getDataSet(session);
for (DataSet dataSet : dataSets) {
for (DataPoint dataPoint : dataSet.getDataPoints()) {
// Create new IndividualSession object, add data to it then add it to arraylist
IndividualSession individualSessionObject = new IndividualSession();
individualSessionObject.setFromDate(new Date(session.getStartTime(TimeUnit.SECONDS)));
individualSessionObject.setToDate(new Date(session.getEndTime(TimeUnit.SECONDS)));
individualSessionObject.setStepCount(dataPoint.getValue(Field.FIELD_STEPS).asInt());
results.add(individualSessionObject);
}
}
}
Log.i(TAG, "Number of sessions found while reading: "+results.size());
}
});
return results;
}
#Override
public int getDaySteps(Date dateTo) {
int stepCount = 0; // to be returned
// Sort out the dates
Calendar calFrom = Calendar.getInstance();
calFrom.add(Calendar.HOUR, -24);
// Get the sessions for appropriate date range
ArrayList results = readAllSessions(calFrom.getTime(), dateTo);
Log.i(TAG, "Number of sessions found while trying to get total steps: "+results.size());
// Iterate through sessions to get count steps
Iterator<IndividualSession> it = results.iterator();
while(it.hasNext())
{
IndividualSession obj = it.next();
stepCount += obj.getStepCount();
}
return stepCount;
}
This outputs
"Number of sessions found while trying to get total steps: 0"
"Number of sessions found while reading: 8"
There are two solutions to this :
Option 1 : Use a blocking colleciton
Change the ArrayList<> results to an ArrayBlockingQueue<> results.
After the call to the readAllSessions method in the getDaySteps method, call while(results.take()!=null) { //rest of the logic }
You need some kind of mechanistm to exit the while loop in step 2 when all results are read
Option 2 : Use the await method from PendingResult
Looking at the documentation for SessionsAPI class, the readSessions method seems to return a PendingResult :
public abstract PendingResult readSession
(GoogleApiClient client, SessionReadRequest request)
Reads data from the user's Google Fit store of the specific type(s)
and for the specific session(s) selected from the request parameters.
Looking at the documentation of the await method in PendingResult class :
public abstract R await ()
Blocks until the task is completed. This is not allowed on the UI thread. The returned result object can have an additional failure mode
of INTERRUPTED.
This is what you can do. Instead of chaining the entire call to setResultCallBack, first call readSessions :
results = Fitness.SessionsApi.readSession(mGoogleApiClient, readRequest);
And then wait for the results in the getDaySteps method :
SessionReadResults sessionResults = results.await();
for (Session session : sessionReadResult.getSessions()) {
List<DataSet> dataSets = sessionReadResult.getDataSet(session);
for (DataSet dataSet : dataSets) {
for (DataPoint dataPoint : dataSet.getDataPoints()) {
// Create new IndividualSession object, add data to it then add it to arraylist
IndividualSession individualSessionObject = new IndividualSession();
individualSessionObject.setFromDate(new Date(session.getStartTime(TimeUnit.SECONDS)));
individualSessionObject.setToDate(new Date(session.getEndTime(TimeUnit.SECONDS)));
individualSessionObject.setStepCount(dataPoint.getValue(Field.FIELD_STEPS).asInt());
//use the results
}
}
}
*results must be declared as an instance/class level variable to be accessible in all the methods in the class. The variable result is of type PendingResult<SessionReadResults>. Also, looks like you can do away with the results ArrayList since everything you want can be extracted from the SessionReadResults returned by the await method. One last note, this answer has not been tested with your code because your code sample is not complete.
I'm developing an application which connects to an outside service to fetch new SMS. Theses messages are stored in a local database by Hibernate. My client can search these messages based on numerous parameters such as time, number, and etc.
After calling the search method with a list of parameters called 'List1', I get the desired result without any problems. Though while I'm waiting for this result a new message has arrived.
Soon after, I call the search method with same parameter list again and I'm expecting to get the new message as well, but I get the previous result.
I have checked my database and the new message is present so all I can think of is Hibernate caching. Since both queries are exactly the same, I guess hibernate return the same result set as before.
In case my assumption is correct, how can I overcome this problem? If not, so what exactly is going on?
Edit
here is relevant part of my source code. Following two methods will be invoked when client initiates a search request:
smsService.refresh();
JSONArray result = smsService.retrieveMessages(...);
#Transactional
public JSONArray retrieveMessages(Long periodBegining, Long periodEnd, String order, Integer limit, String profile, Boolean unread, String correspondent) {
List<ShortMessage> messageList = shortMessageDAO.find(beginDate, endDate, order, limit, profile, unread, correspondent);
JSONArray result = new JSONArray();
for (ShortMessage message : messageList)
result.put(message.toJSON());
shortMessageDAO.markRead(messageList);
return result;
}
#Transactional
public void refresh() {
webService.authentication(serviceUsername, servicePassword);
while(webService.hasUnread() > 0) {
SMS sms = webService.retrieveMessage();
ShortMessage message = new ShortMessage(sms.getHash(), sms.getFrom(), sms.getTo(), "DEFAULT", sms.getMessage(), new Date(sms.getTime()), true);
shortMessageDAO.insert(message);
}
}
}
public List<ShortMessage> find(Date beginDate, Date endDate, String order, Integer limit, String profile, Boolean unread, String correspondent) {
Criteria criteria = sessionFactory.getCurrentSession().createCriteria(ShortMessage.class);
criteria.add(Restrictions.ge("time", beginDate));
criteria.add(Restrictions.le("time", endDate));
criteria.add(Restrictions.eq("profile", profile));
if (unread)
criteria.add(Restrictions.eq("unread", true));
if (correspondent != null)
criteria.add(Restrictions.eq("origin", correspondent));
criteria.addOrder(order.equals("ASC") ? Order.asc("time") : Order.desc("time"));
criteria.setMaxResults(limit);
criteria.setCacheMode(CacheMode.IGNORE);
return (ArrayList<ShortMessage>) criteria.list();
}
Yes it looks like hibernate is caching your query and returning cached results.
Please give us a overview of your code to suggest better.
Below listed are two ways of controlling the caching behaviour of queries:-
1) At the main named query level:-
#NamedQuery(
name = "myNamedQuery"
query = "SELECT u FROM USER WHERE u.items is EMPTY"
hints = {#QueryHint(name = "org.hibernate.cacheMode", value = "IGNORE")}
)
2) At individual query level :-
Query q = session.createQuery("from User")
.setCacheMode(CacheMode.IGNORE);
After running numerous test, I found out that this problem is not cache related at all. Upon receiving each message I would have stored the time of arrival based on data provided by SMS panel and not my own machine time.
There was a slight time difference (20 seconds to be exact) between those 2 which was the reason behind the query not returning the new received message.