In the previous articles…
As we introduced in the first article, Spring is an excellent tool for Java application development. We also explored how to set up a basic project and create an initial, simple API.
In this article, we’ll connect the project to a database, specifically PostgreSQL, and explore how to persist and manipulate saved data.
PostgreSQL
PostgreSQL, originally known as “post-ingres,” was conceived by Michael Stonebraker in 1985. It serves as a robust alternative to other relational databases like MySQL. The key distinction of PostgreSQL lies in its support for defining custom data types (based on standard SQL types) and data type inheritance.
For detailed insights, refer to the official website.
Spring & PostgreSQL
Database Installation
First, ensure PostgreSQL is installed. Follow this guide for installation instructions.
Dependencies
To begin, add the necessary dependencies to your project:
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>42.6.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
With these dependencies, you now have access to all entity manipulation features provided by “spring-boot-starter-data-jpa” and all database communication interfaces provided by “postgresql.”
For IntelliJ users, each time you modify the pom.xml file, click the dedicated icon to apply the changes.
PostgreSQL Configuration
With dependencies in place, you need to set the connection parameters your application will use to interface with the database. Open the “application.properties” file and add the following settings:
spring.datasource.url=jdbc:postgresql://localhost:5432/YOUR_DB_NAME
spring.datasource.username=YOUR_USERNAME
spring.datasource.password=YOUR_PASSWORD
spring.datasource.driver-class-name=org.postgresql.Driver
(The spring.datasource.driver-class-name parameter depends on the installed Postgres version and may vary.)
Before creating domain entities, add the following setting to auto-generate the schema for domain entities:
spring.jpa.hibernate.ddl-auto=update
Domain Entities
Now, let’s create a domain entity.
Within your project, create a new package named domain and add a file called User.java with the following code:
@Entity(name = "users")
public class User {
@Id
@GeneratedValue(strategy= GenerationType.IDENTITY)
private Long id;
private String username;
public User() {}
public User(String username) {
this.username = username;
}
public void setId(Long id) {
this.id = id;
}
public Long getId() {
return id;
}
public void setUsername(String username) {
this.username = username;
}
public String getUsername() {
return username;
}
}
Here are some of the annotations we used:
- @Entity(name=”users”): Tells the database the name of the domain entity. The default value is the lowercase class name, but we customized it here since “User” is a keyword in Postgres.
@Id
: Indicates the primary key field.- @GeneratedValue(strategy=GenerationType.IDENTITY): Specifies that the database should auto-generate the ID field, with Postgres creating a unique sequential identifier of type
long
.
Repository
Repositories are interfaces providing methods for managing (creating, reading, updating, etc.) database records.
In our case, implementations of these interfaces are provided by the “spring-boot-starter-data-jpa” dependency.
Create a package named repository and add the file UserRepository.java with the following content:
@Repository
public interface UserRepository extends JpaRepository<User, String> {}
We’ll leave the interface as is for now, as we’re only using basic CRUD operations. We’ll explore custom functionalities for this interface later.
Controller
Now it’s time to create the controller for managing users. In the main package, create a new file UserController.java and add the following code:
@RestController
@RequestMapping("/user")
public class UserController {
private final UserRepository userRepository;
public UserController(UserRepository userRepository) {
this.userRepository = userRepository;
}
}
This code creates a new API path under “/user.” Next, define the CRUD methods for the User entity by adding the following:
@PostMapping(value = "/register")
public ResponseEntity<User> register(@RequestBody String username) {
User newUser = new User(username);
userRepository.save(newUser);
return ResponseEntity.ok(newUser);
}
@GetMapping(value = "")
public ResponseEntity<List<User>> getAll() {
List<User> users = userRepository.findAll();
return ResponseEntity.ok(users);
}
@GetMapping(value = "/{userId}")
public ResponseEntity<User> getById(@PathVariable String userId) {
User user = userRepository.findById(userId).orElse(null);
if (user == null) {
return ResponseEntity.notFound().build();
}
return ResponseEntity.ok(user);
}
@PutMapping(value = "/{userId}")
public ResponseEntity<User> updateById(@PathVariable String userId, @RequestBody String username) {
User user = userRepository.findById(userId).orElse(null);
if (user == null) {
return ResponseEntity.notFound().build();
}
user.setUsername(username);
user = userRepository.save(user);
return ResponseEntity.ok(user);
}
@DeleteMapping(value = "/{userId}")
public ResponseEntity<User> deleteById(@PathVariable String userId) {
User user = userRepository.findById(userId).orElse(null);
if (user == null) {
return ResponseEntity.notFound().build();
}
userRepository.delete(user);
return ResponseEntity.ok().build();
}
In this example, we introduced a new annotation and a new way to write the API URL. Consider the Get API example:
@GetMapping(value = "/{userId}")
public ResponseEntity<User> getById(@PathVariable String userId) {
User user = userRepository.findById(userId).orElse(null);
if (user == null) {
return ResponseEntity.notFound().build();
}
return ResponseEntity.ok(user);
}
This API’s URL will look like “localhost:8080/user/1” since “/{userId}” tells Spring that the URL will contain a variable component. The @PathVariable annotation maps this variable to the userId string.
Testing
To test the API, use Postman or an equivalent app.
- Get user list:
- Method: GET
- URL: localhost:8080/user
- Response: []
- Create new user:
- Method: POST
- URL: localhost:8080/user/register
- Body: username
- Response:
{
"id": 1,
"username": "user1"
}
- Get user by ID:
- Method: GET
- URL: localhost:8080/user/1
- Update user:
- Method: PUT
- URL: localhost:8080/user/1
- Body: different username
- Delete user:
- Method: DELETE
- URL: localhost:8080/user/1
Conclusion
In this second part, we connected to a database and learned how to persist and manipulate data. In the third and final part, we’ll implement authentication with JWT Token and Spring Security.
The complete code for this example is available at this link.