Welcome to the first lesson of the course, "Persisting Data with Spring Data JPA." Throughout this course, you'll learn how to leverage Spring Data JPA to connect your application to relational databases, create simple and complex relations, implement pagination and sorting, and more. In this introductory lesson, we’ll explore the low-level way of working with relational databases using JDBC and contrast it with the high-level, more efficient way using JPA. We'll also introduce Spring Data JPA, a Spring module that simplifies database operations by building on top of JPA. Let's get started!
Java Database Connectivity (JDBC) is a Java Standard Edition (SE) API that allows Java applications to interact with relational databases. It involves writing explicit SQL queries, managing database connections, and handling result sets manually. Here is an example of how to retrieve data using JDBC:
Java1import java.sql.Connection; 2import java.sql.DriverManager; 3import java.sql.ResultSet; 4import java.sql.Statement; 5 6public class JdbcExample { 7 public static void main(String[] args) { 8 String url = "jdbc:mysql://localhost:3306/mydb"; 9 String username = "root"; 10 String password = "password"; 11 12 try (Connection conn = DriverManager.getConnection(url, username, password); 13 Statement stmt = conn.createStatement(); 14 ResultSet rs = stmt.executeQuery("SELECT * FROM my_table")) { 15 16 while (rs.next()) { 17 System.out.println(rs.getString("column1")); 18 } 19 } catch (Exception e) { 20 e.printStackTrace(); 21 } 22 } 23}
As you can see, this approach is cumbersome with a lot of boilerplate code. This method has nothing to do with Spring Boot and reflects the complexities involved in manual database operations.
If you want to dive deeper into JDBC, refer to the official specification.
The Java Persistence API (JPA) is a Java Enterprise Edition (EE) specification for Object Relational Mapping (ORM), which simplifies database interactions by mapping Java objects to database tables. ORM allows you to manipulate database records through Java objects, making your code cleaner and more maintainable. Here’s an example of a JPA entity class:
Java1import jakarta.persistence.Entity; 2import jakarta.persistence.GeneratedValue; 3import jakarta.persistence.GenerationType; 4import jakarta.persistence.Id; 5 6@Entity 7public class Person { 8 @Id 9 @GeneratedValue(strategy = GenerationType.IDENTITY) 10 private Long id; 11 private String name; 12 private int age; 13 14 // Getters and Setters 15}
In this example:
@Entity
: Marks this class as a JPA entity. This annotation will map the class to a database table namedperson
(default naming convention).@Id
: Specifies the primary key of the entity. This annotation maps theid
field to the primary key column of theperson
table.@GeneratedValue(strategy = GenerationType.IDENTITY)
: Defines how the primary key is generated. UsingIDENTITY
strategy, theid
field will be auto-incremented by the database.
So, this class will be mapped to a table named person
, which will have columns id
, name
, and age
, with id
being the primary key.
For more information on JPA, consult the official specification.
Spring Data JPA is a Spring module that builds on top of JPA and provides an abstraction layer to further reduce the boilerplate code required for database operations. It integrates closely with the Spring framework, offering additional features such as derived queries, pagination, and auditing. This module simplifies data access in Spring applications and makes writing repository interfaces straightforward and less time-consuming.
Here are the essential Gradle dependencies you need to add to your project:
Groovy1dependencies { 2 implementation 'org.springframework.boot:spring-boot-starter-data-jpa' 3 implementation 'com.h2database:h2' // H2 database dependency 4}
Explanation:
spring-boot-starter-data-jpa
: This dependency includes Spring Data JPA and Hibernate as the default JPA provider.com.h2database:h2
: This is the JDBC driver for the H2 database. Note that the database driver is not included inspring-boot-starter-data-jpa
, so you need to specify the appropriate driver for your database.
The H2 database is an open-source in-memory database which is particularly useful during development for its speed and simplicity. For more information, visit their official website.
Hibernate is the most commonly used JPA provider, but there are alternatives such as EclipseLink and OpenJPA. If you prefer to use a different JPA provider, you can exclude Hibernate and include your preferred provider in the dependencies:
Groovy1dependencies { 2 implementation 'org.springframework.boot:spring-boot-starter-data-jpa' { 3 exclude group: 'org.hibernate', module: 'hibernate-core' 4 } 5 implementation 'com.h2database:h2' 6 implementation 'org.eclipse.persistence:eclipselink:2.7.7' // EclipseLink JPA provider 7}
To explore more about Spring Data JPA, check out the official documentation.
Here is how you can define an entity class using Spring Data JPA:
Java1package com.codesignal.entities; 2 3import jakarta.persistence.Entity; 4import jakarta.persistence.GeneratedValue; 5import jakarta.persistence.GenerationType; 6import jakarta.persistence.Id; 7 8@Entity 9public class TodoItem { 10 11 @Id 12 @GeneratedValue(strategy = GenerationType.IDENTITY) 13 private Long id; 14 private String title; 15 private boolean completed; 16 17 // Getters and Setters 18}
As Spring Data JPA is an extension of JPA, the entity class will look exactly the same as if we were using basic JPA. In this class:
@Entity
marks it as a JPA entity.@Id
specifies the primary key.@GeneratedValue(strategy = GenerationType.IDENTITY)
defines how the primary key is generated.
To interact with the TodoItem
entity, you can define a repository interface:
Java1package com.codesignal.repositories; 2 3import com.codesignal.entities.TodoItem; 4import org.springframework.data.jpa.repository.JpaRepository; 5 6public interface TodoRepository extends JpaRepository<TodoItem, Long> { 7}
This interface extends JpaRepository<TodoItem, Long>
(where Long
is the type of the primary key of TodoItem
), which provides CRUD operations and other methods out of the box. Spring Data JPA generates the actual implementation at runtime. When your Spring Boot application starts, it will create an instance of this interface, which you can then inject into your controllers or other beans using @Autowired
or constructor injection.
Let's implement a simple CRUD controller to manage TodoItem
entities:
Java1package com.codesignal.controllers; 2 3import com.codesignal.entities.TodoItem; 4import com.codesignal.repositories.TodoRepository; 5import org.springframework.beans.factory.annotation.Autowired; 6import org.springframework.web.bind.annotation.*; 7 8import java.util.List; 9import java.util.Optional; 10 11@RestController 12@RequestMapping("/todos") 13public class TodoController { 14 15 @Autowired 16 private TodoRepository todoRepository; 17 18 @GetMapping 19 public List<TodoItem> getAllTodos() { 20 return todoRepository.findAll(); 21 } 22 23 @PostMapping 24 public TodoItem createTodoItem(@RequestBody TodoItem todoItem) { 25 return todoRepository.save(todoItem); 26 } 27 28 @PutMapping("/{id}") 29 public TodoItem updateTodoItem(@PathVariable Long id, @RequestBody TodoItem todoItem) { 30 todoItem.setId(id); 31 return todoRepository.save(todoItem); 32 } 33 34 @GetMapping("/{id}") 35 public Optional<TodoItem> getTodoItemById(@PathVariable Long id) { 36 return todoRepository.findById(id); 37 } 38 39 @DeleteMapping("/{id}") 40 public void deleteTodoItem(@PathVariable Long id) { 41 todoRepository.deleteById(id); 42 } 43}
In this controller:
TodoRepository#findAll()
: Retrieves allTodoItem
entities.TodoRepository#save(TodoItem todoItem)
: Saves a new or updatedTodoItem
entity.TodoRepository#findById(Long id)
: Finds aTodoItem
entity by its ID and returns anOptional<TodoItem>
.TodoRepository#deleteById(Long id)
: Deletes aTodoItem
entity by its ID.
Hibernate, the most commonly used JPA provider, allows you to enable SQL logging to see the actual SQL queries it generates. You can enable this feature in your application.properties
file:
.properties1spring.jpa.show-sql=true # Enables logging of the generated SQL queries from JPA/Hibernate to the console. 2spring.jpa.properties.hibernate.format_sql=true # Formats the logged SQL queries, making them more readable.
This setting helps you understand and debug the SQL operations that JPA performs behind the scenes.
In this lesson, we introduced you to JDBC, JPA, and Spring Data JPA, explaining their purposes and differences. We demonstrated how to create a JPA entity class and a Spring Data JPA repository interface, and how to implement a CRUD controller to manage the entity. Lastly, we showed how to enable SQL logging to see the actual SQL queries. In the next practice, you'll get hands-on experience with these concepts.