How to execute SQL query in parallel using spring boot? - java

I have multiple charts in a page which will be updated with values from database.
I am making an ajax call when chart is initialized, now the request comes to controller class.
From controller class I am making multiple call to database using repository class object.
How can I make a single request to database with multiple queries and get array of response.
For e.g. Here I have made a 3 different calls to get 3 different value for a chart:
Controller Class and Repository class
How can I combine these request into single one.
There is a concept of Batching in JDBC where we can use addBatch and excuteBatch to do what I wanted, but I am not able to understand if I can achieve the same using Spring batch.

Sounds like you want to implement these as async queries.
This is covered in the Spring Data JPA documentation, I believe. You can use any of these method formats in the current version:
#Async
Future<User> findByFirstname(String firstname);
#Async
CompletableFuture<User> findOneByFirstname(String firstname);
#Async
ListenableFuture<User> findOneByLastname(String lastname);
https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#repositories.query-async
(You could also spin off separate Thread instances for each, but I wouldn't advise it)

Statement.addBatch is not supposed to be used for SELECT but for batching INSERTs and UPDATEs
What you need is a bit of a Custom query with UNION to get all the data you need in one sql.
SELECT COUNT(n.lastUpdatedOn)
FROM TableEntity n
WHERE n.lastUpdatedOn BETWEEN :start1 AND :end1
UNION
SELECT COUNT(n.lastUpdatedOn)
FROM TableEntity n
WHERE n.lastUpdatedOn BETWEEN :start2 AND :end2
UNION
SELECT COUNT(n.lastUpdatedOn)
FROM TableEntity n
WHERE n.lastUpdatedOn BETWEEN :start3 AND :end3
And your Repository code.
#Query("SELECT COUNT(n.lastUpdatedOn) FROM TableEntity n WHERE n.lastUpdatedOn BETWEEN :start1 AND :end1 UNION SELECT COUNT(n.lastUpdatedOn) FROM TableEntity n WHERE n.lastUpdatedOn BETWEEN :start2 AND :end2 UNION SELECT COUNT(n.lastUpdatedOn) FROM TableEntity n WHERE n.lastUpdatedOn BETWEEN :start3 AND :end3")
List<Long> countModifiedTimeStamp(#Param("start1") Timestamp start1, #Param("end1") Timestamp end1, #Param("start2") Timestamp start2, #Param("end2") Timestamp end2, #Param("start3") Timestamp start3, #Param("end3") Timestamp end3);
And when you call
List<Long> counts = this.repo.countModifiedTimeStamp(todayStartDay, today, last7days, today, longBack, last7days);
In the returned list you will have today at first element, last7days in second and longBack in third.

Related

How to provide variable parameters for custom quarries in spring data jpa and not use for loop

I have two databases and I have to search for a list of products based on a range(from and to) from 1st DB but the problem is the number of ranges is unknown.
currently, from 1st DB I am getting a list of range(from and to). then using for loop to query the 2nd DB.
//1st getting list of ranges
List<Ranges> ranges = this.productRepository.getRangeFromAndTo();
//query to get ranges
#Query(value = "SELECT * FROM PRODUCT_RANGE WHERE productId = ?1 AND goodToUse = ?2 AND CHECKOUT is null", nativeQuery=true)
List<Range> getRangeFromAndTo(String productId, String goodToUse);
//using for loop to query 2nd db
ranges.forEach(range ->{ productService.countProducts(range.getRangeFrom(),fnnRange.getRangeTo());});
//this is the query
#Query(value="select Count(*) from RESOURCE where RESOURCE_VALUE between ?1 and ?2 and upper(RESOURCE_STATUS)=upper('available')", nativeQuery=true)
Long countByresourceValueBetween(String fnnRangeFrom, String fnnRangeTo);
I want to query the 2nd DB at one go to reduce the time.
Because you don't know how many ranges you have in advance, the best aproach I think is to send all the queries to the second database in parallel, wrapping every call in a CompletableFuture, for example, and then combine all of them into one future to get the final result.
This way you only have to wait for the slower query instead of waiting for the sum of all queries.
Here is an example of how convert a list of completablefutures into a completablefuture of a list.
List<Future> to Future<List> sequence
When the query completes if you wat the sum of all elements you can use
result.stream().mapToLong(it->it).sum();

How to insert/update a single record using a MERGE statement with Spring JDBC

I have an update/insert SQL query that I created using a MERGE statement. Using either JdbcTemplate or NamedParameterJdbcTemplate, does Spring provide a method that I can use to update a single record, as opposed to a Batch Update?
Since this query will be used to persist data from a queue via a JMS listener, I'm only dequeuing one record at a time, and don't have need for the overhead of a batch update.
If a batch is the only way to do it through Spring JDBC, that's fine... I just want to make certain I'm not missing something simpler.
You can use a SQL MERGE statment using only a one row query containing your parameters.
For example if you have a table COMPANYcontaing IDas a key and NAMEas an attribute, the MERGE statement would be:
merge into company c
using (select ? id, ? name from dual) d
on (c.id = d.id)
when matched then update
set c.name = d.name
when not matched then insert (c.id, c.name)
values(d.id, d.name)
If your target table contains the parametrised key, the name will be updated, otherwise a new record will be inserted.
With JDBCTemplate you use the update method to call the MERGEstatement, as illustrated below (using Groovy script)
def id = 1
def name = 'NewName'
String mergeStmt = """merge into company c
using (select ? id, ? name from dual) d
on (c.id = d.id)
when matched then update
set c.name = d.name
when not matched then insert (c.id, c.name)
values(d.id, d.name)""";
def updCnt = jdbcTemplate.update(mergeStmt, id, name);
println "merging ${id}, name ${name}, merged rows ${updCnt}"
Just use one of update methods, for example this one: JdbcTemplate#update instead of BatchUpdate.
Update updates a single record, batchUpdate updates multiple records using JDBC batch

QueryDsl - creating query with subquery operating on two tables

I'm new with this API and I'm having some problems with a subquery. I'm using QueryDsl - JPA. I have two tables e.g. Film and Producer. In Producer table I have a primary key producer_id and in my Film table I also have this field as producer_id and it's connected. My goal is to count number of film entity for specific producer using QueryDsl.
I wrote something like it:
QFilm film = QFilm.film;
QProducer producer = QProducer.producer;
createQuery().from(film, producer).select(film.count())
.where(film.producerId.eq(producer.producerId)).fetch();
I'm getting a sum of all matching results, e.g. in Producer table I have 2 producers, and in Film table I have 4 films. First producer have 2 films, and second also have 2. Result of this query is 4. But I want for this particular query to return only 2 for first producer, and only 2 for second one. How to archive this?
You are not mentioning what is the relationship between the two db fields(OneToMany, OneToOne e.t.c). Also you are declaring a QFilm and QProducer instance without actually using them in the query.
Assuming that there is OneToOne relationship between producer.producer_id - film.producer_id and you are using the Querydsl's generated metamodel classes I would try this:
QFilm qFilm = QFilm.film;
long count = creatyQuery().selectFrom(qFilm).where(qFilm.producerId.eq(yourDesiredProducerId)).fetchCount();

Spring Boot generates invalid MySQL query (only_full_group_by)

I'm using Spring Boot 1.5.2 and the following #Query:
#Query(value = "SELECT m FROM Message m WHERE m.from.userId = :myId OR m.to.userId = :myId GROUP BY m.from.userId, m.to.userId ORDER BY m.date DESC")
List<Message> chatOverview(#Param("myId") final Long myUserId);
The intention of the query is to create a chat messenger overview, where you see the last message of each conversation you had. It works fine for me in dev, but in production (newer MySQL database version) I get this error:
java.sql.SQLSyntaxErrorException: Expression #1 of SELECT list is not in GROUP BY clause and contains nonaggregated column 'message0_.message_id' which is not functionally dependent on columns in GROUP BY clause; this is incompatible with sql_mode=only_full_group_by
I read in this thread what the reason for this change was, however I couldn't find a way to fix this with JPA / Spring. I cannot change any settings in the production MySQL database and I would like to avoid any upgrading in Spring either. How can I fix my query?
Here is the definition and purpose of the GROUP BY (see section 4.7) clause:
The GROUP BY construct enables the aggregation of result values according to a set of properties.
That means it is used only if you're aggregating (sum, avg, max, min, ...) the value(s) of a field(s). But in your case I don't see any aggregation function. So just remove the GROUP BY clause and everything should be fine:
SELECT m FROM Message m WHERE m.from.userId = :myId OR m.to.userId = :myId ORDER BY m.date DESC
Grouping on userId doesn't make sense too because all the entities returned by this query will have the same value for this field.

Convert Postgresql query to Hibernate

In my Java Web application I use Postgresql and some data tables are filled automatically in server. In the database I have a STATUS table like below:
I want to select the data related to a vehicle between selected dates and where the vehicle stayed connected. Simply I want to select the data which are green in the above table which means I exactly want the data when firstly io1=true and the data when io1=false after the last io1=true. I have postgresql query statement which exactly gives me the desired data; however, I have to convert it to HQL because of my application logic.
working postgresql query:
WITH cte AS
( SELECT iostatusid, mtstrackid, io1,io2,io3, gpsdate,
(io1 <> LAG(io1) OVER (PARTITION BY mtstrackid
ORDER BY gpsdate)
) AS status_changed
FROM iostatus
WHERE mtstrackid = 'redcar' AND gpsdate between '2014-02-28 00:00:00' and '2014-02-28 23:59:59'
)
SELECT iostatusId, mtstrackid, io1, io2, io3,gpsdate
FROM cte
WHERE status_changed
OR io1 AND status_changed IS NULL
ORDER BY gpsdate ;
How should I convert the above query to HQL or how could I retrieve the desired data with HQL?
The goal of hibernate is mapping database entities to java objects. This kind of complex queries are not entities themselves. This is against the spirit of hibernate.
If this query generates an entity in your application logic, I recommend putting the results into a table and applying Hibernate queries to that table.
If this query generates some kind of aggregation or summary, there are two possible ways:
One way is you compute this aggregation/summary in your application after retrieving entities from iostatus table with hibernate.
If this query has nothing to do with your application logic then you can use Native SQL interface of Hibernate and execute the query directly. (You can even use JPA if you are willing to manipulate two database connections.)
If you absolutely need to convert it to HQL, you need to eliminate the partition function. If the order of iostatusId is identical to the order of gpsdate, you can do it similar to
SELECT i2.*
FROM iostatus i1
INNER JOIN iostatus i2 ON i1.iostatusId = i2.iostatusId - 1
AND i1.io1 <> i2.io1
AND i1.mstrackid = i2.mstrackid
WHERE i2.mtstrackid = 'redcar' AND
i2.gpsdate between '2014-02-28 00:00:00' and '2014-02-28 23:59:59'
If gpsdate is no way related to iostatusId then you need something like
SELECT i2.*
FROM iostatus i1
INNER JOIN iostatus i2 ON i1.gpsdate < i2.gpsdate
AND i1.io1 <> i2.io1
AND i1.mstrackid = i2.mstrackid
WHERE i2.mtstrackid = 'redcar' AND
i2.gpsdate between '2014-02-28 00:00:00' and '2014-02-28 23:59:59' AND
NOT EXISTS (SELECT * FROM iostatus i3
WHERE i3.gpsdate > i1.gpsdate AND
i2.gpsdate > i3.gpsdate AND
i3.io1 = i1.io1 AND
i1.mstrackid = i3.mstrackid)
I guess both of the queries can be converted to HQL, but I'm not positively sure.
By the way I must warn you that, these methods might not perform better then finding the changes in your application, because they involve joining the table onto itself, which is an expensive operation; and the second query involves a nested query after the join, which is also quite expensive.

Categories

Resources