Microservices communicating via RestTemplate
Before we start writing code for user-service-A . We need to understand that in user service we have parameters like userId ,firstName, lastName ,email and departmentId.
--> userId ,firstName, lastName ,email are parameters of user microservice
--> departmentId is id that corresponds to a particular department from department-microservice-A.
And with the help of departmentId and RestTemplate a request will be send from user service to department service and a response will be recieved from department-service.
Basically
Microservice1(user-service-A) acts as a client that sends a request and waits for a response from Microservice2(department-service-A).
This is also called Synchronous Communication, the client sends a request and waits for a response from the service.
We can use RestTemplate or WebClient or Spring Cloud Open Feign library to make a Synchronous Communication multiple microservices.
Image 1 - Communication via RestTemplate
NOTE:
Make sure both microservices are not running on same port in my case
department-microservice-A is running on port 8080 (default port for Tomcat Server) no need to configure in application .properties
user-microservice-A is running on port 8081 need to configure in application .properties of user microservice
------------------------------------------------------------------------------------------------------
Lets understand how we are going to create a microservice that is communicating with other microservice.
1- First create a bean for RestTemplate in main application Method.
src/main/java --> com.zeek.userserviceA --> UserServiceAApplication.java
package com.zeek.userserviceA;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;
@SpringBootApplication
public class UserServiceAApplication {
public static void main(String[] args) {
SpringApplication.run(UserServiceAApplication.class, args);
}
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
2- Be sure to give the id of another microservice which in this case is departmentId in Entity of our microservice(user).
Entity Class
src/main/java --> com.zeek.userserviceA.entity --> User.java
package com.zeek.userserviceA.entity;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
@Entity
public class User {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long userId;
private String firstName;
private String lastName;
private String email;
private Long departmentId;
//Constructors using SuperClass
public User() {
super();
}
//Constructors using Fields
public User(Long userId, String firstName, String lastName, String email, Long departmentId) {
super();
this.userId = userId;
this.firstName = firstName;
this.lastName = lastName;
this.email = email;
this.departmentId = departmentId;
}
//Getters and Setters
public Long getUserId() {
return userId;
}
public void setUserId(Long userId) {
this.userId = userId;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public Long getDepartmentId() {
return departmentId;
}
public void setDepartmentId(Long departmentId) {
this.departmentId = departmentId;
}
}
3- Write your repository interface(userRepository) for userId only.
Repository Interface
src/main/java -->com.zeek.userserviceA.repository -> UserRepository.java
package com.zeek.userserviceA.repository;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import com.zeek.userserviceA.entity.User;
@Repository
public interface UserRepository extends JpaRepository<User,Long> {
User findByUserId(Long userId);
}
4- Generate a package Name vo(value object) in which there are classes which we need to generate our desired Response format.
--> In this project we need response for user and department together when we use getUserWithDepartment in service layer.The format generated must be like this
{
"user": {
"userId": 1,
"firstName": "Zee",
"lastName": "Khan",
"email": "zee@gmail.com",
"departmentId": 1
},
"department": {
"departmentId": 1,
"departmentName": "English",
"departmentAddress": "First Cross,1st Street",
"departmentCode": "EN-006"
}
}
So two classes are needed
VO package - Department.java and ResponseTemplate.java
--> Department Class
- In user service this class will be same as in department service because we will set its value for input id corresponding to the id generated in department service using RestTemplate.
package com.zeek.userserviceA.VO;
public class Department {
private Long departmentId;
private String departmentName;
private String departmentAddress;
private String departmentCode;
//Constructors using superclass
public Department() {
super();
}
//Constructors using fields
public Department(Long departmentId, String departmentName, String departmentAddress, String departmentCode) {
super();
this.departmentId = departmentId;
this.departmentName = departmentName;
this.departmentAddress = departmentAddress;
this.departmentCode = departmentCode;
}
//Getters and Setters
public Long getDepartmentId() {
return departmentId;
}
public void setDepartmentId(Long departmentId) {
this.departmentId = departmentId;
}
public String getDepartmentName() {
return departmentName;
}
public void setDepartmentName(String departmentName) {
this.departmentName = departmentName;
}
public String getDepartmentAddress() {
return departmentAddress;
}
public void setDepartmentAddress(String departmentAddress) {
this.departmentAddress = departmentAddress;
}
public String getDepartmentCode() {
return departmentCode;
}
public void setDepartmentCode(String departmentCode) {
this.departmentCode = departmentCode;
}
}
-->ResponseTemplateVO Class
- Response format for getUserWithDepartment. getUserWithDepartment gets user and department both for that existing user for that id.
package com.zeek.userserviceA.VO;
import com.zeek.userserviceA.entity.User;
public class ResponseTemplateVO {
private User user;
private Department department;
//Constructors using superclass
public ResponseTemplateVO() {
super();
}
//Constructors using fields
public ResponseTemplateVO(User user, Department department) {
super();
this.user = user;
this.department = department;
}
//Getters and Setters
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
public Department getDepartment() {
return department;
}
public void setDepartment(Department department) {
this.department = department;
}
}
Service Class
src/main/java -->com.zeek.userserviceA.service -> UserService.java
package com.zeek.userserviceA.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import com.zeek.userserviceA.VO.Department;
import com.zeek.userserviceA.VO.ResponseTemplateVO;
import com.zeek.userserviceA.entity.User;
import com.zeek.userserviceA.repository.UserRepository;
@Service
public class UserService {
@Autowired
private UserRepository userRepository;
@Autowired
private RestTemplate restTemplate;
public User saveUser(User user) {
return userRepository.save(user);
}
public ResponseTemplateVO getUserWithDepartment(Long userId) {
ResponseTemplateVO vo = new ResponseTemplateVO();
User user = userRepository.findByUserId(userId);
Department department =
restTemplate.getForObject("http://localhost:8080/departments/" + user.getDepartmentId()
,Department.class);
vo.setUser(user);
vo.setDepartment(department);
return vo;
}
}
5-Write the controller class and Run the program
Controller Class
src/main/java --> com.zeek.userserviceA.controller -->UserController.java
package com.zeek.userserviceA.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.zeek.userserviceA.VO.ResponseTemplateVO;
import com.zeek.userserviceA.entity.User;
import com.zeek.userserviceA.service.UserService;
@RestController
@RequestMapping("/users")
public class UserController {
@Autowired
private UserService userService;
@PostMapping("/")
public User saveUser(@RequestBody User user) {
return userService.saveUser(user);
}
@GetMapping("/{id}")
public ResponseTemplateVO getUserWithDepartment(@PathVariable("id") Long userId) {
return userService.getUserWithDepartment(userId);
}
}
application.properties
src/main/resources --> application.properties
server.port=8081
spring.datasource.url=jdbc:mysql://localhost:3306/user?useSSL=false
spring.datasource.username=root
spring.datasource.password=root
# Hibernate properties
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL5InnoDBDialect
#create,create-drop
spring.jpa.hibernate.ddl-auto=update
CONSOLE
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v2.6.4)
2023-05-31 21:42:15.143 INFO 25784 --- [ main] c.z.u.UserServiceAApplication : Starting UserServiceAApplication using Java 18.0.1 on Zeeshan with PID 25784 (C:\Users\zeesh\eclipse-workspace\A_workspace\user-service-A\target\classes started by ZeeK in C:\Users\zeesh\eclipse-workspace\A_workspace\user-service-A)
2023-05-31 21:42:15.145 INFO 25784 --- [ main] c.z.u.UserServiceAApplication : No active profile set, falling back to 1 default profile: "default"
2023-05-31 21:42:15.702 INFO 25784 --- [ main] .s.d.r.c.RepositoryConfigurationDelegate : Bootstrapping Spring Data JPA repositories in DEFAULT mode.
2023-05-31 21:42:15.755 INFO 25784 --- [ main] .s.d.r.c.RepositoryConfigurationDelegate : Finished Spring Data repository scanning in 46 ms. Found 1 JPA repository interfaces.
2023-05-31 21:42:16.304 INFO 25784 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 8081 (http)
2023-05-31 21:42:16.314 INFO 25784 --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat]
2023-05-31 21:42:16.315 INFO 25784 --- [ main] org.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/9.0.58]
2023-05-31 21:42:16.391 INFO 25784 --- [ main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext
2023-05-31 21:42:16.391 INFO 25784 --- [ main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 1199 ms
2023-05-31 21:42:16.579 INFO 25784 --- [ main] o.hibernate.jpa.internal.util.LogHelper : HHH000204: Processing PersistenceUnitInfo [name: default]
2023-05-31 21:42:16.623 INFO 25784 --- [ main] org.hibernate.Version : HHH000412: Hibernate ORM core version 5.6.5.Final
2023-05-31 21:42:16.763 INFO 25784 --- [ main] o.hibernate.annotations.common.Version : HCANN000001: Hibernate Commons Annotations {5.1.2.Final}
2023-05-31 21:42:16.846 INFO 25784 --- [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Starting...
2023-05-31 21:42:16.975 INFO 25784 --- [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Start completed.
2023-05-31 21:42:16.988 INFO 25784 --- [ main] org.hibernate.dialect.Dialect : HHH000400: Using dialect: org.hibernate.dialect.MySQL5InnoDBDialect
2023-05-31 21:42:17.528 INFO 25784 --- [ main] o.h.e.t.j.p.i.JtaPlatformInitiator : HHH000490: Using JtaPlatform implementation: [org.hibernate.engine.transaction.jta.platform.internal.NoJtaPlatform]
2023-05-31 21:42:17.534 INFO 25784 --- [ main] j.LocalContainerEntityManagerFactoryBean : Initialized JPA EntityManagerFactory for persistence unit 'default'
2023-05-31 21:42:17.890 WARN 25784 --- [ main] JpaBaseConfiguration$JpaWebConfiguration : spring.jpa.open-in-view is enabled by default. Therefore, database queries may be performed during view rendering. Explicitly configure spring.jpa.open-in-view to disable this warning
2023-05-31 21:42:18.170 INFO 25784 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8081 (http) with context path ''
2023-05-31 21:42:18.178 INFO 25784 --- [ main] c.z.u.UserServiceAApplication : Started UserServiceAApplication in 3.429 seconds (JVM running for 3.86)
2023-05-31 21:43:52.334 INFO 25784 --- [nio-8081-exec-1] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring DispatcherServlet 'dispatcherServlet'
2023-05-31 21:43:52.335 INFO 25784 --- [nio-8081-exec-1] o.s.web.servlet.DispatcherServlet : Initializing Servlet 'dispatcherServlet'
2023-05-31 21:43:52.336 INFO 25784 --- [nio-8081-exec-1] o.s.web.servlet.DispatcherServlet : Completed initialization in 1 ms
Comments
Post a Comment