Let us now explore the asynchronous side of Spring Data JPA module:
- Convert ch12-asyncjpa to a Spring Boot 2.0 application by adding the Spring Boot 2.0.0.M2starter POM dependencies, such as webflux, actuator for project status monitoring and management, Spring JDBC, and MYSQL connector.
- Since there is no dedicated Spring Data JPA module for asynchronous repository transactions, add the same starter POM dependencies for Spring Data JPA to pom.xml:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency>
- Inside the core package, org.packt.microservice.core, add the following Bootstrap class:
@SpringBootApplication public class HRDeptBootApplication extends SpringBootServletInitializer { // refer to sources }
- Copy the config folder and logback.xml from the previous project to src/main/resources. Update the log file details of logback.xml.
- Inside the src/main/resources directory, add application.properties that contains the same details as the previous Spring Data JPA project. Since we will rely on the auto-increment feature of MySQL for object ID generation, always set spring.jpa.hibernate.use-new-id-generator-mappings to false to allow data persistence.
- To configure the Spring Data JPA module, add the following configuration class inside org.packt.microservice.core.config, which will enable JPA transaction management:
@Configuration @EnableJpaRepositories( basePackages="org.packt.microservice.core.dao") @EnableTransactionManagement public class SpringDataConfig { }
- Enable asynchronous features though this configuration class that also generates thread pools through Executor:
@EnableAsync @Configuration public class SpringAsynchConfig implements AsyncConfigurer { private static Logger logger = LoggerFactory.getLogger(SpringAsynchConfig.class); @Bean("mvcTaskexecutor") @Override public Executor getAsyncExecutor() { ConcurrentTaskExecutor executor = new ConcurrentTaskExecutor( Executors.newFixedThreadPool(100)); executor.setTaskDecorator(new TaskDecorator() { @Override public Runnable decorate (Runnable runnable) { return () -> { long t = System.currentTimeMillis(); runnable.run(); logger.info("creating thread pool...."); System.out.printf("Thread %s has a processing time: %s%n", Thread.currentThread().getName(), (System.currentTimeMillis() - t)); }; } }); return executor; } }
- Copy the JPA Department entity model from the previous project to the org.packt.microservice.core.model.data package.
- Now, create the DepartmentRepository interface using JpaRepository, showcasing the implementation of asynchronous properties as follows:
@Repository public interface DepartmentRepository extends JpaRepository<Department, Integer>{ @Async public Future<List<Department>> findAllByDeptid(Integer deptid); @Async public CompletableFuture<Department> findIgnoreCaseByName(String name); @Async public ListenableFuture<Department> findDeptById(Integer id); }
- Then, create a DepartmentService interface that has the following services. Drop this file inside the org.packt.microservice.core.service package:
public interface DepartmentService { public List<Department> findAllDeptList(); public Department findAllDeptById(Integer id); public List<Department> findAllByDeptName(String name); public Future<List<Department>> findAllByDeptId(Integer deptId); public CompletableFuture<Department> findAllFirstByNameIgnoreCase(String name); public ListenableFuture<Department> findAllFirstById(Integer id); }
- To implement the services, just apply the asynchronous properties from DepartmentRepository:
@Service public class DepartmentServiceImpl implements DepartmentService{ @Autowired private DepartmentRepository departmentRepository; @Override public Future<List<Department>> findAllByDeptId(Integer deptId) { return departmentRepository.findAllByDeptid(deptId); } @Override public CompletableFuture<Department> findAllFirstByNameIgnoreCase(String name) { return departmentRepository .findIgnoreCaseByName(name); } @Override public ListenableFuture<Department> findAllFirstById(Integer id) { return departmentRepository.findDeptById(id); } // refer to sources }
- We have come to the highlight of this recipe, which is the implementation of the request handlers given the asynchronous data retrieval from DepartmentRepository. These request handlers just reuse the concepts discussed in the previous chapters on how to build callbacks which retrieve the exact data from Future<T>, CompletableFuture<T>, and ListenableFuture<T>.
- Create @Controller, bearing all these request methods inside org.packt.microservice.core.controller:
@RestController public class DeptAsyncController { @Autowired private DepartmentService departmentServiceImpl; private Department result = new Department(); @GetMapping(value="/webSyncDept/{id}.json", produces ="application/json", headers = {"Accept=text/xml, application/json"}) public WebAsyncTask<Department> websyncDeptList( @PathVariable("id") Integer id){ Callable<Department> callable = new Callable<Department>() { public Department call() throws Exception { ListenableFuture<Department> listenFuture = departmentServiceImpl.findAllFirstById(id); listenFuture.addCallback( new ListenableFutureCallback<Department>(){ @Override public void onSuccess(Department dept) { result = dept; } @Override public void onFailure(Throwable arg0) { result = new Department(); } }); return result; } }; return new WebAsyncTask<Department>(500, callable); } @GetMapping(value="/deferSelectDept/{name}.json", produces ="application/json", headers = {"Accept=text/xml, application/json"}) public DeferredResult<Department> deferredSelectDept( @PathVariable("name") String name) { DeferredResult<Department> deferredResult = new DeferredResult<>(); CompletableFuture.supplyAsync(()->{ try { return departmentServiceImpl .findAllFirstByNameIgnoreCase(name) .get(500, TimeUnit.MILLISECONDS); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } catch (TimeoutException e) { // TODO Auto-generated catch block e.printStackTrace(); } return null; }).thenAccept((msg)->{ deferredResult.setResult(msg); }); return deferredResult; } // refer to sources }
- Save all files. Then clean, build, and deploy the microservice.