Skip to content

Instantly share code, notes, and snippets.

@1UC1F3R616
Last active June 14, 2024 18:37
Show Gist options
  • Save 1UC1F3R616/92b3893684da15569c52bd96ed6a99a5 to your computer and use it in GitHub Desktop.
Save 1UC1F3R616/92b3893684da15569c52bd96ed6a99a5 to your computer and use it in GitHub Desktop.
A Guide to Spring Boot: Crystal Clear Level K (K>L>M) | For native Java concepts refer java.md

Understanding Origins

  • First came servlet then came Spring then came Spring Boot.
  • Java Servlets are Java programs that run on a web server. They handle http requests but are very basic in nature.
  • Spring uses Servlet under the hood but much more mature, a framework. Still it needs a server setup like tomcat. It had DI, AOP, Web MVC etc. Spring is more then just web, u can make a CLI with it.
  • Spring Boot has it's embedded server as well and much more. It's "convention over configuration" approach and eliminate a lot of boilerplate code and configuration. It has autoconfiguration (It automatically configures many common components based on your project dependencies) and starter dependencies (pre-packaged dependencies that quickly add functionalities like web, data, security, etc) and Actuator.
  • Servlets provided the foundation for Java web development.
  • Spring Framework added powerful features and tools for building enterprise applications.
  • Spring Boot simplified Spring development, making it more accessible and efficient.

Spring MVC

  • Spring is the overarching framework with a wide range of modules for different application needs.
  • Spring MVC is a specific module within Spring designed for web development.
  • Spring Boot is built on top of the entire Spring Framework, including Spring MVC.

HTTP Request handling in Spring Boot

  1. HTTP Request Arrival: A request hits the DispatcherServlet.

  2. Handler Mapping: The DispatcherServlet consults a HandlerMapping to determine which controller and method can handle the request based on the URL and HTTP method.

  3. Controller Invocation: The identified controller method is executed. It processes the request parameters, performs business logic, and prepares a response.

  4. View Resolution (Optional): If the controller returns a model and view name, Spring MVC's ViewResolver determines the appropriate template engine (e.g., Thymeleaf, JSP) to render the final HTML response.

  5. Response Sent: The DispatcherServlet sends the rendered response back to the client.

  • It's same for Spring and Spring Boot as Spring Boot has only those 4 plus simplification things differing it from Spring.

Key Annotations

Annotation Purpose
@RestController Marks a class as a RESTful controller (combines @Controller and @ResponseBody).
@Controller Marks a class as a Spring MVC controller.
@RequestMapping Maps HTTP requests to controller methods based on URL patterns and HTTP methods.
@GetMapping Shorthand for @RequestMapping with the HTTP GET method.
@PostMapping Shorthand for @RequestMapping with the HTTP POST method.
@PutMapping Shorthand for @RequestMapping with the HTTP PUT method.
@DeleteMapping Shorthand for @RequestMapping with the HTTP DELETE method.
@PatchMapping Shorthand for @RequestMapping with the HTTP PATCH method.
@RequestParam Extracts query parameters from the request URL.
@PathVariable Extracts variables from the URL path segments.
@RequestBody Extracts the request body (e.g., JSON data) and maps it to a Java object.
@ResponseBody Indicates that the return value of a method should be directly written to the response body.
@ResponseStatus Sets the HTTP status code of the response.
@CrossOrigin Enables cross-origin resource sharing (CORS) for a controller or specific methods.
@ExceptionHandler Handles exceptions thrown during request processing.
@ControllerAdvice Provides global exception handling and advice across multiple controllers.
@InitBinder Customizes data binding for a specific controller.
@ModelAttribute Binds a method parameter or return value to a model attribute.
@SessionAttributes Stores model attributes in the user's session.
@Configuration Marks a class as a source of bean definitions.
@Bean Declares a method that produces a bean to be managed by the Spring container.
@Component A general-purpose stereotype for any Spring-managed component.
@Service Indicates a class that provides business logic.
@Repository Indicates a class that interacts with a data source (e.g., database).
@Autowired Injects dependencies automatically by type.
@Qualifier Disambiguates bean injection when multiple candidates exist.
@Value Injects values from properties files or other sources.
@Transactional Marks a method or class as transactional for database operations.
@Async Marks a method to be executed asynchronously in a separate thread.
@Scheduled Schedules a method for periodic execution (e.g., cron jobs).
@Cacheable Caches the results of a method invocation.
@EnableCaching Enables Spring's cache abstraction.
@Valid Triggers validation of a request body or form data.

Fundamentals - DI, IoC

Inversion of Control (IoC)

  • At its core, IoC is a design principle where the control of creating and managing objects is handed over to a framework (like Spring) instead of being handled directly by your code.
  • Traditional Approach
public class MyService {
    private MyRepository repository = new MyRepositoryImpl(); 
    // ... use the repository ...
}
  • MyService is tightly coupled to a specific implementation of MyRepository. With IoC, you invert this control:
public class MyService {
    @Autowired
    private MyRepository repository; // Spring will provide this
    // ... use the repository ...
}
  • Now, Spring manages the creation of the MyRepository object and injects (provides) it into the MyService.

Dependency Injection

  • DI is a specific implementation of IoC. It's the process by which the framework (Spring) supplies the dependencies (objects) that a class needs, instead of the class having to create them itself.
  • Spring supports several types of DI:
    • Constructor-based DI: Dependencies are injected through the constructor.
    • Setter-based DI: Dependencies are injected through setter methods.
    • Field-based DI: Dependencies are injected directly into fields using @Autowired (less recommended).
@Service
public class MyServiceImpl implements MyService {

    private final MyRepository repository;

    @Autowired
    public MyServiceImpl(MyRepository repository) { // Constructor-based DI
        this.repository = repository;
    }

    // ... business logic ...
}
  • Here, Spring injects an implementation of MyRepository into MyServiceImpl through its constructor.
  • Benefits of IoC and DI
    • Loose Coupling: Classes depend on interfaces, not concrete implementations, making them easier to replace or change.
    • Improved Testability
    • Simplified Configuration
    • Centralized Management: Spring manages the lifecycle and configuration of beans.

The Magic Behind Autowiring

  1. Component Scanning:
  • When your Spring Boot application starts, it performs a process called "component scanning." It scans your project's classpath for classes annotated with stereotypes like @Component, @Service, @Repository, @Controller, etc.
  • It identifies these classes as potential beans and registers them in the Spring container.
  1. Bean Definition:
  • For each discovered class, Spring creates a "bean definition." This definition includes information about the class (e.g., its name, scope, dependencies).
  1. Dependency Resolution:
  • When Spring encounters a field, constructor parameter, or setter method annotated with @Autowired, it tries to find a matching bean in the container.
  • The matching process is based on the type of the dependency. In our example, Spring looks for a bean that implements the MyRepository interface.
  1. Bean Creation and Injection:
  • If a unique matching bean is found, Spring creates an instance of that bean (if it hasn't already) and injects it into the dependent bean. This is typically done through the constructor. If multiple matching beans are found, Spring tries to resolve the ambiguity using qualifiers or other mechanisms (discussed below).

How to Control Which Implementation Gets Injected

  • Qualifiers: You can use the @Qualifier annotation to explicitly specify which bean should be injected when multiple candidates exist.
  • Primary Beans: You can mark a bean as @Primary to make it the default choice when there's ambiguity.
  • Profiles: You can create different bean configurations for different environments (e.g., development, production) using @Profile.

Qualifier Example

@Repository
@Qualifier("jpaRepository") // Qualify this implementation
public class MyJpaRepository implements MyRepository { /* ... */ }

@Repository
@Qualifier("jdbcRepository") // Qualify this implementation
public class MyJdbcRepository implements MyRepository { /* ... */ }

@Service
public class MyServiceImpl implements MyService {
    private final MyRepository repository;

    @Autowired
    public MyServiceImpl(@Qualifier("jpaRepository") MyRepository repository) { // Inject the JPA one
        this.repository = repository;
    }
}
  • Convention over Configuration: Spring Boot favors convention over explicit configuration. By following naming conventions and using annotations, you can often avoid a lot of manual configuration.
  • Flexibility: You have control over how beans are created and injected. You can use annotations, XML, or Java configuration to customize the behavior.

Spring Container

  • The Spring container, also known as the Spring IoC (Inversion of Control) container or Application Context, is the core of the Spring Framework. It's responsible for managing the lifecycle of beans (objects) in your Spring application.
  • Key Responsibilities:
    • Bean Instantiation: Creates instances of beans based on their definitions (provided through annotations, XML, or Java configuration).
    • Dependency Injection (DI): Automatically injects dependencies between beans, ensuring that each bean gets the objects it needs to function.
    • Bean Wiring: Wires beans together, establishing relationships between them so they can collaborate.
    • Bean Lifecycle Management: Controls the entire lifecycle of beans, from creation and initialization to destruction.
    • Configuration Management: Reads and manages configuration metadata, which tells the container how to create and wire beans.

ApplicationContext

  • ApplicationContext is a central interface that provides configuration information and manages the lifecycle of beans (objects) within a Spring application. It acts as a container, holding references to all the beans and providing various services to access and manipulate them.

Understanding Some Frequent Used Annotations

  • Annotations are not part of the core logic of your code. They are metadata, meaning "data about data." They tell the compiler, framework, or other tools something about the elements they are attached to.

@Bean

  • What it is: In the Spring Framework, a bean is simply an object that is instantiated, managed, and provided by the Spring IoC (Inversion of Control) container.
  • Why it matters: Beans are the fundamental building blocks of your Spring application. The container takes care of creating them, wiring them together (dependency injection), and managing their lifecycle. This promotes loose coupling and makes your code easier to test and maintain.
  • How to create:
    • Annotations like @Component, @Service, @Repository, @Controller, etc.
    • XML configuration
    • Java configuration (@Configuration and @Bean methods)
  • The @Bean annotation is designed to be used on methods within a @Configuration class. Its purpose is to indicate that the method produces a bean that should be managed by the Spring container.
  • Bean is used on methods not classes

@Component

  • A general-purpose stereotype annotation (@Component) that marks a class as a candidate for auto-detection by the Spring container.
  • Why it matters: When you annotate a class with @Component, Spring automatically discovers and registers it as a bean. This is the foundation for Spring's component scanning and auto-configuration mechanisms.
  • You often use more specific annotations that extend @Component, like @Service for business logic, @Repository for data access, and @Controller for web controllers.
  • so if a class has 3 methods and we use component on class then bean is made of class or methods?: Spring creates a single bean instance of that entire class. This bean represents the class itself, not its individual methods.
  • if a class is not a component but inside we have used say @GetMapping will we have that bean registered?: No, simply having a method annotated with @GetMapping (or other request mapping annotations) within a class doesn't automatically register the class as a bean in the Spring container.
  • is it true that in place of @Controller I could use @Service and it still works same: No. Spring won't recognize the methods annotated with @RequestMapping as request handlers. Your application won't respond to incoming requests as expected.

Understanding Databases

JDBC

  • JDBC is an API (Application Programming Interface). It provides a set of classes and methods that allow Java applications to connect to various databases and execute SQL statements.
  • JDBC offers low-level control. You directly interact with the database using JDBC, writing raw SQL statements to perform CRUD (Create, Read, Update, Delete) operations and manage data retrieval.
  • JDBC requires manual resource management.

JPA

  • JPA is a specification that defines a standard way for Java applications to interact with relational databases. It provides a set of interfaces, annotations, and guidelines for object-relational mapping (ORM). It's like a universal language for data persistence in Java.
  • A higher-level abstraction. You work with entities (POJOs representing database tables) instead of writing raw SQL statements. JPA automatically maps these entities to tables and their properties to columns.
  • Think of JPA as a language that specifies how to map your Java objects (entities) to database tables and how to perform CRUD (Create, Read, Update, Delete) operations on that data.
  • @Entity: This annotation, along with others like @Id, @Column, etc., originates from the JPA specification.
  • In some rare cases, a carefully crafted raw SQL query might outperform a JPA query. This is typically for very complex queries or specific database operations. If you absolutely need to use raw SQL, JPA provides functionalities to execute native SQL queries within your application using @Query Annotation
@Repository
public interface MyEntityRepository extends JpaRepository<MyEntity, Long> {

  @Query(nativeQuery = true, value = "SELECT * FROM MY_TABLE WHERE name = ?1")
  List<MyEntity> findByNameNative(String name);
}
  • EntityManager
    • An interface defined by JPA that provides functionalities for interacting with persistence providers (like Hibernate) and managing entities.
    • An abstraction layer provided by JPA, agnostic to the specific JPA provider. It provides methods for managing the lifecycle of your entities, including creating, persisting, finding, updating, and deleting them.

Hibernate

  • Hibernate is an ORM implementation that follows the JPA specification. It's a specific tool that utilizes JPA functionalities to achieve object-relational mapping in your application.

  • It provides a concrete way to carry out the functionalities outlined by JPA.

  • A popular open-source implementation of the JPA specification. It provides its own implementation of the EntityManager interface, along with other functionalities for object-relational mapping.

  • Entity Class (POJO)

@Entity
public class Product {

  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  private Long id;

  private String name;
  private double price;

  // Getters and Setters omitted for brevity
}
  • HibernateUtil Class
public class HibernateUtil {

  private static final SessionFactory sessionFactory;

  static {
    try {
      // Configure Hibernate based on your configuration file (hibernate.cfg.xml)
      sessionFactory = new Configuration().configure().buildSessionFactory();
    } catch (Throwable ex) {
      throw new ExceptionInInitializerError(ex);
    }
  }

  public static Session getSession() {
    return sessionFactory.openSession();
  }
}
  • CRUD Operations
public class ProductManager {

  public static void main(String[] args) {

    // Create a Product
    Product product1 = new Product();
    product1.setName("Laptop");
    product1.setPrice(1000.00);

    // Create a Hibernate Session
    Session session = HibernateUtil.getSession();
    session.beginTransaction();

    // Save the product
    session.save(product1);

    // Commit the transaction
    session.getTransaction().commit();

    System.out.println("Product created with ID: " + product1.getId());

    // Read a Product
    session.beginTransaction();

    Product productFromDB = session.get(Product.class, product1.getId());

    System.out.println("Retrieved product: " + productFromDB.getName() + " (Price: $" + productFromDB.getPrice() + ")");

    session.getTransaction().commit();

    // Update a Product
    session.beginTransaction();

    productFromDB.setPrice(1200.00);

    // Update reflects in the session, no explicit update needed
    session.getTransaction().commit();

    System.out.println("Product updated (new price: $" + productFromDB.getPrice() + ")");

    // Delete a Product
    session.beginTransaction();

    session.delete(productFromDB);

    session.getTransaction().commit();

    System.out.println("Product deleted");

    session.close();
  }
}

Spring Data JPA

  • Spring Data JPA provides a layer of abstraction on top of JPA that simplifies repository development. It offers interfaces like Repository, CrudRepository, JpaRepository, and more, allowing you to extend them to create custom repositories for your entities.

  • Spring Data JPA relies on JPA for core functionalities. It utilizes JPA concepts like entities and persistence providers to handle data persistence.

  • Spring Data JPA is a library built on top of JPA. It's an extension specific to the Spring framework that simplifies JPA usage.

  • Repository: This is the most basic interface in Spring Data JPA repositories. It doesn't define any specific methods for data access. It's rarely extended directly.

  • CrudRepository: This interface extends Repository and defines essential CRUD (Create, Read, Update, Delete) operations for your entities. Methods like save(), findById(), findAll(), etc., are included.

  • JpaRepository: This interface extends CrudRepository and adds functionality specific to JPA. It includes methods for sorting, pagination, flushing data to the database, and some JPA-related entity management features.

  • You can also create your own custom interfaces that extend Repository, CrudRepository, or JpaRepository. This allows you to define additional methods specific to your data access needs beyond the built-in functionalities.

  • Hibernate as a JPA Provider: It interprets annotations like @Entity and provides functionalities to map your entities to a relational database.

    • When you mark a class with @Entity, you're essentially telling Hibernate (or any JPA provider) that this class represents a database entity.
    • Hibernate will create a corresponding table in the database based on the entity's properties.
    • Without Hibernate, Can You Use @Entity? Technically, the @Entity annotation is part of the JPA specification, not specific to Hibernate. However, in practice, you'd typically use it with a JPA provider like Hibernate to leverage its persistence functionalities. You can use JPA concepts like entities and annotations without Spring Data JPA, but it will involve more manual coding for data access logic.
  • Here are some other popular libraries that are used for data persistence in Java applications:

    • Spring Data JPA
    • Hibernate
    • jOOQ: It is a type-safe SQL query DSL (Domain-Specific Language) library. It allows you to write SQL queries in a Java-like syntax with compile-time type checking, improving code safety and readability.

Why Both Hibernate and Spring Data JPA used in Project?

  • Common Scenario: It's typical to have both Hibernate and Spring Data JPA in a Spring project that uses JPA for data persistence.
  • Working Together: Hibernate acts as the underlying JPA provider, responsible for the actual object-relational mapping and database interaction. Spring Data JPA builds upon this foundation by offering a simplified and Spring-friendly way to interact with your JPA entities through repositories.

Conclusion

  • When using Spring Boot, it often configures Hibernate as the default JPA provider unless you specify a different one. And we use Spring Data JPA for writing our crud respository. That is why Hibernate is an ORM and Spring Data JPA is an abstraction. Where postgres and all are simply DB that we use with them.

JpaRepository Methods

Category Method Description
Create (Save) save(T entity) Persists a given entity instance.
saveAll(Iterable<T> entities) Persists a collection of entities.
Read (Find) findById(ID id) Retrieves an entity by its ID (Optional return type).
findAll() Retrieves all entities of the type.
findAllById(Iterable<ID> ids) Retrieves a collection of entities by their IDs.
count() Returns the number of entities of the type.
existsById(ID id) Checks if an entity with the given ID exists.
Methods with findBy... (e.g., findByFirstName("John")) Finder methods to search by specific property values.
Update save(T entity) Can be used for updates as well, as it persists the modified state of the entity.
Delete deleteById(ID id) Deletes an entity by its ID.
delete(T entity) Deletes a specific entity instance.
deleteAllById(Iterable<ID> ids) Deletes a collection of entities by their IDs.
deleteAll() Deletes all entities of the type.
Other Methods getReferenceById(ID id) Returns a reference to an entity with the given ID (used for optimistic locking).
Methods with sorting and paging options (available in some JpaRepository extensions).
  • Creating our own method: You can extend JpaRepository<T, ID> to create your own custom repository interfaces for your entities. This allows you to define additional methods specific to your needs beyond the pre-defined ones in JpaRepository.
public interface ProductRepository extends JpaRepository<Product, Long> {

  // Additional finder method by name (not available in base JpaRepository)
  List<Product> findByName(String name);

  // Custom method using JPA Query (assuming a price property in Product)
  List<Product> findProductsByPriceGreaterThan(double minPrice);
}
  • Spring Data JPA provides a feature called convention over configuration. So findByName will work out of the box as you can guess what it do. If you write some custom method like myMethodSecret then you will have to implement it. Incase you want some different query set from defined convention method then use @Query, @Param or override (not suggested).

DTO

  • Data Transfer Objects or Domain Objects
  • Purpose: Transfer data between application layers or APIs, controlling the data exposure based on the use case.
  • Reduced complexity: Exposes only the necessary data for a specific use case, hiding internal implementation details.
  • Improved security
  • Loose coupling
  • UserDto acts as a DTO. It contains a subset of properties from User, potentially excluding sensitive data like password for presentation purposes.
// User class (represents the full user data)
public class User {
  private Long id;
  private String username;
  private String email;
  private String password; // Might be excluded from DTO for security

  // Getters and setters (omitted for brevity)
}

// UserDto class (represents data transferred for presentation or limited use cases)
public class UserDto {
  private Long id;
  private String username;
  private String email;

  // Getters and setters (omitted for brevity)
}

public class MyService {

  private ModelMapper modelMapper = new ModelMapper();

  public User convertDtoToUser(UserDto userDto) {
    // Use ModelMapper to map userDto properties to a new User object
    User user = modelMapper.map(userDto, User.class);
    return user;
  }
}
  • We will discuss Mapper seprately

DAO

  • DAO (Data Access Object) is a design pattern used to isolate the application logic from the persistence layer (usually a database) in Java applications. It provides a layer of abstraction that hides the details of how you interact with the database, offering methods for common CRUD (Create, Read, Update, Delete) operations on your entities.
  • Abstraction: It hides the underlying database technology (e.g., JDBC, JPA) from the application logic.
  • Encapsulation: It encapsulates the data access logic within the DAO class.
public interface ProductDao {

  Product saveProduct(Product product) throws SQLException;

  Product findProductById(Long id) throws SQLException;

  List<Product> findAllProducts() throws SQLException;

  void updateProduct(Product product) throws SQLException;

  void deleteProduct(Long id) throws SQLException;
}

public class ProductDaoJdbcImpl implements ProductDao {

  private final DataSource dataSource;

  // Constructor and implementation methods for CRUD operations using JDBC (omitted for brevity)

}

However, in modern Java applications, Spring Data JPA is generally the preferred approach for data access. It builds on top of JPA (Java Persistence API) and offers several advantages over traditional DAOs:

  • Simplified implementation
  • Automatic query generation
  • Improved maintainability
  • Integration with JPA features
  • Here's an example of a Spring Data JPA repository for the same Product entity:
public interface ProductRepository extends JpaRepository<Product, Long> {

  // Additional finder methods specific to your needs (optional)
  Product findProductByName(String name);

}
  • DAO using JDBC: Consider DAOs with JDBC in specific scenarios where you need fine-grained control over database interactions or are working with legacy code. However, it's generally less preferred due to its complexity and boilerplate code.

Serialization

  • Serialization is the process of converting a Java object into a byte stream or another format (like JSON or XML) that can be easily stored or transmitted across a network. This allows you to persist the state of an object to a file, database, or send it over a network connection to another system.
  • Java provides built-in support for serialization through the Serializable interface and the ObjectOutputStream and ObjectInputStream classes. By implementing the Serializable interface, you mark a class as serializable.
  • By default, Spring Boot uses Jackson to automatically convert Java objects to JSON for RESTful API responses and vice versa for incoming requests.
  • You can customize the serialization/deserialization process in Spring Boot using Jackson annotations like @JsonProperty, @JsonIgnore, @JsonInclude, and more.

Mapper

  • A mapper is a component that facilitates the transformation of data between different object models or representations. It acts as a bridge, allowing you to convert objects from one format to another while maintaining the integrity and relationships of the data.
  • Example you might have a domain model that represents your business objects and a DTO (Data Transfer Object) model that is used to transfer data over a network. A mapper allows you to keep these models distinct and avoid tight coupling between them.
  • You can write manual mapping or using libraries like MapStruct or ModelMapper.
  • Manual Mapping
src/main/java/com/example/myapp
├── controller
│   └── CustomerController.java
├── dto
│   └── CustomerDto.java
├── entity
│   └── Customer.java
├── mapper
│   └── CustomerMapper.java
├── repository
│   └── CustomerRepository.java
└── service
    └── CustomerService.java

Customer.java

@Entity
public class Customer {
    @Id
    @GeneratedValue
    private Long id;
    private String firstName;
    private String lastName;
    private String email;
    private Map<String, String> additionalAttributes; // Map to store additional data

    // Constructors, getters, setters...
}

CustomerDto.java

public class CustomerDto {
    private Long id;
    private String firstName;
    private String lastName;
    private String email;
    private Map<String, String> additionalAttributes; // Map to store additional data

    // Constructors, getters, setters...
}

CustomerMapper.java

public class CustomerMapper {

    public static CustomerDto toDto(Customer customer) {
        if (customer == null) {
            return null;
        }

        CustomerDto dto = new CustomerDto();
        dto.setId(customer.getId());
        dto.setFirstName(customer.getFirstName());
        dto.setLastName(customer.getLastName());
        dto.setEmail(customer.getEmail());

        // Map additional attributes
        dto.setAdditionalAttributes(new HashMap<>(customer.getAdditionalAttributes()));

        return dto;
    }

    public static Customer toEntity(CustomerDto dto) {
        // Similar logic for mapping DTO to entity
    }
}

CustomerController.java

@RestController
@RequestMapping("/customers")
public class CustomerController {

    @Autowired
    private CustomerService customerService;

    @GetMapping("/{id}")
    public ResponseEntity<CustomerDto> getCustomer(@PathVariable Long id) {
        Customer customer = customerService.getCustomerById(id);
        return ResponseEntity.ok(CustomerMapper.toDto(customer)); 
    }

    // ... other endpoints for creating, updating, deleting customers ...
}
  • ModelMapper

CustomerMapper.java <-- Updated

import org.modelmapper.ModelMapper;
import org.springframework.stereotype.Component; // Add this annotation

@Component // This registers the mapper as a Spring bean
public class CustomerMapper {

    private static final ModelMapper modelMapper = new ModelMapper(); // Create a ModelMapper instance

    public CustomerDto toDto(Customer customer) {
        return modelMapper.map(customer, CustomerDto.class); // Simple mapping
    }

    public Customer toEntity(CustomerDto dto) {
        return modelMapper.map(dto, Customer.class); // Simple mapping
    }
}

CustomerController.java (Minor Change)

@RestController
@RequestMapping("/customers")
public class CustomerController {

    @Autowired
    private CustomerService customerService;

    @Autowired
    private CustomerMapper customerMapper;  // Inject the mapper

    @GetMapping("/{id}")
    public ResponseEntity<CustomerDto> getCustomer(@PathVariable Long id) {
        Customer customer = customerService.getCustomerById(id);
        return ResponseEntity.ok(customerMapper.toDto(customer)); // Use the mapper
    }

    // ... other endpoints ...
}
  • MapStruct : for mapping between Customer and CustomerDto:

CustomerMapper.java (Updated):

import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;

@Mapper
public interface CustomerMapper {
    CustomerMapper INSTANCE = Mappers.getMapper(CustomerMapper.class); // Singleton instance

    CustomerDto toDto(Customer customer);
    Customer toEntity(CustomerDto dto);
}

CustomerController.java (Minor Change):

@RestController
@RequestMapping("/customers")
public class CustomerController {

    @Autowired
    private CustomerService customerService;

    @GetMapping("/{id}")
    public ResponseEntity<CustomerDto> getCustomer(@PathVariable Long id) {
        Customer customer = customerService.getCustomerById(id);
        return ResponseEntity.ok(CustomerMapper.INSTANCE.toDto(customer)); // Use the mapper instance
    }

    // ... other endpoints ...
}

MapStruct do Compile-time Validation thus provide type-safey as well. It is most advance of three.


Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment