Spring Boot CRUD with PostgreSQL

This article will guide you through a basic example of a Spring Boot 3 application which exposes CRUD Service method via a REST Endpoint. We will use PostgreSQL as datatabase, although you can easily switch to another RDBMs with some simple changes in the configuration.

Step 1: Set up your Project

Firstly, set up your project including the Spring Data JPA starter, the Web Starter and the PostgreSQL Driver:

Spring Boot CRUD with PostgreSQL

Step 2: Create your Model

Next step will be creating a simple Model for our application. To keep it simple, we will just be adding a Customer Class:

@Entity
public class Customer {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Schema(accessMode = Schema.AccessMode.READ_ONLY)
    private long id;

    private String name;

    private String surname;

    private String email;
    public Customer() {}
    public Customer(String name, String email) {
        this.name = name;
        this.email = email;
    }
    // getters/setters omitted for brevity
}

Please note that this Class will be using a GenerationType.IDENTITY which relies on the auto-increment database column available on PostgreSQL. Also, we are adding the @Schema(accessMode = Schema.AccessMode.READ_ONLY) annotation that would prevent including the id when testing the application with Swagger.

Step 3: Define the Repository Interface

The Repository Interface extends the org.springframework.data.jpa.repository.JpaRepository. This interface provides out-of-the-box standard methods for performing CRUD operations. Therefore, you can just use the following bare-minimum definition:

@Repository
public interface CustomerRepository extends JpaRepository<Customer, Long> {
}

In this tutorial, we will show a more advanced example which shows how to add extra methods without actually adding an implementation:

@Repository
public interface CustomerRepository extends JpaRepository<Customer, Long> {
    List<Customer> findByName(String name);

    // Find by name and surname (like in SQL: WHERE name = ? AND surname = ?)
    List<Customer> findByNameAndSurname(String name, String surname);

    // Find by name containing a specific string (like in SQL: WHERE name LIKE ?)
    List<Customer> findByNameContaining(String name);

    // Find by email address (like in SQL: WHERE email = ?)
    List<Customer> findByEmail(String email);

    // Find by email address containing a specific string (like in SQL: WHERE email LIKE ?)
    List<Customer> findByEmailContaining(String email);
}

As you can see, the findBy constructs will be transformed into a WHERE clause. Also the Containing construct will be a SQL LIKE statement.

Step 4: Add the Controller Class

To expose our Repository methods to the external Clients, we will add a Spring Boot Controller which maps all the Repository methods with the corresponding HTTP methods:

@RestController
public class DemoController {
    @Autowired
    CustomerRepository repository;
    @GetMapping("/list")
    public Iterable<Customer> findAll() {
        return repository.findAll();
    }
    @GetMapping("/byId")
    public Optional<Customer> findOne(@RequestParam Long id) {
        return repository.findById(id);
    }

    // Find by name (inherited from JpaRepository)
    @GetMapping("/byName")
    public List<Customer> findByName(@RequestParam String name) {
        return repository.findByName(name);
    }

    // Find by name and surname
    @GetMapping("/byNameAndSurname")
    public List<Customer> findByNameAndSurname(@RequestParam String name, @RequestParam String surname) {
        return repository.findByNameAndSurname(name, surname);
    }

    // Find by name containing a string
    @GetMapping("/byNameContaining")
    public List<Customer> findByNameContaining(@RequestParam String name) {
        return repository.findByNameContaining(name);
    }

    // Find by email address
    @GetMapping("/byEmail")
    public List<Customer> findByEmail(@RequestParam String email) {
        return repository.findByEmail(email);
    }

    // Find by email containing a string
    @GetMapping("/byEmailContaining")
    public List<Customer> findByEmailContaining(@RequestParam String email) {
        return repository.findByEmailContaining(email);
    }

    @PostMapping(value = "/add", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
    public ResponseEntity<Customer> create(@RequestBody Customer customer) {
        repository.save(customer);
        return new ResponseEntity<>(customer, HttpStatus.CREATED);
    }

}

As you can see, the Spring Boot Controller contains both default JpaRepository methods (for example findByName) and the custom ones.

Step 5: Add an Application Class

The last Class we will add to our example is the Spring Boot Application Class which will boot our application. There’s nothing special about it:

@SpringBootApplication
public class DemoApplication {

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

}

Step 6: Include the Configuration

To complete our CRUD REST example, we need to add a configuration which is specific to create a PostgreSQL Datasource. Also, we will add a resources/data.sql script to insert some initial data.

Firstly, let’s add the following application.properties file in the resources folder:

spring.application.name=demo

spring.jpa.hibernate.ddl-auto=create-drop
spring.jpa.defer-datasource-initialization=true
spring.jpa.properties.hibernate.jdbc.lob.non_contextual_creation=true
spring.sql.init.mode=always


spring.datasource.url= jdbc:postgresql://localhost:5432/testdb
spring.datasource.username=postgres
spring.datasource.password=postgres
spring.jpa.database=POSTGRESQL

Here’s a quick explanation of these properties:

  1. spring.jpa.hibernate.ddl-auto=create-drop: Creates the database schema at startup and drops it at shutdown. Ideal for testing.
  2. spring.jpa.defer-datasource-initialization=true: Delays data source initialization until after entity manager factories are set up, ensuring the schema is ready before data insertion.
  3. spring.jpa.properties.hibernate.jdbc.lob.non_contextual_creation=true: Enables non-contextual LOB creation for compatibility with certain databases.
  4. spring.sql.init.mode=always: Always runs SQL scripts to initialize the database, ensuring the schema and data are set up correctly

Finally, add in the data.sql some INSERT statements so that you test your CRUD application right away:

INSERT INTO customer (name, surname, email) VALUES
  ('John Doe', 'Manager', 'john.doe@example.com');

INSERT INTO customer (name, surname, email) VALUES
  ('Jane Smith', 'Developer', 'jane.smith@example.com');

INSERT INTO customer (name, surname, email) VALUES
  ('Alice Johnson', 'Tester', 'alice.johnson@example.com');

INSERT INTO customer (name, surname, email) VALUES
  ('Bob Williams', 'Sales', 'bob.williams@example.com');

INSERT INTO customer (name, surname, email) VALUES
  ('Emily Garcia', 'Marketing', 'emily.garcia@example.com');

Step 7: Start PostgreSQL

To start PostgreSQL we recommend using Docker or Docker Compose. With Docker Compose you can easily extend the PostgreSQL with additional configuration elements and bind the DB Service with other Docker Services.

Here is a minimal docker-compose.yml file which uses the POSTGRES environment variables matching our Datasource definition:

version: "3.9"
services:
  db:
    image: postgres
    restart: always
    environment:
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD: postgres
      POSTGRES_DB: testdb
    ports:
      - "5432:5432"

Once PostgreSQL is available, you can easily test your application using CRUD REST methods. For the sake of simplicity, you can add the following dependency which will make available Swagger UI in your project:

<dependency>
	<groupId>org.springdoc</groupId>
	<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
	<version>2.0.2</version>
</dependency>

More about using Swagger UI in Spring Boot applications here: Swagger UI tutorial for Spring Boot users

Conclusion

This article was a walktrough the definition of a simple Spring Boot 3 application which uses a Controller to expose JPA Data CRUD methods usingh PostgreSQL Database

Found the article helpful? if so please follow us on Socials
Twitter Icon       Facebook Icon       LinkedIn Icon       Mastodon Icon