Summary
Highlights
This immersive 13-hour course covers Spring Boot for building standalone applications and Spring Data JPA for simplifying data access in Java. Mastering these frameworks is crucial for career advancement, as demand for skilled developers is high.
The Spring Framework is an open-source tool for Enterprise Java applications, simplifying complex development with features like Aspect-Oriented Programming (AOP), Dependency Injection, and Plain Old Java Objects (POJO). Key features include the IoC container for managing Java objects, AOP for cross-cutting concerns (logging, transactions), a data access framework supporting JDBC, Hibernate, and JPA, and Spring MVC for building web applications following the MVC pattern.
Spring Beans are objects managed by the Spring framework, with their lifecycle (instantiation, configuration, dependencies, destruction) handled by the Spring container. Beans can be configured using @Configuration and @Bean annotations in Java classes, allowing for dependency injection. Different stereotypes like @Component, @Service, and @Repository classify Spring components, and bean naming follows method names by default or can be explicitly defined.
Spring offers four ways to inject dependencies: Constructor Injection (recommended for mandatory dependencies), Field Injection (discouraged for production, suitable for tests), Method Injection, and Setter Injection (for optional dependencies). Constructor injection is generally advocated by the Spring team for its clarity and immutability benefits.
Bean scopes define the lifecycle and availability of a Spring bean. Default is Singleton (one instance per application context), other scopes include Prototype (new instance per request), Request, Session, Application, and WebSocket (web-specific scopes). Special beans like 'environment' allow access to properties and profiles, enabling environment-specific configurations (e.g., dev, test, prod) with `@Profile` annotation or `application.properties`/`application.yaml`.
Spring Initializr (start.spring.io) simplifies project setup, allowing selection of build system (Maven/Gradle), language (Java/Kotlin/Groovy), Spring Boot version, and metadata (group/artifact ID). Dependencies like 'Spring Web' and 'Spring Data JPA' can be easily added. Java 17 is the minimum for Spring Boot 3.x. The generated project includes a main application class to run an empty Spring Boot application, typically on port 8080.
The project structure includes `.idea` (IDE files), `mvn` (Maven wrapper for easy execution), `src/main/java` (source code), `src/main/resources` (static files, templates, `application.properties/yaml` for configuration), and `src/test/java` (test files). `pom.xml` manages dependencies and project details. Customizing the application banner is possible by creating a `banner.txt` file in `src/main/resources`.
Beans can be created using the `@Bean` annotation within a `@Configuration` class, or by annotating a class with `@Component`, `@Service`, or `@Repository`. Spring manages the lifecycle and dependencies of these beans. When multiple beans of the same type exist, `@Qualifier` or `@Primary` annotations can specify which one to inject. Splitting configuration into multiple classes using `@Import` is a best practice for maintainability.
REST (Representational State Transfer) is an architectural style for web services, defining constraints for building stateless, client-server applications. Key principles include client-server architecture, statelessness, cacheability, layered system, and a uniform interface. RESTful APIs use standard HTTP methods (GET, POST, PUT, DELETE) and plural nouns for resources (e.g., `/accounts`).
HTTP methods (verbs) like GET (retrieve), POST (create), PUT (update/create), DELETE (delete), PATCH (partial update), OPTIONS (supported methods), and HEAD (headers only) define actions on resources. Status codes provide feedback: 2xx (Success: 200 OK, 201 Created, 204 No Content), 3xx (Redirection: 304 Not Modified for caching), 4xx (Client Error: 400 Bad Request, 401 Unauthorized, 403 Forbidden), and 5xx (Server Error: 500 Internal Server Error, 503 Service Unavailable).
A Spring `RestController` uses `@RestController` and HTTP method annotations (e.g., `@GetMapping`, `@PostMapping`). Requests can include parameters in the path (`@PathVariable`) or query string (`@RequestParam`). `@RequestBody` binds the HTTP request body to a method parameter. Status codes can be customized with `@ResponseStatus`.
Postman is an API client for testing HTTP requests. It allows sending different HTTP methods, setting headers, and sending various body types (text, JSON, form data). When sending complex objects in the request body (e.g., JSON), ensuring the Java object has appropriate getters and setters (accessors) is crucial for Spring to correctly deserialize the JSON into Java objects. `@JsonProperty` can be used to map JSON field names to different Java field names.
Java Records (introduced in Java 14) offer a concise way to declare data-carrying classes, automatically providing constructors, getters, `equals()`, `hashCode()`, and `toString()`. They are suitable for simple Data Transfer Objects (DTOs) without complex logic. POJOs (Plain Old Java Objects) are versatile but require more boilerplate code for accessors and standard methods. Records are immutable and final, making them unsuitable for libraries requiring mutable beans (like older Hibernate/JPA versions). The choice depends on Java version, mutability needs, and complexity of the DTO.
`@PathVariable` extracts values from the URI path, while `@RequestParam` extracts query parameters (e.g., `?key=value`). In Spring MVC, `@Controller` or `@RestController` are used at the class level, and `@RequestMapping` (or specialized `@GetMapping`, etc.) at the method level to handle HTTP requests. The `DispatcherServlet` uses `RequestMappingHandlerMapping` to match incoming requests to the appropriate controller method based on URL, HTTP method, and path variables. Ambiguous mappings lead to startup errors.
To set up a PostgreSQL database with Docker, use `docker run` with options for detached mode (`-d`), persistent volumes (`-v`), environment variables for username/password (`-e`), and port mapping (`-p`). Database connection details are configured in `application.yaml` or `application.properties` under `spring.datasource` (URL, username, password, driver class name). The driver dependency (e.g., `postgresql`) must be added to `pom.xml`. `@ddl-auto` hibernate property handles schema creation/update.
An entity is a Java object mapped to a database table. `@Entity` marks a class as an entity. It requires an `@Id` for the primary key (using wrapper types like `Integer` for nullability). `@Generated` facilitates auto-incrementing IDs. `@Table` allows customizing the table name in the database. `@Column` provides fine-grained control over column properties like name, unique, nullable, length, insertable, and updatable.
Spring Data JPA repositories are Java interfaces simplifying data access. They extend interfaces like `JpaRepository`, `CrudRepository`, or `PagingAndSortingRepository`, providing predefined methods for CRUD operations (save, find, delete). Extending `JpaRepository` gives access to a wide range of functionalities. No explicit implementation is needed; Spring generates queries at runtime based on method names or `@Query` annotations.
Relationships (one-to-one, one-to-many, many-to-many) model real-world data connections, enforcing data integrity and improving performance. Many-to-many relationships (e.g., author to course) are implemented using `@ManyToMany` annotations on lists in both entities. One entity is the 'owner' (e.g., Course), defining the `@JoinTable` with `name`, `joinColumns` (for owner's ID), and `inverseJoinColumns` (for inverse entity's ID). The other entity uses `mappedBy` pointing to the owner's field name.
One-to-many relationships (e.g., course to section) are implemented using `@OneToMany` on the list in the 'one' side (Course) and `mappedBy` to the field name on the 'many' side (Section). The 'many' side (Section) has a `@ManyToOne` annotation to the 'one' entity (Course) and uses `@JoinColumn` to specify the foreign key column name (e.g., `course_id`).
When dealing with bidirectional relationships, JSON serialization can lead to infinite recursion. To prevent this, use Jackson annotations `@JsonManagedReference` on the 'owning' side (the parent entity's collection field) and `@JsonBackReference` on the 'inverse' side (the child entity's reference to the parent). This tells Jackson to serialize the owning side fully but skip the back-reference to prevent loops.
DTOs encapsulate and structure data transferred between different system parts, separating the internal domain model from the exposed API representation. DTOs offer data separation, abstraction, performance improvements (sending only necessary data), flexibility (multiple representations per object), and easier versioning. They are crucial for controlling data exposure and simplifying client-server communication.
Introducing a service layer helps separate concerns: controllers handle requests/responses, repositories manage data access, and services encapsulate business logic. This promotes code reusability, modularity, and testability. Services act as an intermediary, handling validation, complex calculations, and coordinating multiple operations, leading to better code organization and maintainability.
Data validation in REST APIs is crucial for data integrity, security, error prevention, user experience, performance, and business logic compliance. Spring Boot Starter Validation (`spring-boot-starter-validation`) provides annotations like `@NotEmpty`, `@NotBlank`, `@NotNull`, `@Email`, `@Min`, `@Max`, `@Size`, `@Pattern`, `Future`, `Past`, `Positive`, etc. Validation is applied to DTOs using `@Valid` in controller methods. Custom error messages can be defined with the `message` attribute in validation annotations.
When validation fails, Spring throws a `MethodArgumentNotValidException`. This can be caught and handled within the controller using `@ExceptionHandler`. A custom handler method can extract validation errors (field name and default message) and return a structured `ResponseEntity` (e.g., a `Map` of errors) with an appropriate HTTP status code (e.g., 400 Bad Request) to improve user experience.
Software testing is crucial for quality assurance, regression prevention, documentation, good coding practices, refactoring safety, and collaboration. Spring Boot provides `spring-boot-starter-test`, including JUnit, AssertJ, Mockito, and Hamcrest. `@SpringBootTest` loads the application context for tests. `spring-boot-test-autoconfigure` offers annotations for testing 'slices' of the application, loading only necessary configurations. Mocking (using Mockito) allows testing components in isolation from their dependencies.
Mockito is a Java mocking framework used for unit testing. `@Mock` creates mock objects for dependencies, and `@InjectMocks` injects these mocks into the service being tested. The `@BeforeEach` method can be used to initialize the mocks using `MockitoAnnotations.openMocks(this)`. `Mockito.when().thenReturn()` stubs method calls of mocked dependencies, and `Mockito.verify().times(n)` ensures methods are called a specific number of times.
Named queries centralize, optimize, and organize query definitions. They are defined either within the entity class using `@NamedQuery` (for single queries) or `@NamedQueries` (for multiple queries), allowing JPQL or native SQL. The named query method in the repository uses `@Param` to map parameters. Named queries are validated and optimized at application startup, improving performance and maintainability. Update queries require `@Modifying` and `@Transactional` annotations.
Spring Data JPA Specifications provide a flexible way to build dynamic, type-safe queries using the JPA Criteria API. The repository must extend `JpaSpecificationExecutor`. Specifications are created as static methods returning `Specification<T>` (T being the entity type), and using a `CriteriaBuilder` to construct predicates (filters) based on root entity and attributes. Specifications can be combined with `and()`, `or()`, and `not()` methods to build complex queries dynamically.