Today we'll get into working with databases, moving on to the next major Spring module — Spring Data JPA.
Basics of working with data in applications
Data is the heart of any application. Online stores keep orders, online banks keep transactions, social networks keep posts and comments. Without reliable data storage an app is like a May bug in May: it remembers nothing and every restart begins life with a clean slate. That's why databases play a key role: they structure, store, and let you work efficiently with information even when there are millions of records.
ORM and JPA: your bridge between objects and tables
Working with raw SQL is cool — until you have to update 10 thousand lines of code after a table change. That's where ORM — Object Relational Mapping — comes in. It's the tech that converts database tables into Java objects and back.
JPA (Java Persistence API) is the Java standard for working with ORM. But if you thought, "Sweet, now I write less code!", hold on. Without an implementation JPA is just an interface (and not the fun kind). We need a hero. Enter Hibernate.
Spring Data JPA
Spring Data JPA is a magic wand that brings JPA together with Spring's simplicity and automation. Here's why it matters:
- Automation, buddy! You can almost avoid writing SQL by hand. Spring Data JPA generates queries for you.
- Easy to use. Working with data is now as easy as calling
findAll(). - Standard integration. All of this is configured on top of what we've already learned in Spring Boot.
Core concepts
- Entities (Entities): these are your objects that will be mapped to database tables.
- Repositories (Repositories): interfaces that simplify data access. They already know how to do magic like
save(),findById()anddeleteAll(). - EntityManager: the central tool for managing the lifecycle of entities. It actually works behind the scenes, and we won't have to deal with it much personally.
- Transactions: they let you perform DB operations atomically. If something goes wrong, you can roll back all changes.
Structure of Spring Data JPA
Working with Spring Data JPA consists of the following steps:
- Connecting to the database. We put the connection parameters (URL, username, password) in
application.properties. Spring Boot will take care of the rest. - Creating entities (Entities). We use JPA annotations like
@Entityto indicate which Java class corresponds to a database table. - Using repositories (Repositories). Here's where the magic starts. We create interfaces that extend
JpaRepository, and after that you can work with tables like regular objects.
Practice: what does this look like in code?
First you need to connect to a database. But before that... create a Spring Boot project. Yep, that's our routine now.
1. Setting up the connection in application.properties
# Database connection parameters
spring.datasource.url=jdbc:h2:mem:testdb
spring.datasource.username=sa
spring.datasource.password=password
spring.jpa.show-sql=true
spring.jpa.hibernate.ddl-auto=update
We're using the built-in H2 database for practice. It's "in-memory", but that means we don't have to install anything.
2. Creating an entity (Entity)
Here's an example User entity that will map to the users table in the database:
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
@Entity // Indicates that this class maps to a database table
public class User {
@Id // Marks this as the primary key
@GeneratedValue(strategy = GenerationType.IDENTITY) // Automatic ID generation
private Long id;
private String name;
private String email;
// 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 getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
}
The User class will create a users table in the database with columns id, name and email.
3. Creating the repository (Repository)
A repository is an interface that lets us interact with the database without writing SQL queries:
import org.springframework.data.jpa.repository.JpaRepository;
// JpaRepository will automatically add basic methods like save(), findById(), findAll()
public interface UserRepository extends JpaRepository<User, Long> {
}
4. Working with data in a controller
Suppose we have a controller that allows adding and fetching users:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
@RequestMapping("/users")
public class UserController {
@Autowired
private UserRepository userRepository;
@PostMapping
public User createUser(@RequestBody User user) {
return userRepository.save(user); // Save to the database
}
@GetMapping
public List<User> getUsers() {
return userRepository.findAll(); // Return all users
}
}
Now we have a working DB integration. You can send an HTTP POST to /users to create a new user, or GET the same endpoint to fetch all users.
How does this help in real life?
Imagine an online store: you need to store data about products, orders, customers. At each step you'll have to talk to the database. Using Spring Data JPA lets you focus on application logic instead of writing SQL. You save time, avoid boilerplate, and reduce the chance of mistakes.
Common student mistakes
- Missing dependency in
pom.xml. Don't forget to addspring-boot-starter-data-jpa. - Incorrect database configuration. One of the most popular mistakes is not specifying parameters in
application.properties, likespring.datasource.url. - Annotation mistakes. Sometimes people forget the
@Entityon the class or@Idon the field. - Eager loading of data in large tables. We'll talk more about this in one of the next lectures.
Dig into this code, try adding new users and experiment a bit. We're just starting to dive into data work, so relax and enjoy this ORM magic.
GO TO FULL VERSION