I would like to ask what is the best approach to run a long process using Spring. I have a webapp and when the client does a request it runs a Spring controller. This Controller would get some parameters from the request and then runs a query and fetch records from the DB.
The records from the DB are high, i need to do a comparing logic which may take a long time, so I need to run it separately.
When this process is executed , it should write the final results into an excel file and mail it.
You can use the annotation #Async to return immediately.
Fisrt, write a #Service class to process you DB and Excel job.
#Service
public class AccountService {
#Async
public void executeTask(){
// DB and Excel job
}
}
Then, In controller method trigger the task
#Controller
public class taskController{
#RequestMapping(value = "as")
#ResponseBody
public ResultInfo async() throws Exception{
accountService.executeTask();
return new ResultInfo(0, "success", null);
}
}
At last, add this to application-context.xml(spring config file)
<task:annotation-driven executor="taskExecutor"/>
<task:executor id="taskExecutor" pool-size="10"/>
Hope this will help you.
Related
I have a requirement to write a simple application to write some values to DB.
Basically this is some repetitive task which has to be done quite often and I want to build a simple Spring boot app with a UI exposed so that it can be done in an automatic way.
I have an Entity Class with a simple POJO MyClient and I have written a Controller and Service Classes and am able to GET and POST To DB:
My App.properties looks like below:
spring.datasource.url=jdbc:oracle:thin:#db-host-1:1521/xxx.xx.intern
spring.datasource.username=root
spring.datasource.password=root
//Controller Class
#GetMapping("/clients")
public List<MyClient> retrieveAllClientVersions(){
return myClientService.listAllClientVersions();
}
#RequestMapping(value = "/client/add", method = RequestMethod.POST)
#ResponseBody
void addNewClientVersion(#RequestBody MyClient myClient){
myClientService.addNewClientVersion(myClient);
}
//Service Class
private MyClientRepository myClientRepository;
#Autowired
public MyClientService(MyClientRepository myClientRepository){
this.myClientRepository=myClientRepository;
}
public List<MyClient> listAllClientVersions(){
List<MyClient> myClients=new ArrayList<>();
myClientRepository.findAll().forEach(myClients::add);
return myClients;
}
public void addNewClientVersion(MyClient myClient){
myClient.setReleaseKeyVersion(RELEASE_KEY_VERSION);
myClient.setClientVersion(myClient.getClientVersion());
myClient.setDescription(DESCRIPTION);
myClient.setReleaseCertDn(DGV_RELEASE_CERT_DN);
myClient.setStatus(STATUS);
myClient.setClientSecurityProfileDbId(CLIENT_SECURITY_PROFILE_DB_ID);
myClient.setIssuerDbId(ISSUER_DB_ID);
myClientRepository.save(myClient);
}
We have around 50 test environments where I need to run the same query. I wanted to create a UI where I can have check boxes against all environment with buttons like GET and POST.
Whatever environment user selects from check boxes and say POST the "Insert" should run on all those environments.
How can this be handled? Is there a way that based on Query Parameter in POST Request the Insert can be run on different DB. How do we connect to different DB at runtime? What could be best way to do this?
I have a question about Spring #Async annotation.
I have a controller autowired a service(GnInsuranceDetailsService)
#RequestMapping(value="/agn/AP0W01A_010/insertDetail.do")
public ResponseEntity<?> insertDetail(#ModelAttribute("dto")GnInsuranceDetailsDTO dto,HttpSession session) throws Exception {
gnInsuranceDetailsDTO.setBkFlag(getAgnUserInfo(session).getBkFlag());
gnInsuranceDetailsService.insertGnInsuranceDetail(dto);//open another thread to insert data
MessageDTO dto = new MessageDTO(AgnConstant.INSERT_SUCCESS);
return new ResponseEntity<MessageDTO>(dto,HttpStatus.OK);// increase return time for client
}
And the Service insertGnInsuranceDetail method I declare #Async up method.
#Transactional(readOnly = false)
#Async
public void insertGnInsuranceDetail(GnInsuranceDetailsDTO gnInsuranceDetailsDTO) throws Exception{
GnInsuranceDetails entity = gnInsuranceDetailsDTO.convert();
gnInsuranceDetailsDAO.save(detailsEntity);
}
I put the #Async for the service method to increase controller response time for client side,but it does not work as I think.
Do I lose somethings?Or How can I modify in the easiest way to use?
You would not loose anything, when you put #Async in the method service will be executed in a different thread, Controllers insertDetail method will not be returned until your insertGnInsuranceDetail is returned or thrown any exception.
I put the #Async for the service method to increase controller response time for client side,but it does not work as I think.
#Async - Annotation that marks a method as a candidate for asynchronous execution. Can also be used at the type level, in which case all of the type's methods are considered as asynchronous.
This #Async annotation will not help you in delaying the response time. To introduce a delay, use Thread.sleep(milliseconds);
If you want main thread(controller) to wait for insert data thread to get a result (successful/failure) you can just invoke db layer code from the controller. Whereas if you want the client response to be sent earlier, then create a new thread inside the controller and use that thread to insert data. In the latter approach of creating a new thread for insert data your client will not know about the status of data insertion, since the thread is created in the controller and it will end in the controller, no feedback/response can be given to the client, since we will not be having client details.
Hope it Helps!
Reference: https://www.tutorialspoint.com/java/lang/thread_sleep_millis.htm
#Async should be first enabled in your #Configuration bean:
#Configuration
#EnableAsync
public class AppConfiguration {
[...]
}
for xml configuration add this: <task:annotation-driven>
I am using spring jpa transactions in my project.One Case includes inserting a data in a synchronized method and when another thread accesses it the data is not updated.My code is given below :
public UpdatedDTO parentMethod(){
private UpdatedDTO updatedDTO = getSomeMethod();
childmethod1(inputVal);
return updatedDTO;
}
#Transactional
public synchronized childmethod1(inputVal){
//SomeCodes
//Place where update takes place
TableEntityObject obj = objectRepository.findByInputVal(inputVal);
if(obj == null){
childMethod2(inputVal);
}
}
#Transactional
public void childMethod2(inputVal){
//Code for inserting
TableEntityObject obj = new TableEntityObject();
obj.setName("SomeValue");
obj.setValueSet(inputVal);
objectRepository.save(obj);
}
Now if two threads access at the same time and if first thread completes childmethod2 and childmethod1 and without completing parentMethod() after that if second thread comes to the childMethod1() and checks if data exists,the data is null and is not updated by first thread.I have tried many ways like
#Transactional(propagation = Propagation.REQUIRES_NEW)
public synchronized childmethod1(inputVal){
//SomeCodes
//Place where update takes place
TableEntityObject obj = objectRepository.findByInputVal(inputVal);
if(obj == null){
childMethod2(inputVal);
}
}
#Transactional(propagation = Propagation.REQUIRES_NEW)
public void childMethod2(inputVal){
//Code for inserting
TableEntityObject obj = new TableEntityObject();
obj.setName("SomeValue");
obj.setValueSet(inputVal);
objectRepository.save(obj);
}
also tried taking off #transactional in the childMethod1() but nothing works out.I know im doing something wrong here , but couldnt figure out where and what exactly i am doing wrong.Can anyone help me out with this
#Transactional is resolved using proxies on spring beans. It means it will have no effect if your method with #Transactional is called from the same class. Take a look at Spring #Transaction method call by the method within the same class, does not work?
The easiest would be moving those methods into separate service.
Typical checklist I follow in cases like these :
If Java based configuration then make sure
#EnableTransactionManagement annocation is present in the class
containing the #Configuration annotation
Make sure the transactionManager bean is created, again this should be mentioned in the configuration class.
Use of #Transactional annocatio over the method which is calling the repository, typically a class in the DAO layer
Adding the #Service annotation for the class which is invoking the methods in the repository
Nice blog which explains the Transaction configuration with JPA in depth --> http://www.baeldung.com/2011/12/26/transaction-configuration-with-jpa-and-spring-3-1/68954
I have one query on Spring Batch Job.
I want to share data of one job with another job in same execution context. Is it possible? If so, then how?
My requirement is caching. I have file, where some data is stored. My job runs daily and need data of that file. I don't want to read file by my job daily. instead of it, I want to store data of file in cache(Hash Map). So when same job runs next day, it will use data from cache only. Is it possible in spring batch?
Your suggestion are welcome.
You can use spring initialize bean which initializes your cache at startup.
Add initialize bean to your application context;
<bean id="yourCacheBean" class="yourpackage.YourCacheBean" init-method="initialize">
</bean>
YourCacheBean looks like;
public class YourCacheBean {
private Map<Object, Object> yourCache;
public void initialize() {
//TODO: Intialize your cache
}
}
Give the initialize bean to the itemReader or itemProcessor or itemWriter in job.xml;
<bean id="exampleProcessor" class="yourpackage.ExampleProcessor" scope="step">
<property name="cacheBean" ref="yourCacheBean" />
</bean>
ExampleProcessor looks like;
public class ExampleProcessor implements ItemProcessor<String, String> {
private YourCacheBean cacheBean;
public String process(String arg0) {
return "";
}
public void setCacheBean(YourCacheBean cacheBean) {
this.cacheBean = cacheBean;
}
}
Create a job to import file into database. Other jobs will use data from database as a cache.
Another way may be to read file into a Map<> and serialize object to a file than de-serialize when need (but I still prefer database as cache)
Spring have a cache annotation that may help that kind of case and it is really easy to implement. The first call to a method will be executed, afterwards if you call the same method with exactly the same arguments, the value will be returned by the cache.
Here you have a little tutorial: http://www.baeldung.com/spring-cache-tutorial
In your case, if your call to read the file is always with the same arguments will work as you want. Just take care of TTL.
I have controller and long run function in it, like:
#Controller
#RequestMapping("/deposit")
public class DepositController {
#RequestMapping
public ModelAndView getNewJob(long userId, Model model) {
//execute function that can runs a lot of time ...
longRunFunction();
return new ModelAndView("jobTasks");
}
public void longRunFunction(){
// process long run function
}
}
My question is :
How can I execute the longRunFunction()
and return ModelAndView("jobTasks") answer to the browser without waiting for the end of the function?
Thank you !
Hi, I found nice example here http://krams915.blogspot.co.il/2011/01/spring-3-task-scheduling-via.html
This can be done using Asynch support in Spring Framework, essentially delegate the long running task to another service, the method of which is annotated with #Async annotation, this task would then be executed by a threadpool and control will return back to your caller immediately.
Here is much more detailed reference: http://docs.spring.io/spring-framework/docs/3.2.3.RELEASE/spring-framework-reference/html/scheduling.html#scheduling-annotation-support-async
public class SampleBeanImpl implements SampleBean {
#Async
void longRunFunction() { … }
}
Add #Async to the method declaration of longRunningMethod. But to make this work without AspectJ weaving you need to put this method in an other bean.