peter peter - 24 days ago 6
Java Question

How to make the spring mvc controller to update multiple tables?

I have a spring mvc web and I want to perform some tables' update. When I click on the link, I have to wait the task to be completed in order to perform another task. Each link is correspond to different table, how would I improve this if I want to speed up process.
enter image description here

controller class

public class AdminController{
@Autowired
public SessionFactory sessionFactory;

@Autowired
private ThreadPoolTaskExecutor taskExecutor;

@Autowired
public HashMap<Long, ICollector> collectors;

@RequestMapping(value = "/config/configcollectlist",method = RequestMethod.GET)
public String getConfigCollectist(ModelMap model)
{
Session session = sessionFactory.openSession();
List<DbConfigCollect> configCollects = (List<DbConfigCollect>)session.createCriteria(DbConfigCollect.class).list();
session.close();

model.addAttribute("configCollects", configCollects);
return "admin/config/configcollectlist";
}

@RequestMapping(value = "/config/configcollectorlist",method = RequestMethod.GET)
public String getConfigCollectorist(ModelMap model)
{
Session session = sessionFactory.openSession();
List<DbConfigCollector> configCollectors = (List<DbConfigCollector>)session.createCriteria(DbConfigCollector.class).list();
session.close();

model.addAttribute("configCollectors", configCollectors);
return "admin/config/configcollectorlist";
}
}


task class

public class CollectorPropertyRED_UK_PPD implements ICollector{
@Autowired
private ThreadPoolTaskExecutor taskExecutor;

@Autowired
public SessionFactory sessionFactory;

@Override
public void collect(DbConfigCollect inDbConfigCollect)
{
taskExecutor.execute(new CollectorPropertyRED_UK_PPDTask(sessionFactory, inDbConfigCollect));
}
}

public class CollectorPropertyRED_UK_PPDTask extends CollectorTaskBase {
public enum COLLECT_PARAMETER_PAPER_COREACUK_FILE {
FILE_INPUT,
};

public final static String FILE_SUFFIX_CSV = ".csv";

public CollectorPropertyRED_UK_PPDTask(SessionFactory sessionFactory,
DbConfigCollect inDbConfigCollect) {
super(sessionFactory, inDbConfigCollect);
}

public void run() {
String[] parameters = this.myDbConfigCollect.getParameters().split(";");

// load file
File[] sourceFiles = new File(
parameters[COLLECT_PARAMETER_PAPER_COREACUK_FILE.FILE_INPUT
.ordinal()]).listFiles();

int totalCount = 0;
for (int i = 0; i < sourceFiles.length; i++) {

if (sourceFiles[i].getName().endsWith(FILE_SUFFIX_CSV)) {
String timeStamp = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss").format(Calendar.getInstance().getTime());
String fileName = sourceFiles[i].getName();

CSVReader reader = null;
int count = 0;
try {
reader = new CSVReader(new FileReader(sourceFiles[i]));
String[] currLine = null;
SimpleDateFormat dateFormat = new SimpleDateFormat(
"yyyy-MM-dd HH:mm");

while ((currLine = reader.readNext()) != null) {
String unique_id = currLine[0];
++count;
updateToDb(sessionFactory,currLine);

}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (reader != null) {
try {
reader.close();
} catch (Exception e) {
}
}
}
}
}
}
}


Updated:
I have tried to follow the async request guide, but it won't work as expected. When I refresh the browser using the same url, I get the response of the controller immediately, but the doSlowWork() method need to wait until the previous task has been finished. here are my code.

controller class

@Autowired
HelloService helloService;
Logger logger = LoggerFactory.getLogger(AdminController.class);
@RequestMapping(value ="/helloAsync", method = RequestMethod.GET)
public Callable<String> sayHelloAsync() {
System.out.println("Entering controller");

Callable<String> asyncTask = new Callable<String>() {

@Override
public String call() throws Exception {
return helloService.doSlowWork();
}
};

return asyncTask;
}


asynctask class

@Service
public class HelloService {
Logger logger = LoggerFactory.getLogger(HelloService.class);

public String doSlowWork() {

Random rand = new Random();

int n = rand.nextInt(50) + 1;

System.out.println("start: "+n);

try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}

System.out.println("end: "+ n);
return "index"; // return view's name
}
}


Output

INFO 2016-11-16 15:49:37,706 [http-bio-8080-exec-16] com.exbidata.db.mvc.controller.admin.AdminController - Entering controller
INFO 2016-11-16 15:49:37,710 [http-bio-8080-exec-16] com.exbidata.db.mvc.controller.admin.AdminController - Leaving controller
INFO 2016-11-16 15:49:37,720 [taskExecutor-1] com.exbidata.db.mvc.controller.admin.HelloService - Start slow work
INFO 2016-11-16 15:49:38,201 [http-bio-8080-exec-13] com.exbidata.db.mvc.controller.admin.AdminController - Entering controller
INFO 2016-11-16 15:49:38,202 [http-bio-8080-exec-13] com.exbidata.db.mvc.controller.admin.AdminController - Leaving controller
INFO 2016-11-16 15:49:38,975 [http-bio-8080-exec-15] com.exbidata.db.mvc.controller.admin.AdminController - Entering controller
INFO 2016-11-16 15:49:38,976 [http-bio-8080-exec-15] com.exbidata.db.mvc.controller.admin.AdminController - Leaving controller
INFO 2016-11-16 15:49:47,721 [taskExecutor-1] com.exbidata.db.mvc.controller.admin.HelloService - finish slow work
INFO 2016-11-16 15:49:47,723 [taskExecutor-1] com.exbidata.db.mvc.controller.admin.HelloService - Start slow work
INFO 2016-11-16 15:49:57,724 [taskExecutor-1] com.exbidata.db.mvc.controller.admin.HelloService - finish slow work
INFO 2016-11-16 15:49:57,726 [taskExecutor-1] com.exbidata.db.mvc.controller.admin.HelloService - Start slow work
INFO 2016-11-16 15:50:07,726 [taskExecutor-1] com.exbidata.db.mvc.controller.admin.HelloService - finish slow work

Answer

You should use Spring asynchronous request processing feature instead,

With Servlet 3.0 the async support is added. First you need to enable the async support in your web.xml as follows,

<servlet>
  <servlet-name>dispatcher</servlet-name>
  <servlet- class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <load-on-startup>1</load-on-startup>
    <!-- turn on async support for servlet -->
    <async-supported>true</async-supported>
</servlet>

From your existing controller handler method instead of returning string return callable

  @RequestMapping(value = "/config/configcollectlist",method =   RequestMethod.GET)
  public Callable<String> getConfigCollectist(ModelMap model) 
  {
    Callable<String> asyncTask = new Callable<String>() {

       @Override
       public String call() throws Exception {

       Session session = sessionFactory.openSession();
       List<DbConfigCollect> configCollects = (List<DbConfigCollect>)session.createCriteria(DbConfigCollect.class).list();
       session.close();
       model.addAttribute("configCollects", configCollects);

       return "admin/config/configcollectlist"; 
     }
   };

  return asyncTask;
 } 

Also you need to configure spring application context for async support. (I have mentioned XML based configuration)

<mvc:annotation-driven> 
   <mvc:async-support default-timeout="30000" task-executor="taskExecutor"/>
</mvc:annotation-driven>
<!-- modify the parameters of thread pool -->
<bean id="taskExecutor" class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor">
  <property name="corePoolSize" value="5"/>
  <property name="maxPoolSize" value="50"/>
  <property name="queueCapacity" value="10"/>
  <property name="keepAliveSeconds" value="120"/>
</bean>