Module: Database Integration

CRUD Operations

Introduction

This tutorial will guide you through integrating a database with your Spring Boot application and performing basic CRUD (Create, Read, Update, Delete) operations. We'll use H2, an in-memory database, for simplicity, but the concepts apply to other databases like MySQL, PostgreSQL, etc.

Prerequisites

  • Java Development Kit (JDK) 8 or higher
  • Maven or Gradle
  • An IDE (IntelliJ IDEA, Eclipse, VS Code)
  • Basic understanding of Spring Boot

Step 1: Add Database Dependencies

First, add the necessary dependencies to your pom.xml (Maven) or build.gradle (Gradle) file.

Maven (pom.xml):

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
    <groupId>com.h2database</groupId>
    <artifactId>h2</artifactId>
    <scope>runtime</scope>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

Gradle (build.gradle):

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
    runtimeOnly 'com.h2database:h2'
    implementation 'org.springframework.boot:spring-boot-starter-web'
}
  • spring-boot-starter-data-jpa: Provides JPA (Java Persistence API) support for database interactions.
  • h2: The H2 in-memory database. scope=runtime means it's only needed during runtime.
  • spring-boot-starter-web: Needed for creating REST controllers.

Step 2: Define the Entity

Create a Java class representing the data you want to store in the database. This class will be annotated with JPA annotations.

import javax.persistence.*;

@Entity
@Table(name = "products") // Optional: Specify the table name
public class Product {

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

    private String name;

    private double price;

    private String description;

    // Constructors, Getters, and Setters
    public Product() {}

    public Product(String name, double price, String description) {
        this.name = name;
        this.price = price;
        this.description = description;
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public double getPrice() {
        return price;
    }

    public void setPrice(double price) {
        this.price = price;
    }

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }
}
  • @Entity: Marks the class as a JPA entity.
  • @Table(name = "products"): Specifies the database table name. If omitted, the table name defaults to the class name (lowercase).
  • @Id: Marks the id field as the primary key.
  • @GeneratedValue(strategy = GenerationType.IDENTITY): Configures the primary key to be auto-incremented by the database.

Step 3: Create a Repository Interface

Create an interface that extends JpaRepository. This interface provides methods for interacting with the database.

import org.springframework.data.jpa.repository.JpaRepository;

public interface ProductRepository extends JpaRepository<Product, Long> {
    // You can add custom query methods here if needed
}
  • JpaRepository<Product, Long>: Product is the entity type, and Long is the type of the primary key. JpaRepository provides methods like save(), findById(), findAll(), delete(), etc.

Step 4: Create a REST Controller

Create a REST controller to handle HTTP requests and interact with the repository.

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.util.List;

@RestController
@RequestMapping("/products")
public class ProductController {

    @Autowired
    private ProductRepository productRepository;

    // Create (POST)
    @PostMapping
    public ResponseEntity<Product> createProduct(@RequestBody Product product) {
        Product savedProduct = productRepository.save(product);
        return new ResponseEntity<>(savedProduct, HttpStatus.CREATED);
    }

    // Read (GET) - Get all products
    @GetMapping
    public ResponseEntity<List<Product>> getAllProducts() {
        List<Product> products = productRepository.findAll();
        return new ResponseEntity<>(products, HttpStatus.OK);
    }

    // Read (GET) - Get product by ID
    @GetMapping("/{id}")
    public ResponseEntity<Product> getProductById(@PathVariable Long id) {
        return productRepository.findById(id)
                .map(product -> new ResponseEntity<>(product, HttpStatus.OK))
                .orElse(new ResponseEntity<>(HttpStatus.NOT_FOUND));
    }

    // Update (PUT)
    @PutMapping("/{id}")
    public ResponseEntity<Product> updateProduct(@PathVariable Long id, @RequestBody Product updatedProduct) {
        if (productRepository.findById(id).isEmpty()) {
            return new ResponseEntity<>(HttpStatus.NOT_FOUND);
        }

        updatedProduct.setId(id); // Ensure the ID is set correctly
        Product savedProduct = productRepository.save(updatedProduct);
        return new ResponseEntity<>(savedProduct, HttpStatus.OK);
    }

    // Delete (DELETE)
    @DeleteMapping("/{id}")
    public ResponseEntity<Void> deleteProduct(@PathVariable Long id) {
        if (productRepository.findById(id).isEmpty()) {
            return new ResponseEntity<>(HttpStatus.NOT_FOUND);
        }
        productRepository.deleteById(id);
        return new ResponseEntity<>(HttpStatus.NO_CONTENT);
    }
}
  • @RestController: Marks the class as a REST controller.
  • @Autowired: Injects the ProductRepository dependency.
  • @RequestMapping("/products"): Maps HTTP requests to this controller to the /products path.
  • @PostMapping, @GetMapping, @PutMapping, @DeleteMapping: Annotations for handling different HTTP methods.
  • @RequestBody: Binds the request body to the method parameter.
  • @PathVariable: Extracts values from the URL path.
  • ResponseEntity: Used to return HTTP responses with status codes and bodies.

Step 5: Run the Application

Run your Spring Boot application. The H2 database will be automatically configured in memory.

Step 6: Test the API

You can use tools like Postman, curl, or a web browser to test the API endpoints:

  • Create (POST): POST /products with a JSON body containing product details.
  • Read (GET) - All: GET /products
  • Read (GET) - By ID: GET /products/{id}
  • Update (PUT): PUT /products/{id} with a JSON body containing updated product details.
  • Delete (DELETE): DELETE /products/{id}

Accessing the H2 Console (Optional)

To view the data in the H2 database, you can access the H2 console. Add the following to your application.properties or application.yml file:

application.properties:

spring.h2.console.enabled=true
spring.h2.console.path=/h2-console

application.yml:

spring:
  h2:
    console:
      enabled: true
      path: /h2-console

Then, access the console at http://localhost:8080/h2-console (replace 8080 with your application's port). The JDBC URL will be jdbc:h2:mem:testdb (default).

Conclusion

This tutorial covered the basics of database integration and CRUD operations in Spring Boot. You can now extend this foundation to build more complex applications with persistent data storage. Remember to adapt the code to your specific database and data model. Consider using a more robust database like MySQL or PostgreSQL for production environments.