How to update multiple row with SQLite "IN" operator? - java

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.

Related

Java Spring / Reactor send / update flux when underlying database changed

I have a data for candidate "likes", which I'd like to send to client every time the "like" number is changed. I think this is achievable using Spring Flux? But I can't find any example for this. Most flux example is based on specific interval (e.g. every second). This might be a waste, because the transaction is not that much, and a candidate might not get likes in many minutes.
I just want to create dashboard that subscribe to "likes" change, and get updated when certain candidate "likes" number changed.
What is the way to get this?
This is what I did, and it works, but it based on interval (5 seconds), not based on data change.
public Flux<Candidate> subscribeItemChange(String id) {
return Flux.interval(Duration.ofSeconds(5)).map(t -> candidateService.getCandidateDetail(id));
}
The candidateService.getCandidateDetail is basically query database for certain id, so this is more like polling instead of "update on change".
I think I must put something on candidateService.updateLikes() below, but what should I update?
public class CandidateService {
public Candidate getCandidateDetail(String id) {
// query candidate from database
// select * from candidates where id = :id
// and return it
}
public void updateLikes(String id, int likesCount) {
// update candidates set likes_count = :likesCount where id = :id
// ...
// I think I need to write something here, but what?
}
}
You could make use of a dynamic sink, adding a field similar to:
private Sinks.Many<Candidate> likesSink = Sinks.many().multicast().onBackpressureBuffer();
...then you can:
Use sink.tryEmitNext in your updateLikes() method to publish to the sink whenever likes are updated for a candidate;
Implement your subscribeItemChange() method which uses likesSink.asFlux(), which can then be filtered if necessary to only return the stream of "like updates" for a particular candidate.
Based on #Michael Berry guide.
public void updateLikes(String id, int likesCount) {
Candidate c = getCandidateDetail(id);
c.setLikesCount(likesCount);
CandidateDummyDatasource.likesSink.tryEmitNext(c);
}
On subscriber
public Flux<Candidate> subscribeItemChange(String id) {
return CandidateDummyDatasource.likesSink.asFlux()
.filter(c -> c.getId().equals(id))
.map(data -> candidateService.getCandidateDetail(id));
}

How to handle when call requests at same time

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

How to Iterate Datatable with type List<Class> in Cucumber

I have below feature file with Given annotation
Given user have below credentials
|user |password |
|cucumber1 |cucumber |
|cucumber2 |cucumber |
And i'm created below datamodel
public Class DataModel{
public string user;
public String password;
}
Trying to fetch data into the cucumber stepdefinition as below
Public Class stepdefinition {
#Given("^user have below credentials$")
Public void user_have_below_credintials(List<DataModel> dm){
//Iterator or foreach is required to fetch row,column data from dm
}
}
Please help me how can I Iterate object 'dm' to get row and column values
// The old way
for (int i = 0; i < dm.size(); i++) {
DataModel aDataModel = dm.get(i);
String username = aDataModel.user;
String password = aDataModel.password;
}
// A better way if java5+
for (DataModel aDataModel : dm) {
String username = aDataModel.user;
String password = aDataModel.password;
}
// another way if java8+
dm.forEach(aDataModel -> {
String username = aDataModel.user;
String password = aDataModel.password;
});
Note that the variables won't be available outside the loop with the way I wrote it. Just a demonstration of iterating and accessing the properties of each DataModel in your list.
A thing to keep in mind is that you're describing your list of DataModel objects as a data table. But it's not a table, it's simply a collection of values contained in an object, which you have a list of. You may be displaying it, or choosing to conceptualize it as a data table in your head, but the model that your code is describing isn't that, which means you aren't going to iterate through it quite like a table. Once you access a "row", the "columns" have no defined order, you may access them in any order you want to the same effect.

Creating a endpoint to return a list of objects from a database using Spring

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;
}

Hibernate caches the query while data is being changed

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.

Categories