Build a complete Spring Boot REST API using Java @Annotations

The purpose of this article is to demonstrate the important Java @annotations used to build the functional Spring Boot REST API. The use of Java annotations enables developers to reduce code verbosity through simple annotations.

For example, we can refer to transactions. By using the standard programmatic processing of transaction templates, this requires writing more complex configuration and template code, which can be realized through simple @ Transactional declarative annotations.

In the Java programming language, annotations are syntax metadata that can be added to Java source code. Java annotations can also be embedded in and read from Java class files generated by the java compiler. This allows the Java virtual machine to retain comments at run time and read them through reflection. Support for annotations starts with version 5, allowing different Java frameworks to adopt these resources.

Annotations can also be used in REST API s. REST stands for representative state transfer, which is an architectural style used to design distributed applications. Brought by Dr. Roy Fielding. In his paper, he proposed six principles that should be separated between client and server; The communication between client and server should be stateless; There can be multiple hierarchies between them; The server response must be declared cacheable or uncacheable; The unity of its interface must be based on all interactions between the client, server and intermediate components. Finally, the client can expand its functions by using code on demand.

1

case analysis

API is a simple module used to realize the CRUD operation of business entities from more complex systems. It aims to coordinate and coordinate the economic information related to enterprises, institutions and entity groups. For simplicity, the API uses an H2 in memory database.

Project structure

The project structure consists of three modules, but this paper will focus on the modules of the management entity. The module relies on the Common module, which shares error handling and necessary useful classes with the rest of the whole system. The sample code is accessible from the GitHub repository.

https://github.com/jailsonevora/spring-boot-api-communication-through-kafka

Let's start.

2

Spring Boot auto configuration

The great advantage of Spring Boot is that we can focus on business rules, so as to avoid some cumbersome development steps, template code and more complex configuration, so as to improve the development and simplify the guidance of new Spring applications.

In order to start configuring the new Spring Boot application, Spring Initializr creates a simple POJO class to configure the initialization of the application. We have two ways to decorate the configuration. One is @ SpringBootApplication, which uses annotations when there are few modules in our solution.

If we have a more complex solution, we need to assign different paths or basic packages of our modules to the Spring Boot application initializer class@ EnableAutoConfiguration can be implemented using the and @ ComponentScan annotations@ EnableAutoConfiguration instructs Spring Boot to start adding beans according to classpath settings, other beans and various property settings. At the same time, @ ComponentScan allows spring to find other components, configurations and services in the package and let it find controllers in other components.

These two annotations cannot use @ SpringBootApplication at the same time@ Springboot application is a handy comment to add all this. It is equivalent to using @ Configuration, @EnableAutoConfiguration, and @ ComponentScan as their default properties.

package com.BusinessEntityManagementSystem;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.domain.EntityScan;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;

@EnableAutoConfiguration
@EnableJpaAuditing
@EnableJpaRepositories(basePackages = {"com.BusinessEntityManagementSystem"})
@EntityScan(basePackages = {"com.BusinessEntityManagementSystem"})
@ComponentScan(basePackages = {"com.BusinessEntityManagementSystem"})
@Configuration
public class BusinessEntityManagementApplication {

    public static void main(String[] args) {

        SpringApplication.run(BusinessEntityManagementApplication.class, args);
    }
}

Another comment in the code snippet is @ EnableJpaAuditing Auditing allows the system to track and record events related to persistent entities or entity versions. It is also related to JPA configuration. We have @ EnableJpaRepositories This annotation enables the JPA repository. By default, it scans the packages of annotated configuration classes for the Spring Data repository. In this annotation, we specify the base package of the annotation component to be scanned.

To find JPA entities in the project structure, we must instruct automatic configuration to use @ EntityScan to scan packages. For a specific scan, we can specify basepackageclass(), basepackages() or its alias value() to define the specific package to scan. If no specific package is defined, it will be scanned from the package of the class with this annotation.

The last annotation in the class created by Spring Boot Initializr is @ configuration@ Configuration marks the class as the bean definition source for the application context. This can be applied to any configuration class we need.

3

Java @Annotations in Swagger UI configuration

Documentation is an important aspect of any project, so our REST API uses swagger UI for documentation, which is one of many standard metadata. Swagger is a specification and framework for creating interactive REST API documents. It enables documents to keep pace with any changes made to REST services. It also provides a set of tools and SDK generators for generating API client code.

In swagger UI class Configuration, it appears in @ Configuration As mentioned above, this indicates to Spring Boot auto Configuration that a class is a Configuration class that may contain bean definitions.

package com.BusinessEntityManagementSystem;

import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.Contact;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;

import java.util.Collections;

@Configuration
@EnableSwagger2
@EnableAutoConfiguration
public class SwaggerConfig {
    @Bean
    public Docket api() {
        return new Docket(DocumentationType.SWAGGER_2)
                .select()
                .apis(RequestHandlerSelectors.basePackage("com.BusinessEntityManagementSystem"))
                .paths(PathSelectors.ant("/api/businessEntityManagementSystem/v1/**"))
                .build()
                .apiInfo(metaData());
    }
...

A specific comment for Swagger is @ EnableSwagger2. It indicates that Swagger support should be enabled and all required beans defined in the Swagger Java Config class should be loaded. This should apply to the Spring java Configuration and should have an accompanying @ Configuration annotation@ Bean is a method level annotation and a direct simulation of XML elements < bean / >.

4

domain model

MVC is one of the most important modules in the Spring Framework. It is a common design pattern in UI design. It separates the business logic from the UI by separating the roles of model, view and controller. The core idea of MVC pattern is to separate the business logic from the UI and allow them to change independently without affecting each other.

In this design pattern, M represents the model. The model is responsible for encapsulating application data for view rendering. It represents the shape of data and business logic. The model object retrieves the model state and stores it in the database. Its model is usually handled by the service layer and composed of domain objects persisted by the persistence layer.

TYPE Java @Annotations

In the model class, we use the @ Entity annotation to indicate that the class is a JPA Entity. JPA will know that POJO classes can be stored in the database. If we do not define the @ Table annotation, Spring config will assume that this Entity is mapped to a Table similar to the POJO class name. Therefore, in these cases, we can use the @ Table annotation to specify the Table name.

When the model property defines deferred loading, in order to deal with the problems related to model serialization using Jackson API, we must tell the serializer to ignore the chain or useful garbage added by Hibernate to the class so that it can manage deferred loading and obtain data by declaring @ JsonIgnoreProperties({"hibernateLazyInitializer", "handler"}) comments.

package com.BusinessEntityManagementSystem.models;

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.BusinessEntityManagementSystem.interfaces.models.IBusinessEntityModel;
import org.hibernate.annotations.OnDelete;
import org.hibernate.annotations.OnDeleteAction;

import javax.persistence.*;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import java.io.Serializable;
import java.util.HashSet;
import java.util.Set;

@Entity
@Table(name="BEMS_BusinessEntity")
@JsonIgnoreProperties({"hibernateLazyInitializer", "handler"})
public class BusinessEntityModel<IAddressModel extends AddressModel, IPartnerModel extends PartnerModel, IStoreModel extends StoreModel, IAffiliatedCompanyModel extends AffiliatedCompanyModel, IEconomicActivityCodeModel extends EconomicActivityCodeModel> extends AuditModel<String> implements IBusinessEntityModel, Serializable {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @NotNull
    @Column(name = "ID_BusinessEntity", updatable = false, nullable = false)
    private long id;

...

FIELD Java @Annotations

For a class field, there are multiple annotations, depending on the type and purpose of the field. For example, the @ Id annotation must be declared in one of the class properties. Each entity object stored in the database has a primary key. Once allocated, the primary key cannot be modified@ GeneratedValue indicates that the framework should generate document key values using the specified generator types, such as {AUTO, IDENTITY, SEQUENCE, and TABLE}.

Another interesting comment for domain model fields is @ NotNull Declared annotated elements cannot be common Spring annotations null. It can also be used in methods or parameters. Specify the name of the database Column and the behavior of the @ Column table. You can set this behavior to prevent it from being updated or empty.

Sometimes most objects have a natural identifier, so Hibernate also allows this identifier to be modeled as the natural identifier of the entity and provides additional API s for retrieving them from the database. This is achieved using the @ NaturalId annotation.

...

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @NotNull
    @Column(name = "ID_BusinessEntity", updatable = false, nullable = false)
    private long id;

    @NaturalId
    @NotEmpty
    @NotNull
    @Column(name = "NATURAL_ID", updatable = false, nullable = false)
    private long naturalId;

    @Column(name = "TaxId")
    @Size(min = 1, max = 50)
    private String taxId;

    @Column(name = "Status")
    private int status = 1;

...

If we want to prevent the element of an entity from being empty, we can also annotate it @ NotEmpty. It targets a large number of elements because {method, field, annotation_type, constructor, parameter, type_use}@ The size annotation delimits the boundary of the annotated element. The boundary is specified by two attributes min and max.

Relational Java @Annotations

One of the most important features of any ORM mechanism is how to specify the mapping from the relationship between objects to their database counterparts. In the following code, there is a @OneToOne annotation to describe the relationship between the BusinessEntity class and the Address class model@ The JoinColumn annotation specifies the columns that will be treated as foreign keys in this relationship.

In addition to @ OneToOne annotations, we can also manage many to many relationships@ The ManyToMany annotation describes the relationship with the Partner class member. Like other relationship annotations, you can also specify cascading rules and acquisition types. According to the selected cascade setting, when BusinessEntity deletes a, the associated Partner will also be deleted.

...
    // region OneToOne

    @OneToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL, targetEntity= AddressModel.class)
    @JoinColumn(name = "Address", nullable = false)
    @OnDelete(action = OnDeleteAction.CASCADE)
    private IAddressModel address;

    // endregion OneToOne


    // region ManyToMany

    @ManyToMany(fetch = FetchType.LAZY,
            cascade = {
                    CascadeType.ALL
            },
            targetEntity= PartnerModel.class
    )
    @JoinTable(name = "BSUF_BusinessEntity_Partner",
            joinColumns = { @JoinColumn(name = "ID_BusinessEntity") },
            inverseJoinColumns = { @JoinColumn(name = "ID_Partner") })
    private Set<IPartnerModel> partner = new HashSet<>();

// endregion ManyToMany
...

Together with the @ ManyToMany annotation, we specify the @ JoinTable annotation, which allows us to use the two basic properties joincolumns in a many to many relationship to define a bridge table between the other two related tables for the class we declare the @ ManyToMany annotation and the inverseJoinColumns table.

...
  // inverser many to many

  @ManyToMany(fetch = FetchType.LAZY,
              cascade = {
                CascadeType.PERSIST,
                CascadeType.MERGE
              },
              mappedBy = "partner")
  @JsonIgnore
  private Set<IBusinessEntityModel> businessEntity = new HashSet<>();
...

In another table, it is recommended to also define inverse relationships. This declaration is slightly different from what is shown in the code related to the business entity model. The reverse relationship is declared through the attribute "mappedBy." To distinguish.

5

Data transmission object

Data transmission object is a very popular design pattern. It is an object that defines how data is sent over the network. DTO is only used to transfer data and does not contain any business logic.

TYPE Java @Annotations

Sometimes we need to transfer data between entities through JSON. To serialize and deserialize DTO objects, we need to annotate these objects with Jackson annotations.

@JsonInclude(JsonInclude.Include.NON_NULL) indicates when annotated properties can be serialized. By using this annotation, we can specify simple exclusion rules based on attribute values. It can be used for field, method, or constructor parameters. It can also be used in a class. In some cases, the specified rules apply to all properties of the class.

package com.BusinessEntityManagementSystem.dataTransferObject;

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;

/*@JsonInclude(JsonInclude.Include.NON_NULL)*/
@JsonIgnoreProperties({"hibernateLazyInitializer", "handler"})
public class BusinessEntity {
...

@JsonIgnoreProperties({"hibernateLazyInitializer", "handler"}) allows Jackson to ignore the garbage created by Hibernate, so it can manage the delayed loading of the previously mentioned data.

FIELD Java @Annotations

Fields in DTO objects may also have different types of comments@ The JsonProperty annotation specifies the name of the serialized property@ JsonIgnore annotates at the class attribute level to ignore it. In addition to @ JsonIgnore, @ JsonIgnoreProperties and @ JsonIgnoreType. Both annotations are part of the Jackson API and are used to ignore logical attributes in JSON serialization and deserialization.

package com.BusinessEntityManagementSystem.dataTransferObject;

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;

/*@JsonInclude(JsonInclude.Include.NON_NULL)*/
@JsonIgnoreProperties({"hibernateLazyInitializer", "handler"})
public class BusinessEntity {

    @JsonProperty("naturalId")
    private long naturalId;

    @JsonProperty("taxId")
    private String taxId;

    @JsonProperty("status")
    private int status = 1;
...

Jackson API is a high-performance JSON processor for Java. It provides many useful annotations to apply to DTO objects, allowing us to serialize and deserialize objects from JSON to JSON.

6

controller

The controller represents C in MVC mode. The controller is responsible for receiving the user's request and calling the back-end service for business processing. After processing, it may return some data for the view to render. The controller collects it and prepares the model for view rendering. The controller is often called a scheduler servlet. As the front controller of the Spring MVC framework, it must be passed by every Web request so that it can manage the entire request processing process. When a Web request is sent to the Spring MVC application, the controller first receives the request. It then organizes the annotations that exist in the different components or controllers configured in the context of Spring's Web application, all of which need to process the request.

TYPE Java @Annotations

To define a Controller class in Spring Boot, you must mark the class with the @ RestController annotation@ The RestController annotation is a convenient annotation. It is annotated with @ Controller and @ ResponseBody. The type with this annotation is regarded as a Controller, and the @ RequestMapping method adopts the @ ResponseBody semantics by default. The value attribute can indicate a recommendation for the name of the logical component to convert it to a Spring bean if it is automatically detected.

package com.BusinessEntityManagementSystem.v1.controllers;

import com.BusinessEntityManagementSystem.dataAccessObject.IBusinessEntityRepository;
import com.BusinessEntityManagementSystem.interfaces.resource.IGenericCRUD;
import com.BusinessEntityManagementSystem.models.BusinessEntityModel;
import com.Common.Util.Status;
import com.Common.exception.ResourceNotFoundException;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.support.ServletUriComponentsBuilder;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.transaction.Transactional;
import javax.validation.Valid;
import java.util.Optional;

@RestController("BusinessEntityV1")
@RequestMapping("/api/businessEntityManagementSystem/v1")
@Api(value = "businessEntity")
public class BusinessEntityController implements IGenericCRUD<BusinessEntityModel> {
...

Constructor and method Java @Annotations

When a class with the @ RestController annotation receives a request, it looks for the appropriate handler method to process the request. This requires the controller to map each request to a handler method through one or more handler mappings. To this end, the methods of the controller class are decorated with the @ RequestMapping annotation to make them processing methods.

For Swagger documentation purposes, the @ ApiOperation annotation is used to declare a single operation in an API resource. Operations are considered the only combination of paths and HTTP methods. Only the annotated method @ ApiOperation will be scanned and added to the API declaration. Some handlers or operations require transactions to ensure data integrity and consistency.

Transaction management is a basic technology to ensure data integrity and consistency in enterprise applications. Spring supports programmatic and declarative (@ Transactional) transaction management.

...
 @Autowired
    public BusinessEntityController(IBusinessEntityRepository businessEntityRepository) {
        this.businessEntityRepository = businessEntityRepository;
    }

    @RequestMapping(value = "/businessEntity/{id}", method = RequestMethod.GET, produces = "application/json")
    @ApiOperation(value = "Retrieves given entity", response=BusinessEntityModel.class)
    public ResponseEntity<?> get(@Valid @PathVariable Long id){
        checkIfExist(id);
        return new ResponseEntity<> (businessEntityRepository.findByIdAndStatus(id, Status.PUBLISHED.ordinal()), HttpStatus.OK);
    }

@Transactional
    @RequestMapping(value = "/businessEntity", method = RequestMethod.POST, produces = "application/json")
    @ApiOperation(value = "Creates a new entity", notes="The newly created entity Id will be sent in the location response header")
    public ResponseEntity<Void> create(@Valid @RequestBody BusinessEntityModel newEntity){

        return new ResponseEntity <>(null, getHttpHeaders(businessEntityRepository.save(newEntity)), HttpStatus.CREATED);

    }
...

To manage transactions programmatically, we must include transaction management code (template code) in each transaction operation. As a result, the boilerplate transaction code repeats in each of these operations. In most cases, declarative transaction management is preferable to procedural transactions. It is achieved by declaring the separation of transaction management code from our business methods. This can help us more easily enable transactions for our applications and define consistent transaction policies, although declarative transaction management is not as flexible as procedural transaction management. Procedural transaction management allows us to control transactions through code.

Another useful annotation used in a well-designed system is @ Autowired@ Autowired can be used in construction methods to parse collaboration beans and inject them into beans, leading us to better design applications. The application developed with the principle of separating interface from implementation and dependency injection pattern is easy to test, whether it is unit test or integration test, because the principle and pattern can reduce the coupling between different units of our application.

Parameter Java @Annotations

In addition to authentication and authorization, an important area of building secure Web services is to ensure that input is always verified. Java Bean annotations provide a mechanism for input validation. We can do this by @ Valid using annotations in method parameters.

Our class should validate the incoming identifier request before handling soft deletion. By simply adding the @ Valid annotation to the method, Spring will ensure that the incoming identifier request first runs through the validation rules we define.

...
  @Transactional
    @RequestMapping(value = "/businessEntity/{id}", method = RequestMethod.DELETE, produces = "application/json")
    @ApiOperation(value = "Deletes given entity")
    public ResponseEntity<Void> delete(@Valid @PathVariable Long id, @Valid @RequestBody String lastModifiedBy){

        Optional<BusinessEntityModel> softDelete = businessEntityRepository.findByIdAndStatus(id, Status.PUBLISHED.ordinal());
        if (softDelete.isPresent()) {
            BusinessEntityModel afterIsPresent = softDelete.get();
            afterIsPresent.setStatus(Status.UNPUBLISHED.ordinal());
            afterIsPresent.setLastModifiedBy(lastModifiedBy);
            businessEntityRepository.save(afterIsPresent);
            return new ResponseEntity<>(HttpStatus.OK);
        }
        else
            throw new ResourceNotFoundException("Entity with id " + id + " not found");
    }
...

@PathVariable and @ RequestParam are used to extract values from HTTP requests. There are subtle differences between them@ RequestParam is used to retrieve from the URL( https://www.jeevora.com/...?id=1 )Get request parameters, also known as query parameters, and @ PathVariable extracts values from URI (), https://www.jeevora.com/id/1 As our case study shows.

@RequestBodyannotation indicates that the method parameters should be bound to the body of the web request, while @ ResponseBody indicates that the method return value should be bound to the body of the Web response.

7

Data access object

A typical design error is to mix different types of logic (such as presentation logic, business logic and data access logic) into a large module. Because it introduces tight coupling, it reduces the reusability and maintainability of the module. The general purpose of data access object (DAO) pattern is to avoid these problems by separating data access logic from business logic and presentation logic. This pattern suggests that the data access logic be encapsulated in a separate module called the data access object [3].

A repository or data access object (DAO) provides an abstraction for interacting with a data store. Repositories traditionally include an interface that provides a set of finder methods, such as findById, findAll, for retrieving data, and methods for persisting and deleting data. The repository also includes a class that implements this interface using data store specific technologies. Traditionally, each domain object has a repository. Although this is a popular approach, there is a lot of boilerplate code duplication in each repository implementation.

package com.BusinessEntityManagementSystem.dataAccessObject;

import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.repository.NoRepositoryBean;
import org.springframework.data.repository.PagingAndSortingRepository;

import java.util.Optional;

@NoRepositoryBean
public interface IGenericRepository<T> extends PagingAndSortingRepository<T, Long> {

    Optional<T> findByIdAndStatus(long id, int status);

    Page<T> findAllByStatus(int status, Pageable pageable);
}

Using the @ NoRepositoryBean annotation, we can use it to exclude the repository interface from being picked up, so as to obtain an instance being created. This is usually used to provide an extended base interface for all repositories and implement the methods declared in the intermediate interface in combination with a custom repository base class. In this case, we usually derive the specific repository interface from the intermediate interface, but we don't want to create spring beans for the intermediate interface.

reference resources

[1] Balaji Varanasi, Sudha Belida, Spring REST - Rest and Web Services development using Spring, 2015;

[2] Ludovic Dewailly, building RESTful Web services with Spring - a hands-on guide to building enterprise class and extensible RESTful Web services with Spring framework, 2015;

[3] Marten Deinum, Daniel Rubio, Josh Long, Spring 5 Recipes: A Problem-Solution Approach, 2017.

Posted by amazinggrace1983 on Fri, 15 Apr 2022 14:13:35 +0930