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:
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:
spring.jpa.hibernate.ddl-auto=create-drop
: Creates the database schema at startup and drops it at shutdown. Ideal for testing.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.spring.jpa.properties.hibernate.jdbc.lob.non_contextual_creation=true
: Enables non-contextual LOB creation for compatibility with certain databases.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