Spring Data JDBC là một thành viên mới được thêm vào trong Spring Data. Nó là sự kết hợp các thành phần của Spring Data JPA (Java Persistence API) và Spring JDBC. Bài viết này sẽ giới thiệu tới các bạn về khái niệm này và lý do tại sao chúng ta nên sử dụng nó trong dự án Spring Boot.
Giới thiệu
Spring Data JDBC là một thành viên mới trong gia đình Spring Data. Nó được tạo ra để khắc phục những điểm yếu của Spring JDBC và Spring Data JPA. Chúng ta thử cùng xem xét về Spring JDBC, khả năng làm việc của nó thì khá là thấp chủ yếu là được sử dụng để kết nối với cơ sở dữ liệu. Còn Spring Data JPA thì lại quá phức tạp vì nó cho chúng ta quá nhiều sự lựa chọn điều đó làm cho chúng ta rất khó để làm chủ được tất cả các lựa chọn này. Spring Data JDBC là một framework cung cấp cho chúng ta những chức năng giống như chúng ta đang sử dụng Spring Data JPA nhưng lại dễ hiểu hơn rất nhiều bơi nó sử dụng nguyên tắc DDD. Nó còn giúp chúng ta được quyền kiểm soát nhiều hơn bằng cách làm việc ở cấp độ thấp hơn nó cho phép ta được quyết định khi các tương tác với cơ sở dữ liệu được thực hiện như Spring JDBC nhưng lại theo một cách dễ dàng hơn.
Spring Data JDBC vs Spring JDBC vs Spring Data JPA
Spring JDBC
Spring JDBC cung cấp cho chúng ta một framework để thực thi các câu lệnh SQL. Nó xử lý việc kết nối với cơ sở dữ liệu và giúp chúng ta thực thi các câu lệnh SQL bằng cách sử dụng JdbcTemplate. Nhờ vậy mà nó rất là linh hoạt vì chúng ta hoàn toàn có quyền được kiểm soát việc các câu lệnh SQL được thực thi.
Spring Data JPA
Spring Data JPA sử dụng các entity nên do đó cấu trúc của các class cần phải giống với cấu trúc của các bảng trong cơ sở dữ liệu. Ở dạng đơn giản nhất, mỗi bảng cơ sở dữ liệu sẽ đại diện cho một thực thể và có thể được ánh xạ gần như trực tiếp trên một entity class.
Sử dụng @Entity để đánh dấu một Entity class, sử dụng @OneToMany, @ManyToOne, @ManyToMany để tạo kết nối giữa các bảng.
Chúng ta cùng xem xét ví dụ sau để hiểu cách sử dụng @OneToMany và @ManyToOne trong các entity
{ @Id @GeneratedValue(strategy = GenerationType.AUTO)
@Entity
public
class
Rental
private
Long id; @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name ="company_id"
)private
RentalCompany company; } @Entitypublic
class
RentalCompany
{ @Id @GeneratedValue(strategy = GenerationType.AUTO)private
Long id; @OneToMany(fetch = FetchType.LAZY, mappedBy ="company"
)private
List
<Rental> rentals; }Code language:
PHP
(
php
)
Spring Data JDBC
Khi mà chúng ta sử dụng đến Spring Data JDBC, ta sẽ cần tạo các entity class được kết nối với cơ sở dữ liệu. Nhưng điểm khác biệt lớn đó là nó sẽ có nhiều quy tắc hơn và chúng ta cần phải tuân theo khi tạo cấu trúc một class. Cấu trúc của class cần phải tuân theo luật lệ của mẫu thiết kể tổng hợp DDD. Spring Data thực thi điều này vì điều này sẽ dẫn đến việc tạo ra các dự án đơn giản và dễ hiểu hơn. Đây là một số luật mà chúng ta cần phải tuân theo:
- Một entity có thể là một phẩn của aggregate
- Tất các các mối quan hệ bên trong aggregate phải là một chiều
- root aggregate cần được quản lý top relation
Điều này có nghĩa là bằng cách đi theo các liên kết bắt đầu từ root aggregate, mọi thực thể bên trong tập hợp đều có thể được tìm thấy. Do đó, chúng ta không cần một kho lưu trữ cho mỗi thực thể như trong Spring Data JPA, mà chỉ cho các gốc tổng hợp. Để liên kết các lớp thực thể với nhau để tạo thành một tập hợp, ta cần sử dụng tham chiếu đối tượng. Các lớp thực thể bên trong một tập hợp chỉ có thể có mối quan hệ một-một và một-nhiều. Nếu ta có mối quan hệ một – một, thực thể của ta chỉ cần một tham chiếu đối tượng đến đối tượng kia. Khi có mối quan hệ một-nhiều, thực thể cần chứa một tập hợp các tham chiếu đối tượng. Để tạo quan hệ với các thực thể bên ngoài tập hợp, id cần được sử dụng để có được sự kết hợp thấp giữa các lớp này.
Một sự khác biệt lớn trong việc tạo các lớp được sử dụng bởi Spring Data JDBC so với Spring Data JPA là không cần sử dụng @Entity và không có annotation quan hệ như @OneToMany. Spring JDBC biết một lớp là một root aggregate khi nó chứa một kho lưu trữ cho lớp đó. Và do các quy tắc mà các thực thể tổng hợp được kết nối thông qua các tham chiếu đối tượng, Spring Data JDBC cũng biết các tập hợp là gì và có thể chuyển dữ liệu đến cơ sở dữ liệu dưới dạng các tập hợp.
Ví dụ:
{ @Id
public
class
RentalCompany
private
Long id;private
String name;private
Set<Rental> rentals; }public
class
Rental
{ @Idprivate
Long id;private
String renter;private
Long carId;private
LocalDate startDate;private
LocalDate endDate; }public
class
Car
{ @Idprivate
Long id;private
String color;private
String brand;private
String model;private
String licensePlate;private
CarType type; }Code language:
PHP
(
php
)
Chèn dữ liệu (Insert)
Spring JDBC
Với Spring JDBC chúng ta sẽ viết câu lệnh thêm bản ghi của chúng ta vào và thực thi chúng bằng cách sử dụng JDBC Template. Lợi ích của việc tự viết tất cả các câu Query đó là chúng ta có toàn quyền kiểm soát chúng.
SingleConnectionDataSource dataSource =
new
SingleConnectionDataSource(); dataSource.setDriverClassName("org.hsqldb.jdbcDriver"
); dataSource.setUrl("jdbc:hsqldb:data/jdbcexample"
); dataSource.setUsername("sa"
); dataSource.setPassword(""
); JdbcTemplate template =new
JdbcTemplate(ds); template.execute("create table car (id int, model varchar)"
); template.execute("insert into car (id, model) values (1, 'Volkswagen Beetle')"
); dataSource.destroy();Code language:
JavaScript
(
javascript
)
Spring Data JPA
Còn với Spring Data JPA, chúng ta muốn thêm mới một bản ghi vào trong cơ sở dữ liệu thì chúng ta đơn giản chỉ cần kế thừa lại Interface có sẵn của Spring JPA và gọi phương thức save()
{
@Service
public
class
RentalService
private
RentalRepository rentalRepository;public
RentalService(RentalRepository rentalRepository){ this.rentalRepository = rentalRepository; }public
Rental create(Rental rental){return
rentalRepository.save(rental); } }Code language:
PHP
(
php
)
Spring Data JDBC
Spring Data JDBC sử dụng cú pháp có thể tương tự với Spring Data JPA. Sự khác biệt lớn nhất đó là việc quản lý persistence được xử lý bởi kho lưu trữ giống như trong Spring Data JPA, nhưng chỉ root aggregate mới có repository. Điều này có nghĩa là nếu ta muốn thêm hoặc cập nhật dữ liệu, toàn bộ tổng thể cần được lưu. Chúng ta sẽ cần gọi phương thức save của repository của root aggregate và điều này trước tiên sẽ save root aggregate và sau đó tất cả các thực thể được tham chiếu sẽ được lưu lại. Nếu bạn chỉ muốn chèn một phần của tổng hợp, chẳng hạn như chỉ tạo một Rental, thì toàn bộ aggregate sẽ được cập nhật và các thực thể được tham chiếu sẽ bị xóa và chèn lại.
{
@Service
public
class
RentalCompanyService
private
RentalCompanyRepository rentalCompanyRepository;public
RentalCompanyService(RentalCompanyRepository rentalCompanyRepository){ this.rentalCompanyRepository = rentalCompanyRepository; }public
RentalCompany addRental(Rental rental, Long rentalCompanyId){ RentalCompany rentalCompany = rentalRepository.findById(rentalCompanyId); rentalCompany.getRentals().add(rental);return
rentalRepository.save(rentalCompany); } }Code language:
PHP
(
php
)
Thực thi các câu lệnh query
Spring JDBC
Để thực thi các câu lệnh query chúng ta vẫn sử dụng đến JdbcTemplate. Nhược điểm của nó là chỉ cung cấp cho việc kết nối còn lại chúng ta sẽ phải tự làm tất cả mọi thứ. Nếu như cần tìm kiếm một đối tượng, ta sẽ cần ánh xạ kết quả tới các đối tượng Java bằng cách implement lại interface RowMapper.
Cùng xem xét ví dụ sau:
{ @Override
public
class
CarRowMapper
implements
RowMapper
<Car
>public
Car mapRow(ResultSet resultSet, int rowNumber) throws SQLException { Car car =new
Car(); car.setId(resultSet.getInt("ID"
)); car.setColor(resultSet.getString("COLOR"
)); car.setBrand(resultSet.getString("BRAND"
)); car.setModel(resultSet.getString("MODEL"
));return
car; } }Code language:
PHP
(
php
)
Mapper này sẽ được chuyển đến cho JdbcTemplate và nó sẽ sử dụng để tạo các đối tượng Java.
List<Car> cars = jdbcTemplate.queryForObject(
"SELECT * FROM CAR WHERE ID = ?"
,new
Object
[] {id},new
CarRowMapper());Code language:
JavaScript
(
javascript
)
Spring Data JPA
Nếu chúng ta sử dụng JPA thì nó đã cung cấp sẫn cho chúng ta các phương thức tìm kiếm như làm findById, hay là findByName, việc của chúng ta đó là chỉ cần gọi lại các phương thức đó, JPA sẽ tự thực thi các câu lệnh cho chúng ta.
{
@Service
public
class
RentalService
private
RentalRepository rentalRepository;public
RentalService(RentalRepository rentalRepository){ this.rentalRepository = rentalRepository; }public
List
<Rental> getRentalsByCarType(Long rentalCompanyId, CarType carType) {return
rentalRepository.findByCompanyIdAndCarType(rentalCompanyId, carType); } }Code language:
PHP
(
php
)
Repository của chúng ta sẽ khai báo một phương thức như sau
{
public
interface
RentalRepository
extends
PagingAndSortingRepository
<Rental
,Long
>List
<Rental> findByCompanyIdAndCarType(Long rentalCompanyId, CarType carType); }Code language:
PHP
(
php
)
Spring Data JDBC
Spring Data JDBC có ít trừu tượng hơn Spring Data JPA, nhưng sử dụng các khái niệm Spring Data để giúp dễ dàng thực hiện các câu lệnh CRUD hơn Spring JDBC.
Khi muốn thêm một phương thức vào repository của mình trong Spring Data JDBC, chúng ta sẽ cần thêm annotation @Query chứa truy vấn. Chúng ta sẽ phải sử dụng câu lệnh SQL thuần túy thay vì JPQL được sử dụng trong Spring Data JPA.
Ví dụ
{
@Service
public
class
RentalCompanyService
public
RentalCompanyRepository rentalCompanyRepository;public
RentalCompanyService(RentalCompanyRepository rentalCompanyRepository){ this.rentalCompanyRepository = rentalCompanyRepository; }public
List
<Rental> getRentalsByCarType(Long rentalCompanyId, CarType carType) {return
rentalRepository.findByIdAndCarType(rentalCompanyId, carType); } }Code language:
PHP
(
php
)
Điểm khác biệt với JPA đó là chúng ta sẽ sử dụng @Query như sau:
{ @Query(value =
public
interface
RentalCompanyRepository
extends
CrudRepository
<RentalCompany
,Long
>"SELECT * "
+"FROM Rental rental "
+"JOIN Car car ON car.id = rental.car_id "
+"WHERE rental.rental_company = :companyId "
+"AND car.type = :carType"
)List
<Rental> findRentalsByIdAndCarType(@Param("companyId"
) Long companyId, @Param("carType"
)String carType); }Code language:
PHP
(
php
)
Lợi ích của việc sử dụng Spring Data JDBC
- Một trong những lợi ích lớn nhất của Spring Data JDBC đó là nó sử dụng luật của DDD design như sử dụng các aggregate.
- Nó rất dễ để có thể hiểu
- Với Spring Data JDBC tất cả các câu query đều là eager vì vậy sẽ có ít câu query được gửi về database hơn nhờ đó tăng thêm năng suất cho hệ thống.