0% found this document useful (0 votes)
11 views

chapter_1_persistence

Uploaded by

Sami Mlk
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
11 views

chapter_1_persistence

Uploaded by

Sami Mlk
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 20

Persistence Layer

November 21, 2024

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:

• Storing data : It saves data generated by the application so that it can


be retrieved later, even after the application is closed.
• Retrieving data : It fetches stored data when needed, allowing the
application to display or manipulate this information.

Figure 1: Application layers

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.

• Low level API : JDBC is a low-level API, meaning developers manually


manage database connections, handle SQL exceptions, and close resources.

Figure 2: JDBC API

2.1 Setup for the connection


The first step in setting up the persistence layer is to include the JDBC depen-
dency in the application classpath, either manually or by adding the appropriate
Maven dependency tag.
The architecture outlined below represents the foundation for establishing a
connection in the application.

2
Figure 3: Database Connection Setup

• Database Configuration: This is an abstract class that defines the com-


mon behavior for all database configuration classes. The getConnection()
method in this class returns the connection to the specified RDBMS.
• Child Classes: Each child class is responsible for establishing a connec-
tion to a specific RDBMS, inheriting from the base DatabaseConfiguration
class.
• DAO Class: The DAO (Data Access Object) class handles the imple-
mentation of data logic, executing SQL queries, and using the connection
provided by the database configuration class to interact with the database.

2.2 Business Data Logic


The business data logic is built upon two core components.
The architecture shown below outlines the general structure of the persis-
tence layer in a CRUD application.

Figure 4: Persistence Layer Architecture in a CRUD Application

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.

• CRUD Interface: An interface that defines the method signatures for


the core CRUD operations: Create, Read, Update, and Delete.
• DAO Implementation: Concrete classes that implement the CRUD
methods for a specific data model, handling the logic for interacting with
the database.

2.3 Coding example


Below is a simple coding example illustrating how to implement the persistence
layer for a User entity using JDBC. This includes database configuration, a data
model, a CRUD interface, and a DAO implementation.

2.3.1 Database Configuration


The DatabaseConfiguration class is an abstract utility class that centralizes
the database connection logic. This ensures that the connection details (like
URL, username, and password) are managed in one place, promoting reusability
and maintainability.

1 import java . sql . Connection ;


2 import java . sql . DriverManager ;
3 import java . sql . SQLException ;
4

5 public abstract class D a t a b a s e C o n f i g u r a t i o n {


6 private static final String URL = " jdbc : mysql ://
localhost :3306/ exampledb " ;
7 private static final String USER = " root " ;
8 private static final String PASSWORD = " password
";
9

10 public static Connection getConnection () throws


SQLException {
11 return DriverManager . getConnection ( URL , USER
, PASSWORD ) ;
12 }
13 }

Listing 1: Database Configuration in Java

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.

• Connection: Represents an active database connection, which is necessary


for executing SQL queries.

2.3.2 User Model


The User class serves as a data model to represent the structure of a record in
the database. Each instance corresponds to a row in the users table.

1 public class User {


2 private int id ;
3 private String name ;
4 private String email ;
5

6 // Getters and setters


7 public int getId () {
8 return id ;
9 }
10

11 public void setId ( int id ) {


12 this . id = id ;
13 }
14

15 public String getName () {


16 return name ;
17 }
18

19 public void setName ( String name ) {


20 this . name = name ;
21 }
22

23 public String getEmail () {


24 return email ;
25 }
26

27 public void setEmail ( String email ) {


28 this . email = email ;
29 }
30 }

Listing 2: User Data Model in Java

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.

2.3.3 CRUD Interface


The UserDAO interface defines the contract for performing CRUD operations
on the User data.

1 import java . util . List ;


2

3 public interface UserDAO {


4 void createUser ( User user ) ;
5 User getUserById ( int id ) ;
6 List < User > getAllUsers () ;
7 void updateUser ( User user ) ;
8 void deleteUser ( int id ) ;
9 }

Listing 3: CRUD Interface for User Entity

Explanation:
• createUser(User user): Adds a new user record to the database.
• getUserById(int id): Fetches a user record based on its ID.

• getAllUsers(): Retrieves all user records from the database.


• updateUser(User user): Updates an existing user record with new de-
tails.
• deleteUser(int id): Deletes a user record based on its ID.

2.3.4 DAO Implementation


The UserDAOImpl class implements the UserDAO interface. It provides the
concrete logic for interacting with the database.

1 import java . sql .*;


2 import java . util . ArrayList ;
3 import java . util . List ;
4

5 public class UserDAOImpl extends


D a t a b a s e C o n f i gu r a t i o n implements UserDAO {

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.

3 JDBC template approach


3.1 JDBC template
The Spring JdbcTemplate is a high-level API in the Spring Framework that
simplifies database interactions by handling common tasks such as connection
management, SQL statement execution, and exception handling, making data
access code more concise and reducing boilerplate associated with the standard
JDBC API.
NOTE : In order to use JDBC template, it is necessary to declare it
as a dependency in the pom.xml file

Figure 5: JDBC template

3.2 Setup for the Connection


In a Spring Boot application, connection details are defined in the application.properties
file. Based on this configuration, the Spring IoC (Inversion of Control) container

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

Listing 5: MySQL database configuration

3.3 Data Business Logic


In the context of using the JDBC template, the persistence layer follows an
architecture similar to the previous approach. Each data model has a corre-
sponding DAO class responsible for managing its database operations.
However, instead of using Connection objects to interact with the database,
DAOs now use the JDBC template.

Figure 6: JDBC template

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.

Figure 7: Dependency Injection in the Persistence Layer

3.4 Coding example ( Ayoub )


The following example demonstrates the implementation of a persistence layer
for managing a Product entity using Spring JDBC. It includes database config-
uration, a data model, a DAO interface, and a DAO implementation.

3.4.1 Database Configuration


The DatabaseConfig class defines the JdbcTemplate bean, which is essential
for executing SQL operations in Spring applications.

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.

3.4.2 Product Data Model


The Product class serves as a data model for the Product entity, representing
a database record.

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 }

Listing 7: Product Data Model in Java

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.

3.4.3 Product DAO Interface


The ProductDao interface defines the CRUD operations for the Product en-
tity.

1 public interface ProductDao {


2 int save ( Product product ) ;
3 int update ( Product product ) ;
4 int deleteById ( Long id ) ;
5 Optional < Product > findById ( Long id ) ;
6 List < Product > findAll () ;
7 }

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.

3.4.4 DAO Implementation


The ProductDaoImpl class implements the ProductDao interface, providing
concrete methods for interacting with the database.

1 @Repository
2 public class ProductDaoImpl implements ProductDao {
3 private final JdbcTemplate jdbcTemplate ;
4

5 private final RowMapper < Product >


productRowMapper = ( rs , rowNum ) -> {
6 Product product = new Product () ;
7 product . setId ( rs . getLong ( " id " ) ) ;
8 product . setLabel ( rs . getString ( " label " ) ) ;
9 product . setPrice ( rs . getDouble ( " price " ) ) ;
10 return product ;
11 };
12

13 public ProductDaoImpl ( JdbcTemplate jdbcTemplate )


{
14 this . jdbcTemplate = jdbcTemplate ;
15 }
16

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

28 if ( keyHolder . getKey () != null ) {


29 product . setId ( keyHolder . getKey () .
longValue () ) ;
30 }
31

32 return keyHolder . getKey () != null ? 1 : 0;


33 }
34

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 }

Listing 9: DAO Implementation for Product Entity

Explanation:
• RowMapper: Maps each row of the result set to a Product object.

• KeyHolder: Captures the auto-generated ID when a new product is in-


serted into the database.
• Each method employs JdbcTemplate to execute the SQL queries effi-
ciently.

4 Java Persistence API Approach


This section outlines the usage of the Java Persistence API (JPA) and Hiber-
nate for implementing a persistence layer in Java applications. It covers an
introduction to JPA, the setup process for establishing a database connection,
and the integration of business logic.

4.1 JPA and Hibernate


The Java Persistence API (JPA) is a standard specification for object-relational
mapping (ORM) in Java. It provides a platform-independent approach to in-
teract with relational databases by mapping Java objects to database tables.
Hibernate is a popular JPA implementation that offers advanced ORM features
and seamless integration with JPA.
Key Features:

• Eliminates boilerplate code for managing database operations.


• Provides annotations for mapping Java classes to database tables.
• Offers a query language (JPQL) for interacting with the database in an
object-oriented manner.

• Manages database connections and transactions automatically.


Benefits of JPA and Hibernate:
• Simplifies database operations through entity management.
• Reduces the likelihood of SQL injection by using parameterized queries.

• Improves portability across different databases.

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.

4. Use the EntityManagerFactory to create an EntityManager, which pro-


vides an interface for interacting with the persistence context.
Application Properties Configuration: To configure the MySQL database
connection in a Spring Boot application, the following properties should be
added to the application.properties file:
Example of application.properties Configuration:

1 # MySQL Database Configuration


2 spring . datasource . url = jdbc : mysql : // localhost :3306/
testdb ? useSSL = false & serverTimezone = UTC
3 spring . datasource . username = root
4 spring . datasource . password =
5 spring . datasource . driver - class - name = com . mysql . cj .
jdbc . Driver
6 spring . jpa . database - platform = org . hibernate . dialect .
M y SQ L 5 In n o DB D ial e c t
7 spring . jpa . hibernate . ddl - auto = update
8 spring . jpa . show - sql = true
9 spring . jpa . properties . hibernate . format_sql = true

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.

4.3 Data Business Logic


The business logic layer interacts with the persistence layer to perform CRUD
operations and apply business rules.
Responsibilities of the Business Logic Layer:

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.

• Abstracting the persistence logic from the presentation layer.


Using Repositories: Repositories in JPA are interfaces that allow you to
define and manage database interactions with minimal boilerplate code. Hi-
bernate can extend these repositories to provide custom queries and advanced
features.
Entity Lifecycle Management: JPA provides different states for entities
such as transient, persistent, and detached, allowing developers to manage the
entity’s lifecycle effectively.

4.4 Coding Example


In this section, we will walk through a simple example demonstrating how to
set up and use JPA with Hibernate in a Spring Boot application. The following
code illustrates how to define an entity, create a repository, and interact with
the database.
Step 1: Entity Class
To define a JPA entity, we need to annotate the class with @Entity and map
it to a table in the database using @Table. Additionally, we use @Id to mark
the primary key field and @GeneratedValue for automatic generation of the ID.

1 import javax . persistence . Entity ;


2 import javax . persistence . Id ;
3 import javax . persistence . GeneratedValue ;
4 import javax . persistence . GenerationType ;
5 import javax . persistence . Column ;
6

7 @Entity
8 public class Product {
9

10 @Id
11 @GeneratedValue ( strategy = GenerationType .
IDENTITY )
12 private Long id ;
13

14 @Column ( name = " label " , nullable = false )


15 private String label ;
16

17 @Column ( name = " price " , nullable = false )


18 private Double price ;

17
19

20 // Getters and Setters


21 }

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.

1 import org . springframework . data . jpa . repository .


JpaRepository ;
2

3 public interface Pr oductR eposit ory extends


JpaRepository < Product , Long > {
4 // Custom query methods can be defined here if
needed
5 }

By extending JpaRepository, we gain access to several methods like save(),


findById(), findAll(), and deleteById() without having to write the imple-
mentation ourselves.
Step 3: Service Layer
The service layer is used to handle business logic and interact with the
repository. Here’s an example of a service class that performs basic CRUD
operations for Product entities.

1 import org . springframework . beans . factory . annotation .


Autowired ;
2 import org . springframework . stereotype . Service ;
3 import java . util . List ;
4 import java . util . Optional ;
5

6 @Service
7 public class ProductService {
8

9 @Autowired
10 private P roduct Reposi tory p roduct Reposi tory ;
11

12 public Product saveProduct ( Product product ) {


13 return pro ductRe posito ry . save ( product ) ;
14 }
15

18
16 public Optional < Product > getProductById ( Long id )
{
17 return pro ductRe posito ry . findById ( id ) ;
18 }
19

20 public List < Product > getAllProducts () {


21 return pro ductRe posito ry . findAll () ;
22 }
23

24 public void deleteProduct ( Long id ) {


25 prod uctRe posito ry . deleteById ( id ) ;
26 }
27 }

In the ProductService class, we use the ProductRepository to save, re-


trieve, and delete products. The methods are self-explanatory, utilizing the
CRUD operations provided by JpaRepository.
Step 4: Controller Layer
Finally, we create a controller class to expose the service methods via REST-
ful endpoints. This allows us to interact with the Product entity from the front
end.

1 import org . springframework . beans . factory . annotation .


Autowired ;
2 import org . springframework . web . bind . annotation .*;
3

4 import java . util . List ;


5 import java . util . Optional ;
6

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 @GetMapping ( " /{ id } " )


20 public Optional < Product > getProductById (
@PathVariable Long id ) {
21 return productService . getProductById ( id ) ;

19
22 }
23

24 @GetMapping
25 public List < Product > getAllProducts () {
26 return productService . getAllProducts () ;
27 }
28

29 @DeleteMapping ( " /{ id } " )


30 public void deleteProduct ( @PathVariable Long id )
{
31 productService . deleteProduct ( id ) ;
32 }
33 }

In the ProductController, we define the endpoints to create, read, and


delete products. We use @RequestBody for POST requests and @PathVariable
for dynamic URL parameters in GET and DELETE requests.
Step 5: Application Configuration
Ensure that the application is properly configured to run a Spring Boot JPA
application. The configuration details can be set in the application.properties
file, as described in the previous section.
This setup creates a complete flow for managing entities in a Spring Boot
application with JPA and Hibernate. With minimal configuration, Spring Boot
handles most of the boilerplate code, allowing developers to focus on business
logic.

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

You might also like