4.1 Introduction to Databases (MySQL / PostgreSQL / H2)
So far, any data our application has handled is ephemeral—it disappears the moment we stop the application. To store data permanently, we need a database. We'll be using a relational database, where data is organized into tables (like spreadsheets) with rows and columns.
- MySQL / PostgreSQL: These are powerful, industry-standard, open-source databases perfect for production applications.
- H2 Database: This is a lightweight, in-memory database written in Java. It's incredibly convenient for development because it requires no separate installation and can be configured to start and stop with your Spring Boot application. To keep things simple and focused, we will use the H2 database for this course.
4.2 Connecting Spring Boot with a Database (JPA + Hibernate)
How do we get our Java objects (like an Account object) into a database table? The old way involved writing a lot of manual, error-prone SQL code using a technology called JDBC. This created a problem known as the Object-Relational Impedance Mismatch—the world of objects and the world of relational tables don't naturally fit together.
The modern solution is ORM (Object-Relational Mapping).
JPA (Java Persistence API)
Analogy: A Car's Dashboard. JPA is a specification, not a product. It's a standard set of rules and interfaces, like the layout of a car's dashboard. Every car dashboard has a speedometer, a fuel gauge, and a steering wheel. JPA defines that we need ways to `save()`, `find()`, and `delete()` objects, but it doesn't actually do the work itself.
Hibernate
Analogy: The Engine. Hibernate is the most popular implementation of the JPA specification. It's the powerful engine under the hood that reads the JPA "dashboard" and performs the actual work of converting your Java objects into SQL queries and saving them to the database.
Spring Data JPA
Analogy: Automatic Transmission. Spring Data JPA makes using JPA and Hibernate incredibly easy. It's a layer on top that provides "repository" interfaces. By simply creating an interface, Spring Data JPA automatically provides a complete implementation for all standard CRUD (Create, Read, Update, Delete) operations. You get all the power without having to write the boilerplate code.
4.3 Creating Account and Customer Entities
An Entity is a simple Java class (a POJO) that is mapped to a database table. We use special JPA annotations to tell Hibernate how to perform this mapping.
Let's create our first entity, `Account.java`.
import jakarta.persistence.*;
@Entity // 1. Marks this class as a table in the database
@Table(name = "accounts") // 2. (Optional) Specifies the table name
public class Account {
@Id // 3. Marks this field as the primary key
@GeneratedValue(strategy = GenerationType.IDENTITY) // 4. Auto-increments the ID
private Long id;
@Column(name = "account_holder_name", nullable = false) // 5. Maps to a column, cannot be null
private String accountHolderName;
@Column(nullable = false)
private double balance;
// Constructors, Getters, and Setters are required by JPA.
// They are usually generated by the IDE.
}
@Entity: The most important annotation. It tells Spring Data JPA that this class represents a table.@Table: By default, the table name would be `account`. We can use this to explicitly name it `accounts`.@Id: Every database table needs a unique primary key to identify each row.@GeneratedValue: We tell the database to manage the ID for us (e.g., start at 1 and increment for each new row).@Column: Allows us to customize the column mapping, such as the name or whether it can be null.
4.4 Repository Layer with Spring Data JPA
This is where the magic happens. We don't write a class; we write an interface. Spring Data JPA will automatically create an implementation for us at runtime.
Create a new interface called AccountRepository.java.
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface AccountRepository extends JpaRepository<Account, Long> {
// That's it!
}
JpaRepository<Account, Long>, we instantly get a full suite of methods like save(), findById(), findAll(), and deleteById() without writing a single line of implementation code! The `Account` is the entity type, and `Long` is the type of its primary key.
4.5 Writing CRUD Operations
Now, let's wire everything together to create an API endpoint that can save a new account to our database.
Step 1: Add Dependencies and Configuration
In your pom.xml, make sure you have these dependencies (Spring Initializr should have added them if you selected "Spring Data JPA" and "H2 Database"):
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
In src/main/resources/application.properties, add these lines to configure our H2 database:
# H2 Database Settings
spring.datasource.url=jdbc:h2:mem:bankdb
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=password
# Allow access to H2 console in the browser
spring.h2.console.enabled=true
# JPA/Hibernate Settings
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
# This will automatically create/update tables based on our @Entity classes
spring.jpa.hibernate.ddl-auto=update
Step 2: Create the Service and Controller
We'll now create the service to handle the logic and the controller to expose it via a REST API.
// --- AccountService.java ---
@Service // Marks this as a service component
public class AccountService {
@Autowired // Spring automatically injects the AccountRepository instance
private AccountRepository accountRepository;
public Account createAccount(Account account) {
return accountRepository.save(account);
}
}
// --- AccountController.java ---
@RestController
@RequestMapping("/api/accounts") // Base URL for all endpoints in this controller
public class AccountController {
@Autowired
private AccountService accountService;
// Create a new account
@PostMapping
public Account createAccount(@RequestBody Account account) {
return accountService.createAccount(account);
}
}
Now, when you run your application and send a POST request to http://localhost:8080/api/accounts with a JSON body like { "accountHolderName": "Jane Doe", "balance": 500.0 }, Spring Boot will:
- Map the incoming JSON to an
Accountobject. - The
Controllerwill pass this object to theService. - The
Servicewill call theRepository'ssave()method. - Spring Data JPA & Hibernate will generate an `INSERT INTO accounts (...)` SQL statement and execute it against the H2 database.
- The newly saved account (now with an ID) is returned as a JSON response!