chapter_1_persistence
chapter_1_persistence
1 Introduction
The persistence layer acts as an intermediary between the application’s busi-
ness logic and the data storage systems. Its main roles include:
In the next sections, we are going to walk trough the different approaches
to set the persistence layer up for a multi-layered application.
1
2 JDBC classic approach
2.0.1 JDBC API
• JDBC (Java Database Connectivity) is an API (Application Program-
ming Interface) that enables Java applications to interact with relational
databases. It provides a standard set of interfaces and classes to connect
to databases, execute SQL queries, and manage the results.
2
Figure 3: Database Connection Setup
3
Key Components:
• Data Model: A class that defines the common behavior for all concrete
data model classes, representing the structure of the data that will be
managed by the application.
Explanation:
4
• DriverManager.getConnection: Establishes a connection to the database
using the specified URL, username, and password.
• Constants like URL, USER, and PASSWORD store the configuration details
securely and make them easily updatable.
5
Explanation:
• Fields like id, name, and email map directly to the columns of the database
table.
• Getter and setter methods allow encapsulation, enabling controlled access
to these fields.
Explanation:
• createUser(User user): Adds a new user record to the database.
• getUserById(int id): Fetches a user record based on its ID.
6
6
7 @Override
8 public void createUser ( User user ) {
9 String sql = " INSERT INTO users ( name , email
) VALUES (? , ?) " ;
10 try ( Connection conn = getConnection () ;
11 Pre paredS tateme nt stmt = conn .
prepareStatement ( sql ) ) {
12 stmt . setString (1 , user . getName () ) ;
13 stmt . setString (2 , user . getEmail () ) ;
14 stmt . executeUpdate () ;
15 } catch ( SQLException e ) {
16 e . printStackTrace () ;
17 }
18 }
19
20 @Override
21 public User getUserById ( int id ) {
22 String sql = " SELECT * FROM users WHERE id =
?";
23 User user = null ;
24 try ( Connection conn = getConnection () ;
25 Pre paredS tateme nt stmt = conn .
prepareStatement ( sql ) ) {
26 stmt . setInt (1 , id ) ;
27 ResultSet rs = stmt . executeQuery () ;
28 if ( rs . next () ) {
29 user = new User () ;
30 user . setId ( rs . getInt ( " id " ) ) ;
31 user . setName ( rs . getString ( " name " ) ) ;
32 user . setEmail ( rs . getString ( " email " ) )
;
33 }
34 } catch ( SQLException e ) {
35 e . printStackTrace () ;
36 }
37 return user ;
38 }
39
40 @Override
41 public List < User > getAllUsers () {
42 String sql = " SELECT * FROM users " ;
43 List < User > users = new ArrayList < >() ;
44 try ( Connection conn = getConnection () ;
45 Statement stmt = conn . createStatement ()
;
7
46 ResultSet rs = stmt . executeQuery ( sql ) )
{
47 while ( rs . next () ) {
48 User user = new User () ;
49 user . setId ( rs . getInt ( " id " ) ) ;
50 user . setName ( rs . getString ( " name " ) ) ;
51 user . setEmail ( rs . getString ( " email " ) )
;
52 users . add ( user ) ;
53 }
54 } catch ( SQLException e ) {
55 e . printStackTrace () ;
56 }
57 return users ;
58 }
59
60 @Override
61 public void updateUser ( User user ) {
62 String sql = " UPDATE users SET name = ? ,
email = ? WHERE id = ? " ;
63 try ( Connection conn = getConnection () ;
64 Pre paredS tateme nt stmt = conn .
prepareStatement ( sql ) ) {
65 stmt . setString (1 , user . getName () ) ;
66 stmt . setString (2 , user . getEmail () ) ;
67 stmt . setInt (3 , user . getId () ) ;
68 stmt . executeUpdate () ;
69 } catch ( SQLException e ) {
70 e . printStackTrace () ;
71 }
72 }
73
74 @Override
75 public void deleteUser ( int id ) {
76 String sql = " DELETE FROM users WHERE id = ?
";
77 try ( Connection conn = getConnection () ;
78 Pre paredS tateme nt stmt = conn .
prepareStatement ( sql ) ) {
79 stmt . setInt (1 , id ) ;
80 stmt . executeUpdate () ;
81 } catch ( SQLException e ) {
82 e . printStackTrace () ;
83 }
84 }
85 }
8
Listing 4: DAO Implementation for User Entity
Explanation:
• PreparedStatement: Used to safely execute parameterized queries, pre-
venting SQL injection attacks.
• ResultSet: Stores the result of SELECT queries, allowing iteration through
the returned rows.
• Each method utilizes a try-with-resources block to ensure that connec-
tions, statements, and other resources are closed automatically.
9
automatically generates a DataSource object, which is then injected into the
JdbcTemplate object to enable database operations.
1 # Database Configuration
2 spring . datasource . url = jdbc : mysql : // localhost :3306/
mydatabase
3 spring . datasource . username = myuser
4 spring . datasource . password = mypassword
5 spring . datasource . driver - class - name = com . mysql . cj .
jdbc . Driver
Setting up the persistence layer in this context involves several key steps:
1. Configuring JdbcTemplate as a Bean: In a @Configuration class,
define JdbcTemplate as a Spring bean. This allows the JdbcTemplate
instance to be injected as a dependency into the DAO classes, enabling
them to perform database operations.
2. Creating Data Models: Define data model classes that represent the en-
tities in the database. These classes will be used to map database records
to Java objects.
10
3. Defining the CRUD Interface: Create an interface that specifies the
CRUD (Create, Read, Update, Delete) operations. This interface will be
implemented by the DAOs to handle data access in a consistent way.
4. Implementing DAOs: Develop DAO classes that provide concrete im-
plementations of the CRUD operations defined in the interface. These
classes will use JdbcTemplate to interact with the database.
1 @Configuration
2 public class DatabaseConfig {
3 @Bean
4 public JdbcTemplate jdbcTemplate ( DataSource
dataSource ) {
5 return new JdbcTemplate ( dataSource ) ;
6 }
7 }
11
Listing 6: Database Configuration in Java
Explanation:
• JdbcTemplate: A Spring-provided utility for interacting with the database
in a straightforward and efficient way.
• DataSource: Injected into the JdbcTemplate, it provides the connection
to the database.
1 @Data
2 @ A ll A r gs C o ns t r uc t o r
3 @ NoA rg sC on st ru ct or
4 public class Product {
5 private Long id ;
6 private String label ;
7 private Double price ;
8 }
Explanation:
• The @Data, @AllArgsConstructor, and @NoArgsConstructor annota-
tions from Lombok automatically generate boilerplate code like getters,
setters, and constructors.
• Fields like id, label, and price map directly to the database table’s
columns.
12
Listing 8: CRUD Interface for Product Entity
Explanation:
• Methods like save, update, and deleteById handle the persistence logic
for the Product entity.
• findById and findAll allow retrieval of one or multiple products, respec-
tively.
1 @Repository
2 public class ProductDaoImpl implements ProductDao {
3 private final JdbcTemplate jdbcTemplate ;
4
17 @Override
18 public int save ( Product product ) {
19 KeyHolder keyHolder = new G ene ra te dK ey Ho ld er
() ;
20 String sql = " INSERT INTO product ( label ,
price ) VALUES (? , ?) " ;
21 jdbcTemplate . update ( connection -> {
22 Prep aredSt atemen t ps = connection .
prepareStatement ( sql , new String []{ "
id " }) ;
23 ps . setString (1 , product . getLabel () ) ;
24 ps . setDouble (2 , product . getPrice () ) ;
25 return ps ;
13
26 } , keyHolder ) ;
27
35 @Override
36 public int update ( Product product ) {
37 return jdbcTemplate . update (
38 " UPDATE product SET label = ? , price
= ? WHERE id = ? " ,
39 product . getLabel () , product . getPrice
() , product . getId ()
40 );
41 }
42
43 @Override
44 public int deleteById ( Long id ) {
45 return jdbcTemplate . update ( " DELETE FROM
product WHERE id = ? " , id ) ;
46 }
47
48 @Override
49 public Optional < Product > findById ( Long id ) {
50 try {
51 Product product = jdbcTemplate .
queryForObject (
52 " SELECT * FROM product WHERE id
= ?",
53 productRowMapper ,
54 id
55 );
56 return Optional . ofNullable ( product ) ;
57 } catch ( E m p t y R e s u l t D a t a A c c e s s E x c e p t i o n e ) {
58 return Optional . empty () ;
59 }
60 }
61
62 @Override
63 public List < Product > findAll () {
64 return jdbcTemplate . query ( " SELECT * FROM
product " , productRowMapper ) ;
14
65 }
66 }
Explanation:
• RowMapper: Maps each row of the result set to a Product object.
15
4.2 Setup for the Connection
To use JPA and Hibernate in a project, certain configurations are required
to establish a connection to the database and manage entities. In a Spring
Boot application, the connection and JPA settings can be configured in the
application.properties file.
Steps for Setup:
1. Add dependencies for JPA and Hibernate in the pom.xml file (Maven) or
build.gradle file (Gradle).
2. Create a configuration file (application.properties) to define database
properties such as URL, username, password, and Hibernate-specific set-
tings.
3. Annotate Java classes with JPA annotations (@Entity, @Table, etc.) to
define them as entities mapped to database tables.
This configuration ensures that Spring Boot uses Hibernate as the JPA
provider and establishes the connection to the MySQL database with the nec-
essary credentials and settings.
16
• Validating and transforming data before persistence.
• Coordinating between the persistence layer and other layers of the appli-
cation (e.g., controllers).
• Managing database transactions, ensuring data consistency.
7 @Entity
8 public class Product {
9
10 @Id
11 @GeneratedValue ( strategy = GenerationType .
IDENTITY )
12 private Long id ;
13
17
19
In this example, the Product class represents an entity that will be mapped
to a table named product. The id field is the primary key, and it is automati-
cally generated using the IDENTITY strategy.
Step 2: Repository Interface
To interact with the database, Spring Data JPA provides the JpaRepository
interface, which provides built-in CRUD operations. We create an interface that
extends JpaRepository.
6 @Service
7 public class ProductService {
8
9 @Autowired
10 private P roduct Reposi tory p roduct Reposi tory ;
11
18
16 public Optional < Product > getProductById ( Long id )
{
17 return pro ductRe posito ry . findById ( id ) ;
18 }
19
7 @RestController
8 @RequestMapping ( " / products " )
9 public class Pr oductC ontrol ler {
10
11 @Autowired
12 private ProductService productService ;
13
14 @PostMapping
15 public Product createProduct ( @RequestBody
Product product ) {
16 return productService . saveProduct ( product ) ;
17 }
18
19
22 }
23
24 @GetMapping
25 public List < Product > getAllProducts () {
26 return productService . getAllProducts () ;
27 }
28
5 Conclusion
In this chapter, we explored different approaches for managing the persistence
layer in a Spring Boot application. We covered the implementation of persis-
tence with JDBC, detailing the database configuration, data models, and CRUD
interfaces, as well as using JdbcTemplate to interact with the database. In par-
allel, we also discussed setting up persistence with JPA and Hibernate, configur-
ing the database connection, and defining entities using JPA annotations. These
two approaches offer different advantages depending on the project’s needs:
JDBC for more manual and controlled operations, and JPA/Hibernate for au-
tomated management and simplified object-relational mapping. By combining
these techniques, we can build a robust, flexible, and maintainable persistence
layer.
20