Getting Started with Spring Data JPA and Hibernate in Spring Boot

To get started with Spring Data JPA and Hibernate in a Spring Boot application, follow these steps:

1. Add Required Dependencies

Include spring-boot-starter-data-jpa and h2 (or any other database) dependencies in your pom.xml (for Maven projects).

Maven:

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

2. Configure the Application Properties

Set up the database configurations in application.properties or application.yaml. Below is an example for H2 database:
application.properties:

spring.datasource.url=jdbc:h2:mem:testdb
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect

3. Create an Entity Class

Create a Java class annotated with @Entity to represent your database table. Also, use mapping annotations (@Id, @GeneratedValue, etc.) to configure the primary key and other relationships.

import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;

@Entity
public class Employee {

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

    private String name;
    private String department;
    private Double salary;

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

    public Employee(String name, String department, Double salary) {
        this.name = name;
        this.department = department;
        this.salary = salary;
    }

    // Getters and Setters
    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 String getDepartment() {
        return department;
    }

    public void setDepartment(String department) {
        this.department = department;
    }

    public Double getSalary() {
        return salary;
    }

    public void setSalary(Double salary) {
        this.salary = salary;
    }
}

4. Create a Spring Data JPA Repository

Create an interface for the repository that extends JpaRepository

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

public interface EmployeeRepository extends JpaRepository<Employee, Long> {
    // You can define custom query methods here if needed
}

5. Create a Spring Boot Service

Add a service layer to encapsulate business logic and interact with the repository.

import org.springframework.stereotype.Service;

import java.util.List;

@Service
public class EmployeeService {

    private final EmployeeRepository employeeRepository;

    public EmployeeService(EmployeeRepository employeeRepository) {
        this.employeeRepository = employeeRepository;
    }

    public List<Employee> getAllEmployees() {
        return employeeRepository.findAll();
    }

    public Employee saveEmployee(Employee employee) {
        return employeeRepository.save(employee);
    }
}

6. Create a Controller

Now, create a REST Controller to expose APIs for interacting with your service.

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

import java.util.List;

@RestController
@RequestMapping("/employees")
public class EmployeeController {

    @Autowired
    private EmployeeService employeeService;

    @GetMapping
    public List<Employee> getAllEmployees() {
        return employeeService.getAllEmployees();
    }

    @PostMapping
    public Employee createEmployee(@RequestBody Employee employee) {
        return employeeService.saveEmployee(employee);
    }
}

7. Run the Application

Run your Spring Boot application (@SpringBootApplication annotated class) and test your REST endpoints.

  • GET /employees → Retrieve all employees
  • POST /employees → Add a new employee (pass JSON body)

Example JSON for the POST request:

{
    "name": "John Doe",
    "department": "Engineering",
    "salary": 65000.00
}

8. Testing

You can use tools like Postman, Curl, or Spring Boot DevTools to verify that your application works as expected.

9. Switching to a Production Database

Replace the H2 configurations with your production database configurations (e.g., MySQL, PostgreSQL).
For example, using MySQL:

Dependencies:

<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <scope>runtime</scope>
</dependency>

application.properties:

spring.datasource.url=jdbc:mysql://localhost:3306/mydb
spring.datasource.username=root
spring.datasource.password=yourpassword
spring.jpa.database-platform=org.hibernate.dialect.MySQLDialect

This basic setup is a starting point.

How do I integrate Spring’s IoC container with non-Spring-managed objects?

Integrating Spring’s Inversion of Control (IoC) container with non-Spring-managed objects can be useful when you need to access Spring-managed beans within components or instances that are not managed by the Spring framework. Below are some common approaches to achieve this:

1. Using the ApplicationContext Directly

You can access Spring’s ApplicationContext (which holds the Spring container) and retrieve beans from it. The ApplicationContext instance can be injected into any Spring-managed bean or accessed statically.

Steps:

  1. Register the ApplicationContext as a bean.
  2. Use it to fetch beans manually.

Example:

package org.kodejava.spring;

import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;

@Component
public class ApplicationContextProvider implements ApplicationContextAware {

    private static ApplicationContext context;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) {
        context = applicationContext;
    }

    public static ApplicationContext getApplicationContext() {
        return context;
    }
}

Retrieving the Bean:

From a non-Spring-managed class, you can access the Spring context like this:

package org.kodejava.spring;

import org.springframework.context.ApplicationContext;

public class NonSpringClass {
    public void doSomething() {
        ApplicationContext context = ApplicationContextProvider.getApplicationContext();
        MyBean myBean = context.getBean(MyBean.class);
        myBean.performTask();
    }
}

2. Using @Configurable with AspectJ

Spring provides the @Configurable annotation, which can inject dependencies into objects not managed by Spring, such as those manually instantiated using the new operator. This requires AspectJ weaving to work.

Steps:

  1. Add @Configurable to the class.
  2. Enable annotation-driven dependency injection for AspectJ using Spring AOP.

Example:

package org.kodejava.spring;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Configurable;

@Configurable
public class NonSpringClass {

    @Autowired
    private MyBean myBean;

    public void execute() {
        myBean.performTask();
    }
}

Configuration Example:

Add the following to your Spring configuration:

<context:spring-configured />
<tx:annotation-driven />

Ensure AspectJ weaving is enabled either at compile-time or runtime using a javaagent.

3. Using BeanFactory or AutowireCapableBeanFactory

Spring’s AutowireCapableBeanFactory can be used to autowire fields or methods in non-Spring-managed objects.

Example:

package org.kodejava.spring;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.stereotype.Component;

@Component
public class NonSpringClassFactory {

    private final AutowireCapableBeanFactory beanFactory;

    @Autowired
    public NonSpringClassFactory(AutowireCapableBeanFactory beanFactory) {
        this.beanFactory = beanFactory;
    }

    public NonSpringClass createNonSpringClass() {
        NonSpringClass instance = new NonSpringClass();
        beanFactory.autowireBean(instance);
        return instance;
    }
}

4. Programmatic Injection

If you cannot use annotations and static utility classes, you can always inject the Spring bean manually into the non-Spring-managed object.

Example:

package org.kodejava.spring;

import org.kodejava.spring.construct.MyBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class AppConfig {

    @Bean
    public NonSpringClass nonSpringClass(MyBean myBean) {
        return new NonSpringClass(myBean);
    }
}

Non-Spring class:

package org.kodejava.spring;

public class NonSpringClass {
    private final MyBean myBean;

    public NonSpringClass(MyBean myBean) {
        this.myBean = myBean;
    }

    public void execute() {
        myBean.performTask();
    }
}

5. Event Listeners and Publishers

If the interaction is event-driven, you can use Spring’s event-publishing infrastructure and handle events in non-Spring-managed objects.

Example:

The Spring-managed components publish events that can be consumed by non-Spring-managed listeners via custom hooks.

Recommendations:

  • Choose ApplicationContext or AutowireCapableBeanFactory: When dealing with legacy classes or objects where Spring cannot manage the lifecycle.
  • Use @Configurable: When you need seamless dependency injection for dynamically created objects but can rely on AspectJ weaving.

Troubleshooting Common Errors in Your First Spring Boot Application

When starting your first Spring Boot application, you might encounter some common errors. Here’s a guide on how to troubleshoot them effectively.

1. Port Already in Use

  • Error Message:
     java.net.BindException: Address already in use: JVM_BIND
  • Cause: Another application (or another instance of your app) is already using the default Spring Boot port (8080).
  • Solution:
    • Run your application on a different port by adding the following entry in application.properties or application.yml:
      application.properties:
       server.port=8081

application.yml:

       server:
         port: 8081
  • Alternatively, stop the process currently using the port (8080) by running the command:
    • Linux/macOS: sudo lsof -t -i:8080 | xargs kill -9
    • Windows: Use netstat -ano to find the process using the port and then kill it in Task Manager.

2. Dependency Issues

  • Error Message:
     Could not resolve dependencies for project
     or
     ClassNotFoundException
  • Cause: Missing dependencies or inconsistencies in your pom.xml file (Maven) or build.gradle file (Gradle).
  • Solution:
    • Ensure your dependencies are correctly added and check for typos in artifact or group IDs.
    • Run mvn clean install or ./gradlew build to refresh your dependencies.

3. Spring Boot Application Fails to Start

  • Error Message:
     NoSuchBeanDefinitionException
  • Cause: There might be missing or improperly defined beans during auto-configuration.
  • Solution:
    • Ensure you have annotated your component classes correctly:
      • @Service, @Repository, @Controller, or @RestController for Spring beans.
      • Use @ComponentScan to include the packages containing your components.
    • Check for typos in package names or external configuration.

4. Property Source Errors

  • Error Message:
     Cannot bind to property [property name]
  • Cause: Your application is trying to bind a property (e.g., from or application.yml) that is missing or invalid. application.properties
  • Solution:
    • Verify that all mandatory properties are defined in your configuration.
    • For stricter validation, use @ConfigurationProperties with type-safe property binding.

5. Database Connection Errors

  • Error Message:
     Cannot connect to database
  • Cause: Incorrect database URL, username, password, or the database server is not running.
  • Solution:
    • Verify your database properties in or application.yml: application.properties
       spring.datasource.url=jdbc:mysql://localhost:3306/mydb
       spring.datasource.username=root
       spring.datasource.password=password
  • Ensure the database server is running and accessible.

6. Missing @Entity Annotation

  • Error Message:
     Unable to locate persistent class
  • Cause: One or more of your JPA entities may be missing the @Entity annotation.
  • Solution:
    • Ensure all entity classes are annotated with @Entity.
    • Ensure that their primary keys are annotated appropriately with @Id.

7. Thymeleaf or Static Resource Not Found

  • Error Message:
     TemplateNotFoundException
  • Cause: The template file might be missing or placed in the wrong directory.
  • Solution:
    • Thymeleaf templates should be located in src/main/resources/templates.
    • Static resources (CSS, JS, etc.) should be in src/main/resources/static.

8. Circular Dependency Error

  • Error Message:
     Circular view path or Circular dependency detected
  • Cause: Circular injection of beans in the application context.
  • Solution:
    • Use the @Lazy annotation to lazily initialize one of the beans.
    • Restructure your code to eliminate the circular dependency.

9. Incorrect Main Class Declaration

  • Error Message:
     No qualifying bean of type [YourClassName]
  • Cause: The class with the @SpringBootApplication annotation is not correctly defined or is missing.
  • Solution:
    • Ensure that your @SpringBootApplication main class is in the root package, so that Spring Boot can scan all sub-packages.

10. Version Conflicts

  • Error Message:
     MethodNotFound or ClassNotFoundException
  • Cause: Your dependency versions are incompatible.
  • Solution:
    • Use a compatible Spring Boot starter version in your or build.gradle. pom.xml
    • Check the Spring Initializer Website for recommended versions.

Tools for Troubleshooting:

  • Logs: Check logs carefully to locate the cause of the error.
  • DevTools: Use Spring Boot DevTools for automatic restarts and live reloading.
  • Dependency Analyzer: Check dependency conflicts using mvn dependency:tree or ./gradlew dependencies.
  • Debugging: Use breakpoints and IDE debugging tools.

How do I programmatically register beans into the Spring context at runtime?

In Spring, you can programmatically register beans into the application context at runtime using the BeanDefinitionRegistry or ConfigurableApplicationContext. Below are different approaches depending on your use case:

1. Using BeanDefinitionRegistry

The BeanDefinitionRegistry interface can be used to dynamically register bean definitions in the Spring context. ApplicationContext implementations like AnnotationConfigApplicationContext or GenericApplicationContext expose a BeanDefinitionRegistry.

Here’s an example:

package org.kodejava.spring;

import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class DynamicBeanRegistrationExample {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();

        // Get the BeanDefinitionRegistry
        DefaultListableBeanFactory beanFactory = context.getDefaultListableBeanFactory();

        // Create a bean definition
        RootBeanDefinition beanDefinition = new RootBeanDefinition(MyBean.class);

        // Register the bean definition with a specific name
        beanFactory.registerBeanDefinition("myBean", beanDefinition);

        // Refresh the context to reflect changes
        context.refresh();

        // Access the dynamically added bean
        MyBean myBean = context.getBean(MyBean.class);
        myBean.sayHello();

        context.close();
    }

    static class MyBean {
        public void sayHello() {
            System.out.println("Hello from MyBean!");
        }
    }
}

Explanation:

  • The DefaultListableBeanFactory is used to register a bean definition.
  • The RootBeanDefinition object allows you to define the settings for the bean.
  • The refresh() method lets the application context pick up the newly added bean definitions.

2. Using ConfigurableApplicationContext

The ConfigurableApplicationContext provides methods like register to directly add a class to the Spring context.

Here’s an example:

package org.kodejava.spring;

import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class DynamicBeanRegistrationExample2 {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();

        // Register a configuration class or bean type directly
        context.register(MyBean.class);

        // Refresh the context to reflect the bean
        context.refresh();

        // Access the dynamically added bean
        MyBean myBean = context.getBean(MyBean.class);
        myBean.sayHello();

        context.close();
    }

    static class MyBean {
        public void sayHello() {
            System.out.println("Hello from MyBean!");
        }
    }
}

Explanation:

  • The register method dynamically registers a @Component, @Bean, or class definition into the Spring context.

3. Using GenericApplicationContext

A GenericApplicationContext allows for dynamic bean registrations through registerBean().

Here’s an example:

package org.kodejava.spring;

import org.springframework.context.support.GenericApplicationContext;

public class DynamicBeanRegistrationExample3 {
    public static void main(String[] args) {
        GenericApplicationContext context = new GenericApplicationContext();

        // Programmatically register a bean with a name and configuration
        context.registerBean(MyBean.class, () -> new MyBean());

        // Refresh the context to apply changes
        context.refresh();

        // Access the dynamically added bean
        MyBean myBean = context.getBean(MyBean.class);
        myBean.sayHello();

        context.close();
    }

    static class MyBean {
        public void sayHello() {
            System.out.println("Hello from MyBean!");
        }
    }
}

Explanation:

  • The registerBean method registers a bean class along with a Supplier object that provides the bean instance.

4. Using an ApplicationContextInitializer

If you want to register beans at the application startup phase, you can use an ApplicationContextInitializer.

Example:

package org.kodejava.spring;

import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.support.GenericApplicationContext;

public class MyInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
    @Override
    public void initialize(ConfigurableApplicationContext applicationContext) {
        if (applicationContext instanceof GenericApplicationContext) {
            GenericApplicationContext genericContext = (GenericApplicationContext) applicationContext;

            // Register a bean dynamically
            genericContext.registerBean(MyBean.class, MyBean::new);
        }
    }

    static class MyBean {
        public void sayHello() {
            System.out.println("Hello from MyBean!");
        }
    }
}

To use this initializer, you would pass it when initializing your Spring application, for example:

new SpringApplicationBuilder(MyApplication.class)
    .initializers(new MyInitializer())
    .run(args);

Things to Keep in Mind

  • Bean Scope: You can set the scope (singleton, prototype, etc.) of a bean when registering it dynamically.
  • Dependencies: Ensure that you properly handle dependencies for dynamically registered beans.
  • Refresh Lifecycle: In most cases, you will need to call refresh() on the ApplicationContext after registering a new bean to allow Spring to process it.

By using these approaches, you can register beans into the Spring context programmatically at runtime.


Maven Dependencies

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>6.2.6</version>
</dependency>

Maven Central

Creating a Simple CRUD API with Spring Boot and In-Memory Database

Below is a step-by-step guide to create a simple CRUD API using Spring Boot and an in-memory database like H2. We will use Maven to manage the project’s dependencies.

1. Set Up Your Maven Project

Create a new Maven project or directory for the Spring Boot application.

pom.xml

Here is the file with the necessary dependencies: pom.xml

<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/maven-v4_0_0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.example</groupId>
    <artifactId>crud-api</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>jar</packaging>
    <name>Spring Boot CRUD API</name>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.5.3</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <properties>
        <java.version>21</java.version>
    </properties>

    <dependencies>
        <!-- Spring Boot Starter Web -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!-- Spring Boot Starter JPA -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>

        <!-- H2 Database (In-memory database) -->
        <dependency>
            <groupId>com.h2database</groupId>
            <artifactId>h2</artifactId>
            <scope>runtime</scope>
        </dependency>

        <!-- Lombok for simplicity -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <scope>provided</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <!-- Maven Spring Boot plugin -->
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

2. Create the Application Entry Point

src/main/java/org/kodejava/crudapi/CrudApiApplication.java

Create the main class for your Spring Boot application.

package org.kodejava.crudapi;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class CrudApiApplication {

    public static void main(String[] args) {
        SpringApplication.run(CrudApiApplication.class, args);
    }
}

3. Define the Entity

Create a User entity to model your data.

src/main/java/org/kodejava/crudapi/model/User.java

package org.kodejava.crudapi.model;

import jakarta.persistence.*;

import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@NoArgsConstructor
@Entity
@Table(name = "users")
public class User {

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

    private String name;

    private String email;
}

4. Create the Repository

The repository will handle database operations.

src/main/java/org/kodejava/crudapi/repository/UserRepository.java

package org.kodejava.crudapi.repository;

import com.example.crudapi.model.User;
import org.springframework.data.jpa.repository.JpaRepository;

public interface UserRepository extends JpaRepository<User, Long> {
}

5. Define the Service

The service layer will handle business logic.

src/main/java/org/kodejava/crudapi/service/UserService.java

package org.kodejava.crudapi.service;

import com.example.crudapi.model.User;
import com.example.crudapi.repository.UserRepository;
import org.springframework.stereotype.Service;

import java.util.List;

@Service
public class UserService {

    private final UserRepository userRepository;

    public UserService(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    public List<User> getAllUsers() {
        return userRepository.findAll();
    }

    public User getUserById(Long id) {
        return userRepository.findById(id).orElseThrow(() -> new RuntimeException("User not found"));
    }

    public User createUser(User user) {
        return userRepository.save(user);
    }

    public User updateUser(Long id, User user) {
        User existingUser = getUserById(id);
        existingUser.setName(user.getName());
        existingUser.setEmail(user.getEmail());
        return userRepository.save(existingUser);
    }

    public void deleteUser(Long id) {
        userRepository.deleteById(id);
    }
}

6. Create the Controller

The controller defines API endpoints for CRUD operations.

src/main/java/org/kodejava/crudapi/controller/UserController.java

package org.kodejava.crudapi.controller;

import com.example.crudapi.model.User;
import com.example.crudapi.service.UserService;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.util.List;

@RestController
@RequestMapping("/api/users")
public class UserController {

    private final UserService userService;

    public UserController(UserService userService) {
        this.userService = userService;
    }

    @GetMapping
    public ResponseEntity<List<User>> getAllUsers() {
        return ResponseEntity.ok(userService.getAllUsers());
    }

    @GetMapping("/{id}")
    public ResponseEntity<User> getUserById(@PathVariable Long id) {
        return ResponseEntity.ok(userService.getUserById(id));
    }

    @PostMapping
    public ResponseEntity<User> createUser(@RequestBody User user) {
        return ResponseEntity.status(HttpStatus.CREATED).body(userService.createUser(user));
    }

    @PutMapping("/{id}")
    public ResponseEntity<User> updateUser(@PathVariable Long id, @RequestBody User user) {
        return ResponseEntity.ok(userService.updateUser(id, user));
    }

    @DeleteMapping("/{id}")
    public ResponseEntity<Void> deleteUser(@PathVariable Long id) {
        userService.deleteUser(id);
        return ResponseEntity.noContent().build();
    }
}

7. Configure Application Properties

Configure the H2 database in . application.properties

src/main/resources/application.properties

# H2 In-Memory Database Settings
spring.datasource.url=jdbc:h2:mem:testdb
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=password
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
spring.h2.console.enabled=true
spring.h2.console.path=/h2-console

8. Test the Application

Run your Spring Boot application and test the REST APIs using tools like Postman or curl.

  • To get all users:
  GET http://localhost:8080/api/users
  • To get a user by ID:
  GET http://localhost:8080/api/users/{id}
  • To create a user:
  POST http://localhost:8080/api/users
  Content-Type: application/json
  {
      "name": "John Doe",
      "email": "[email protected]"
  }
  • To update a user:
  PUT http://localhost:8080/api/users/{id}
  Content-Type: application/json
  {
      "name": "Jane Doe",
      "email": "[email protected]"
  }
  • To delete a user:
  DELETE http://localhost:8080/api/users/{id}

9. Access the H2 Console

You can access the H2 database console at http://localhost:8080/h2-console.

  • JDBC URL: jdbc:h2:mem:testdb
  • Username: sa
  • Password: password

That’s it! You now have a fully functional CRUD API with Spring Boot and an in-memory H2 database.