Table structure

The user service will use two tables. One to record the personal data of a user, such as their name, unique ID number, date of birth, gender, and so on and the second table to store the address of the user. To create a schema and table, we will use the following SQL file and flywaydb. Flyway is an open source database migration tool. It has Maven plugins that work well with the Spring Boot. To use the Flyway in Spring Boot, you will have to add the following dependency in the POM file:

<dependency> 
    <groupId>org.flywaydb</groupId> 
    <artifactId>flyway-core</artifactId> 
    <version>4.0.3</version> 
</dependency> 

Flyway DB works on convention, so for any migration to happen, it will look for the file at the src/main/resources/db/migration path with a filename starting with V1_0__DESCRIPTION.sql. Flyway DB stores the schema revision in the database as well. It can read from the schema version which files were executed successfully and which are new ones to run. Lets create the user_details and address table now. The following is the SQL code to create the user_details table:

CREATE TABLE `user_details` ( 
  `id` int(11) NOT NULL AUTO_INCREMENT, 
  `user_id` char(36) NOT NULL, 
  `first_name` varchar(250) NOT NULL, 
  `middle_name` varchar(250) DEFAULT NULL, 
  `last_name` varchar(250) DEFAULT NULL, 
  `created_on` TIMESTAMP DEFAULT CURRENT_TIMESTAMP, 
  `deleted_on` TIMESTAMP DEFAULT NULL, 
  `leagal_id` varchar(10) NOT NULL, 
  `date_of_birth` TIMESTAMP NOT NULL, 
  `gender` int(11) NOT NULL, 
  PRIMARY KEY (`id`), 
  KEY `user_id` (`user_id`), 
  KEY `user_details_userId_DeletedOn` (`user_id`,`deleted_on`), 
  KEY `user_id_deleted` (`deleted_on`) 
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

The SQL code for creating the addresses table, table.user_id is the ID from the User_details table:

CREATE TABLE `addresses` ( 
  `id` int(11) NOT NULL AUTO_INCREMENT, 
  `user_id` char(36) NOT NULL, 
  `city` varchar(25) NOT NULL, 
  `address_line_1` varchar(250) NOT NULL, 
  `address_line_2` varchar(250) DEFAULT NULL, 
  `pincode` char(6) NOT NULL, 
  `created_on` TIMESTAMP DEFAULT CURRENT_TIMESTAMP, 
  `deleted_on` TIMESTAMP DEFAULT NULL, 
  PRIMARY KEY (`id`), 
  KEY `user_id` (`user_id`), 
  KEY `addresses_user_id_DeletedOn` (`user_id`,`deleted_on`) 
) ENGINE=InnoDB  DEFAULT CHARSET=latin1; 

As we did in the configuration service, a new boot application code with application name userService will be downloaded from start.sping.io. This time, we will also select the Spring data and MySQL connector to create a new project. For this we are using STS version 3.8.3.

After downloading and unzipping the code in a particular folder, you can run the mvn eclipse:eclipse command to generate Eclipse related files. After this, it will be easy to import a project into Eclipse, or STS as Maven project. After importing the project to your favorite IDE, you can see the file there.

Our final project structure will be similar to the following screenshot:

Lets work towards making our project in the shape of the preceding image. Lets start with POM file. Your POM will be as follows:

<?xml version="1.0" encoding="UTF-8"?> 
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 
    <modelVersion>4.0.0</modelVersion> 
 
    <groupId>com.practicalMicroservcies</groupId> 
    <artifactId>userService</artifactId> 
    <version>0.0.1-SNAPSHOT</version> 
    <packaging>jar</packaging> 
 
    <name>userService</name> 
    <description>Demo project for Spring Boot</description> 
 
    <parent> 
        <groupId>org.springframework.boot</groupId> 
        <artifactId>spring-boot-starter-parent</artifactId> 
        <version>1.4.1.RELEASE</version> 
        <relativePath /> <!-- lookup parent from repository --> 
    </parent> 
 
    <properties> 
        <project.build.sourceEncoding>UTF-
8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-
8</project.reporting.outputEncoding> <java.version>1.8</java.version> </properties> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>Camden.RELEASE</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-config-client</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.flywaydb</groupId> <artifactId>flyway-core</artifactId> <version>4.0.3</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>

Some of the new things that you will see here are Flyway dependency, spring-data-jpa, and MySQL connector in POM. Add the following code into your main application class:

package com.practicalMicroservcies; 
 
import org.flywaydb.core.Flyway; 
import org.springframework.beans.factory.annotation.Value; 
import org.springframework.boot.SpringApplication; 
import org.springframework.boot.autoconfigure.SpringBootApplication; 
import org.springframework.context.annotation.Bean; 
 
@SpringBootApplication 
public class UserServiceApplication { 
    @Value("${spring.datasource.url}") 
    private String url; 
    @Value("${spring.datasource.username}") 
    private String userName; 
    @Value("${spring.datasource.password}") 
    private String password; 
    @Value("${Connect.database}") 
    private String database; 
 
    @Bean(initMethod = "migrate") 
    public Flyway flyway() { 
        /** 
        As flyway Db need url seperetaly that is why we extracting only  
url from given string */ String urlWithoutDatabaseName=
url.substring(0,url.lastIndexOf("/")); Flyway flyway = new Flyway(); flyway.setDataSource(urlWithoutDatabaseName, userName,
password); flyway.setSchemas(database); flyway.setBaselineOnMigrate(true); return flyway; } public static void main(String[] args) { SpringApplication.run(UserServiceApplication.class, args); } }

Here, we are using properties that are defined in the configuration server. We still have to define how this service will get those properties from the server. In order to do that, please add a bootstrap.properties file in the resources folder and add the following line to that:

spring.application.name=userService 
spring.cloud.config.uri=http://localhost:8888 

Both the properties are self-explanatory. The spring.application.name key will define the name of the current service. This name will communicate with the configuration server, and the configuration server will look for the property file with this name and serve it back. Bootstrap properties will be executed before anything is initialized in the project, so this placeholder for properties will always be filled soon.

Now, the property name that starts with spring.datasource is important. These three properties are used by Spring Boot to automatically create a data source without writing any code. The connect.database is not an internal property of Spring Boot. It can be any name to define the database name.

In flywayDb bean, we have set the schema name with this version of Flyway DB; it will create the schema if it is not already there.

So, if you run a command from the terminal now, mvn spring-boot:run, you should be able to see your database and table created in the MySQL.

Let's create the model classes for these two tables under the entity package. One will be Address and one will be UserDetail. Insert the following code in the Address.java file:

package com.practicalMicroservcies.entity; 
 
import static javax.persistence.GenerationType.IDENTITY; 
 
import java.io.Serializable; 
import java.util.Date; 
 
import javax.persistence.Column; 
import javax.persistence.Entity; 
import javax.persistence.GeneratedValue; 
import javax.persistence.Id; 
import javax.persistence.Table; 
import javax.persistence.Temporal; 
import javax.persistence.TemporalType; 
 
@Entity 
@Table(name = "addresses") 
public class Address implements Serializable { 
 
    /** 
     * Serial Id for serialization 
     */ 
    private static final long serialVersionUID = 1245490181445280741L; 
    /** 
     * unique Id for each record. 
     */ 
 
    @Id 
    @GeneratedValue(strategy = IDENTITY) 
    @Column(name = "id") 
    private int id; 
 
    /** 
     * User Id , unique for every user 
     */ 
 
    @Column(name = "user_id", nullable = false, unique = true, length = 
36) private String userId; /** * City, Staying city of user */ @Column(name = "city", nullable = false, length = 25) private String city; /** * Address Line 1 for User Address. */ @Column(name = "address_line_1", nullable = false, length = 250) private String addressLine1; /** * * Address Line 2 for User Address. */ @Column(name = "address_line_2", nullable = false, length = 250) private String addressLine2; /** * Pincode for user City */ @Column(name = "pincode", nullable = false, length = 36) private String pinCode; /** * Date on which user is created */ @Temporal(TemporalType.TIMESTAMP) @Column(name = "created_on", columnDefinition = "TIMESTAMP DEFAULT
CURRENT_TIMESTAMP", length = 0) private Date createdOn; /** * Date on which user is deleted. */ @Temporal(TemporalType.TIMESTAMP) @Column(name = "deleted_on", columnDefinition = "TIMESTAMP DEFAULT
NULL", length = 0) private Date deletedOn; }

The setter and getter methods of the class variable should be created here. User can optionally over ride the toString method of class also, as follows:

    @Override 
    public String toString() { 
        return "Address [id=" + id + ", userId=" + userId + ", city=" + 
city + ", addressLine1=" + addressLine1 + ", addressLine2=" +
addressLine2 + ", PinCode=" + pinCode + ", createdOn=" +
createdOn + ", deletedOn=" + deletedOn + "]"; }

There will be one repository class for it, which is as follows:

package com.practicalMicroservcies.repo; 
 
import org.springframework.data.jpa.repository.JpaRepository; 
 
import com.practicalMicroservcies.entity.Address; 
 
public interface AddressRepository extends JpaRepository<Address, Integer> { 
    Address findByUserId(String userId); 
} 

There will be an Entity class for UserDetail as well, which is as follows:

package com.practicalMicroservcies.entity; 
 
import static javax.persistence.GenerationType.IDENTITY; 
 
import java.io.Serializable; 
import java.util.Date; 
 
import javax.persistence.Column; 
import javax.persistence.Entity; 
import javax.persistence.GeneratedValue; 
import javax.persistence.Id; 
import javax.persistence.Table; 
import javax.persistence.Temporal; 
import javax.persistence.TemporalType; 
 
@Entity 
@Table(name = "user_details") 
public class UserDetail implements Serializable { 
 
    /** 
     *  
     */ 
    private static final long serialVersionUID = 4804278804874617196L; 
 
    /** 
     * unique Id for each record. 
     */ 
 
    @Id 
    @GeneratedValue(strategy = IDENTITY) 
    @Column(name = "id") 
    private int id; 
 
    /** 
     * User Id , unique for every user 
     */ 
 
    @Column(name = "user_id", nullable = false, unique = true, length = 
36) private String userId; /** * First Name of User */ @Column(name = "first_name", nullable = false, unique = true,
length = 250) private String firstName; /** * Last Name of User */ @Column(name = "last_name", nullable = false, unique = true, length
= 250) private String lastName; /** * Middle Name of user */ @Column(name = "middle_name", nullable = false, unique = true,
length = 250) private String middleName; /** * Legal id Of user( could be social security number etc) */ @Column(name = "legal_id", nullable = false, unique = true, length
= 20) private String legalId; /** * Gender of user */ @Column(name = "gender", nullable = false, unique = true, length =
20) private String gender; /** * Date of birth of user */ @Temporal(TemporalType.TIMESTAMP) @Column(name = "date_of_birth", columnDefinition = "TIMESTAMP",
length = 0) private Date dateOfBirth; /** * Date on which user is created */ @Temporal(TemporalType.TIMESTAMP) @Column(name = "created_on", columnDefinition = "TIMESTAMP DEFAULT
CURRENT_TIMESTAMP", length = 0) private Date createdOn; /** * Date on which user is deleted. */ @Temporal(TemporalType.TIMESTAMP) @Column(name = "deleted_on", columnDefinition = "TIMESTAMP DEFAULT
NULL", length = 0) private Date deletedOn; }

We will be having the setter and getter methods of all the instance variables of UserDetail. We can have the toString method override also in the following class:

    @Override 
    public String toString() { 
        return "UserDetail [id=" + id + ", userId=" + userId + ", 
firstName=" + firstName + ", lastName=" + lastName + ",
middleName=" + middleName + ", legalId=" + legalId + ",
gender=" + gender + ", createdOn=" + createdOn + ", deletedOn="
+ deletedOn + "]"; } }

The repository class for this entity is also required:

package com.practicalMicroservcies.repo; 
 
import org.springframework.data.jpa.repository.JpaRepository; 
 
import com.practicalMicroservcies.entity.UserDetail; 
 
public interface UserDetailRepository extends JpaRepository<UserDetail, Integer> { 
     
    UserDetail findByUserId(String userId); 
} 

Now, we will need a service class to deal with these repositories:

package com.practicalMicroservcies.service; 
 
import java.util.Date; 
import java.util.UUID; 
 
import javax.annotation.Resource; 
import javax.transaction.Transactional; 
 
import org.springframework.stereotype.Service; 
 
import com.practicalMicroservcies.entity.Address; 
import com.practicalMicroservcies.entity.UserDetail; 
import com.practicalMicroservcies.repo.AddressRepository; 
import com.practicalMicroservcies.repo.UserDetailRepository; 
 
@Service 
@Transactional 
public class UserDetailServices { 
 
    @Resource 
    AddressRepository addressRepo; 
 
    @Resource 
    UserDetailRepository userRepo; 
 
    public void saveAddress(Address address) { 
        addressRepo.save(address); 
        System.out.println("User Saved!"); 
 
    } 
 
    public void saveUser(UserDetail userDetail) { 
        userRepo.save(userDetail); 
        System.out.println("User Saved!"); 
 
    } 
 
    public Address getAddress(UUID userId) { 
        Address returnAddressObject = 
addressRepo.findByUserId(userId.toString()); return returnAddressObject; } public UserDetail getUser(UUID userId) { UserDetail userObjectToRetrun =
userRepo.findByUserId(userId.toString()); System.out.println("User Saved!"); return userObjectToRetrun; } public void deleteUser(UUID userId) { Address addressObject =
addressRepo.findByUserId(userId.toString()); addressObject.setDeletedOn(new Date()); addressRepo.saveAndFlush(addressObject); UserDetail userObject =
userRepo.findByUserId(userId.toString()); userObject.setDeletedOn(new Date()); userRepo.saveAndFlush(userObject); System.out.println("User Deleted!"); } }

Finally, we need a controller to handle the request from frontend. The user ID in this service will be a UUID and it is supposed to be generated at the frontend. By doing so, the frontend or consumer can hit the microservice in parallel using the same user ID, as shown in the following piece of code:

@RestController 
@RequestMapping("/PM/user/") 
public class UserController { 
 
 
    @Resource 
    UserDetailServices userService; 
 
    @Resource 
    ObjectMapper mapper; 
    
 

We need to have two services here, one is UserDetailServices which we need to do user related operations and object mapper for JSON to object and vice versa conversion.

We need a method which should be responsible for adding a new address in the database corresponding to any user:

     @RequestMapping(method = RequestMethod.POST, value = "{userId}/address", produces = "application/json", consumes = "application/json") 
    public ResponseEntity<String> createUserAddress(@RequestBody 
Address address, @PathVariable("userId") UUID userId) { logger.debug(createUserAddress + " Address for user Id " +
userId + " is updated as " + address); address.setUserId(userId.toString()); userService.saveAddress(address); return new ResponseEntity<>(HttpStatus.CREATED); } / Below Method is responsible for creating a user. public static final String createUser = "createUser(): "; @RequestMapping(method = RequestMethod.POST, value = "{userId}",
produces = "application/json", consumes = "application/json") public ResponseEntity<String> createUser(@RequestBody UserDetail
userDetail, @PathVariable("userId") UUID userId) { logger.debug(createUser + " creating user with Id " + userId +
" and details : " + userDetail); userDetail.setUserId(userId.toString()); userService.saveUser(userDetail); return new ResponseEntity<>(HttpStatus.CREATED); }

The following is the delete request for deleting the user from the database based on ID:

@RequestMapping(method = RequestMethod.DELETE, value = "{userId}", produces = "application/json", consumes = "application/json") 
    public ResponseEntity<String> deleteUser(@RequestBody UserDetail 
userDetail, @PathVariable("userId") UUID userId) { logger.debug(deleteUser + " deleting user with Id " + userId); userService.deleteUser(userId); return new ResponseEntity<>(HttpStatus.CREATED); }

The following method is responsible for getting the user detail from ID which is a GET request:

    @RequestMapping(method = RequestMethod.GET, value = "{userId}",  
produces = "application/json", consumes = "application/json") public ResponseEntity<UserDetail> getUser(@PathVariable("userId")
UUID userId) { logger.debug(getUser + " getting information for userId " +
userId); UserDetail objectToReturn = userService.getUser(userId); if (objectToReturn == null) return new ResponseEntity<>(HttpStatus.NOT_FOUND); else return new ResponseEntity<>(objectToReturn, HttpStatus.OK); }

The next method is responsible for getting the address from the database respective of any particular user ID:

    @RequestMapping(method = RequestMethod.GET, value = "
{userId}/address", produces = "application/json", consumes =
"application/json") public ResponseEntity<Address> getAddress(@PathVariable("userId")
UUID userId) { logger.debug(getAddress + " getting address for user Id: " +
userId); Address objectToReturn = userService.getAddress(userId); if (objectToReturn == null) return new ResponseEntity<>(HttpStatus.NOT_FOUND); else return new ResponseEntity<>(objectToReturn, HttpStatus.OK); }

The URLs exposed from this controller are as follows:

Post http://localhost:8080/PM/user/<user_Id> 
Post http://localhost:8080/PM/user/<user_Id>/address 
Get Post http://localhost:8080/PM/user/<user_Id> 
Get Post http://localhost:8080/PM/user/<user_Id>/address 
Delete Post http://localhost:8080/PM/user/<user_Id> 

At this point, we are good to test a simple user service. Start the configuration server and user detail service in different terminals with the mvn spring-boot:run command. You will see that your database is created, and you can test to verify that the URLs are working fine with the help of any rest client, such as postman or curl. For address entity, try the POST request with a JSON value, as follows:

{ 
  "userId": "93a52ce4-9331-43b5-8b56-09bd62cb0444", 
  "city": "New York", 
  "addressLine1": "4719 Fermun Road", 
  "addressLine2": "Yorktown Heights", 
  "pinCode": "10004" 
} 

To create a user, use the following JSON:

{ 
"userId": "93a52ce4-9331-43b5-8b56-09bd62cb0444", 
  "firstName": "John", 
  "lastName": "Montgomery", 
  "middleName": "Allen", 
  "legalId": "B053-62-64087", 
  "gender": "1", 
  "dateOfBirth": "2010-10-27T11:58:22.973Z", 
} 

We start with a basic service will put security and testing bits in the respective chapter.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset