Hướng dẫn spring boot mongodb validation - xác thực mongodb khởi động mùa xuân

Note: Bài viết mang tích chất lưu lại kiến thức để sử dụng sau, do mày mò khắp google nhặt mỗi nơi một tí mới giải quyết được, nếu bạn nào gặp vấn đề tương tự thì có thể tham khảo.

Số là mình bắt đầu thực hiện một dự án mới trên Spring Boot và cần làm việc với NoSQL, cụ thể là Mông Gô Đê Bê (MongoDB), Trước thì mình có đọc và tham khảo qua về SQL và NoSQL rồi, tuy nhiên mình chỉ làm việc với MySQL, SQL, Postgre, Oracle - rặt một đám SQL. Đơn giản thì mình nghĩ chắc connect Mongo cũng thế thôi, vì JPA và Hibernate viết ra để dùng ALL In ONE mà. Cơ mà đâm đầu vào thì nó cũng mệt mỏi vãi đạn.

Những thứ cần chuẩn bị:

1 project Spring Boot maven hay gradle thì tùy, Mình xài gradle

Server Mongo ( Local cũng được)

Mongo Compass( Có cũng dc mà không cũng được)

IDE - tất nhiên ( M xài Intelij, bản 2020 trở lên tool mới hỗ trợ kết nối Mongo nhé)

Trước tiên các bạn cần nhớ bảng trong Mongo không gọi là Table mà

Table = Collection

Row = Documnent ( Cặp key value)

Còn vài thứ nữa các bạn tham khảo bài của bạn này nhé: Hướng dẫn Mongo cơ bản

Ấy thế mà trong entity @Table lại thay bằng @Document chứ không có cái @Collection ) Méo hiểu.

Hướng dẫn spring boot mongodb validation - xác thực mongodb khởi động mùa xuân
) Méo hiểu.

CỤ thể thì các bước cần làm như sau

1 - Thêm dependencies:

Các bạn cần:

org.springframework.data:spring-data-mongodb
org.mongodb:mongo-java-driver:3.12.11

2 - Config trong file yml

Chắc hẳn các bạn đã quyen với kiểu connect này:

spring:
  datasource:
    url: jdbc:mysql://localhost:3306/cloudsigning
    driver-class-name: com.mysql.cj.jdbc.Driver
    username: root
    password:
    jpa:
      hibernate:
        ddl-auto: create
        naming:
          implicit-strategy: org.springframework.boot.orm.jpa.hibernate.SpringImplicitNamingStrategy
          physical-strategy: org.springframework.boot.orm.jpa.hibernate.SpringPhysicalNamingStrategyserver

Nhưng cụ Mongo thì đi một đường riêng:

spring:
  data:
    mongodb:
      authentication-database: admin
      database: test
      host: localhost
      port: 27017 

Ồ, Good Jobs. Có vẻ ngắn gọn ra phết nhờ

Run cái coi. Lỗi sấp mặt lợn, và sau 10p mò mò à nó k dùng @ Table mà xài @ Document + Repository thì extends MongoRepository thay vì JpaRepository

3 - Entity - Chỉ cần @Id thôi nhé, không cần chỉ định column cũng dc. right

@Entity
@Document(value = "user")
public class UserAccount {
    @Id
    private Integer id;

    private String userName;

4- Repository

@Repository
public interface UserAccountRepository extends MongoRepository {

 }

5 - Config Mongo

===> có vẻ ổn nhờ, Run cái nữa nào --- lại lỗi-- Google tiếp. À lại config tiếp:

@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class , MongoAutoConfiguration.class, MongoDataAutoConfiguration.class})

== chạy thử, hơi giunnnnnn. Lại hẹo. Lần này lỗi có vẻ tường minh tí

Lỗi đây: A component required a bean named 'mongoTemplate' that could not be found.

Có vẻ thiếu Context không kiếm dc cái bean kia. Lại mò GG tiếp Đây rồi, mò mãi mới kiếm được trên GitHub, chắc của mấy anh Ấn thần thánh

@Configuration
public class MongoConfig extends AbstractMongoClientConfiguration {

    @Override
    protected String getDatabaseName() {
        return env.getProperty("spring.data.mongodb.database");
    }

}

Run lại cái nào Úi xời mừng rơi nước mắt, chạy rồi )))))))))))

Hướng dẫn spring boot mongodb validation - xác thực mongodb khởi động mùa xuân
)))))))))))

Đấy, cụ thể là các bạn chú ý mấy cái nghớ nghẩn đó.

Và đặc biệt trong file pom/build không được thiếu thằng này nhé " org.mongodb:mongo-java-driver" , không có nó là cái AbstractMongoClientConfiguration không có đủ client để run và hẹo luôn nha. Còn lại CRUD thì khả năng không khác các loại khác đâu, Query thì nhớ xài PLSQL nhá

Có thể các bạn thấy mình viết đơn giản có ba lần "**Run thử phát xem **" là chạy mượt mà bày vẽ viết cái bài hướng dẫn. Thực tế thì mình mất xừ buổi tối để connect mà chả giải quyết được việc gì khác, " **Run thử **" cũng đâu đấy 10-20 lần nha ))

Hướng dẫn spring boot mongodb validation - xác thực mongodb khởi động mùa xuân
))

Thank!!!

P/S bạn nào có cách ngắn hơn thì chỉ mình cái nhé

Lời mở đầu

Như chúng ta đã biết, việc sử dụng Aggregation trong mongoDb để thực hiện xử lý dữ liệu trong MongoDb là rất cần thiết. Đây là một framework mạnh mẽ linh hoạt trong quá trình truy vấn và xử lý dữ liệu với các toán tử mạnh mẽ cũng như hỗ trợ xử lý dữ liệu với số lượng lớn. Loạt bài trước, tác giả đã miêu tả tổng quan về Aggregation framework trong mongoDb. Các bạn có thể tìm hiểu tại đây : https://viblo.asia/p/tim-hieu-ve-aggregation-framework-trong-mongodb-Az45brRV5xY . Bài viết này sẽ đi sâu vào tìm hiểu cách tích hợp Aggregation vào Spring để có thể linh hoạt hơn trong việc xử lý dữ liệu

Các lưu ý cần thiết

Các công cơ bản.

  • Maven
  • Spring boot
  • Database : MongoDb (có thể download tại https://www.mongodb.com/download-center/community lựa chọn hệ điều hành phù hợp và download)
  • Dependency : Spring boot web , Spring boot data mongodb
  • Robo3T
  • Postman

Lưu ý

  • Trong loạt bài viết này sẽ trình bày xử lý Aggregation trong spring. Các thao tác xử lý cơ bản như CRUD sẽ không được đề cập trong bài viết này. Các bạn có thể tìm hiểu thêm trên internet. Về mặt cơ bản các toán tử CRUD trong mongoDb vẫn xử lý tương tự như trong hệ cơ sở dử liệu (RDBMS). Bài viết này sẽ trình bày thêm về việc xử lý dữ liệu dựa vào Cursor để xử lý các bản ghi vượt quá 16mb.

Các thuật ngữ thường dùng trong bài viết

Khác với hệ cơ sở dữ liệu RDBMS, Spring data MongoDb dùng thuật ngữ khác để thực hiện ánh xạ, xử lý và thao tác trên MongoDb, Dưới đây là bảng so sánh giữa 2 hệ cở sở dữ liệu và sự khác nhau của nó

MongoDbRDBMSÝ nghĩa
Document Entity Đánh dấu đây là một lớp có thể thực hiện thao tác ánh xạ và xử lý dữ liệu với database, xác định tên Document
Field Column Xác định tên cột(với RDBMS ) hoặc tên trường với MongoDb để thực hiện ánh xạ

Bài viết sẽ thực hiện dùng Aggregation thao tác với Collection DB tên là Employees gồm các thông tin như phía dưới :

/* 1 */
{
    "_id" : ObjectId("5daa7b6cd20119058c403b85"),
    "firstName" : "A",
    "lastName" : "Nguyen Van",
    "age" : 25,
    "location" : "DN",
    "salary" : 22.2
}

/* 2 */
{
    "_id" : ObjectId("5daa7b6cd20119058c403b86"),
    "firstName" : "B",
    "lastName" : "Tran Van",
    "age" : 27,
    "location" : "HN",
    "salary" : 22.7
}

/* 3 */
{
    "_id" : ObjectId("5daa7b6cd20119058c403b87"),
    "firstName" : "C",
    "lastName" : "Le Van",
    "age" : 28,
    "location" : "DN",
    "salary" : 80.2
}

Thực hiện

Trước tiên cần tiến hành tạo một project bằng spring boot bằng cách truy cập vào trang start.spring.io với các thông tin cấu hình như phía dưới :

Về thông tin cấu hình : Group và Artifact có thể tùy chọn đặt tên . Tuy nhiên nên đặt theo quy tắc : com.tên company hoặc cá nhân. tên project muốn đặt. Ở mục Dependencies chọn 2 dependency là Spring Web và Spring Data Mongo Db. Dependency Lombok có thể tùy chọn có hoặc không.

Tiếp đến để tiến hành kết nối với cơ sở dữ liệu database, chúng ta cần định cấu hình kết nối với MongoDb trong file applications.properties hoặc application.yml như phía dưới :

Applications.properties :

server.port=8989
spring.data.mongodb.uri=mongodb://root:root@localhost:27017/BaoTrung
spring.jpa.show-sql=true
logging.level.org.springframework.data.mongodb.core.MongoTemplate=DEBUG
logging.level.org.springframework.web = DEBUG

Cấu hình trên chỉ ra server đang chạy ở port 8989, spring.data.mongodb.uri định cấu hình connect với database với các thông số như username là root password là root , port 27017(mặc định của mongoDb) và database là BaoTrung. Các thông số tiếp theo chỉ định việc show các lệnh khi thao tác với mongoDb cũng như các level log khi thực hiện thao tác. Trong loạt bài viết đang sử dụng ở mức debug.port 8989, spring.data.mongodb.uri định cấu hình connect với database với các thông số như username là root password là root , port 27017(mặc định của mongoDb) và database là BaoTrung.
Các thông số tiếp theo chỉ định việc show các lệnh khi thao tác với mongoDb cũng như các level log khi thực hiện thao tác. Trong loạt bài viết đang sử dụng ở mức debug.

Có một lưu ý ở đây : Trong bài viết đang sử dụng Mongo 3.0 Java driver ,cấu hình sẽ thông qua uri. Việc cấu hình như phía dưới sẽ không thành công đối với phiên bản Mongo 3.0 Java driver trở lên (chỉ áp dụng với Mongo 3.0 Java driver trở xuống) . Lý do là spring.data.mongodb.host và spring.data.mongodb.port đã bị loại bỏ trong Mongo 3.0 Java driverMongo 3.0 Java driver ,cấu hình sẽ thông qua uri. Việc cấu hình như phía dưới sẽ không thành công đối với phiên bản Mongo 3.0 Java driver trở lên (chỉ áp dụng với Mongo 3.0 Java driver trở xuống) . Lý do là spring.data.mongodb.hostspring.data.mongodb.port đã bị loại bỏ trong Mongo 3.0 Java driver

//Không áp dụng với Mongo 3.0 Java Driver trở lên

spring.data.mongodb.authentication-database=admin
spring.data.mongodb.username=root
spring.data.mongodb.password=root
spring.data.mongodb.database=baotrung
spring.data.mongodb.port=27017
spring.data.mongodb.host=localhost

Model

Thực hiện tạo một class có tên là Employee như phía dưới :Employee như phía dưới :

Employee.class

@Document(collection = "Employees")
public class Employee {

    @Id
    private String id;
    @Field(value = "firstName")
    @NotBlank(message = "FirstName can't empty!")
    private String firstName;
    @NotBlank(message = "LastName can't empty!")
    private String lastName;
    @NotNull
    private Integer age;
    @NotBlank(message = "Location can't empty!")
    private String location;
    private Double salary;

    public Employee() {
    }

    public Employee(String id, String firstName, String lastName, Integer age, String location, Double salary) {
        this.id = id;
        this.firstName = firstName;
        this.lastName = lastName;
        this.age = age;
        this.location = location;
        this.salary = salary;
    }

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getFirstName() {
        return firstName;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }

    public String getLastName() {
        return lastName;
    }

    public void setLastName(String lastName) {
        this.lastName = lastName;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public String getLocation() {
        return location;
    }

    public void setLocation(String location) {
        this.location = location;
    }

    public Double getSalary() {
        return salary;
    }

    public void setSalary(Double salary) {
        this.salary = salary;
    }

    @Override
    public String toString() {
        return "Employee{" +
                "id='" + id + '\'' +
                ", firstName='" + firstName + '\'' +
                ", lastName='" + lastName + '\'' +
                ", age=" + age +
                ", location='" + location + '\'' +
                ", salary=" + salary +
                '}';
    }
}

Lớp trên chỉ đơn giản là dùng @Document xác định nó là một thực thể để có thể thao tác với MongoDb với các trường như FirstName, LastName, Salary ,Age, Location . Có một lưu ý ở đây là do ở MongoDb đang sử dụng document tên là Employees nên nếu muốn ánh xạ đúng tên document tên là Employess thì chúng ta sẽ sử dụng

spring:
  data:
    mongodb:
      authentication-database: admin
      database: test
      host: localhost
      port: 27017 
7@Document xác định nó là một thực thể để có thể thao tác với MongoDb với các trường như FirstName, LastName, Salary ,Age, Location . Có một lưu ý ở đây là do ở MongoDb đang sử dụng document tên là Employees nên nếu muốn ánh xạ đúng tên document tên là Employess thì chúng ta sẽ sử dụng
spring:
  data:
    mongodb:
      authentication-database: admin
      database: test
      host: localhost
      port: 27017 
7

Tạo một lớp tên là EmployeeController và thực hiện xác định các api cần thiết cho việc xử lý dữ liệu. Ở đây chúng ta sẽ xác định các api như findByLocation , calculateTotalUser và calculateSalaryEmployeeController và thực hiện xác định các api cần thiết cho việc xử lý dữ liệu. Ở đây chúng ta sẽ xác định các api như findByLocation , calculateTotalUser và calculateSalary

Controller : :

EmployeeController

spring:
  datasource:
    url: jdbc:mysql://localhost:3306/cloudsigning
    driver-class-name: com.mysql.cj.jdbc.Driver
    username: root
    password:
    jpa:
      hibernate:
        ddl-auto: create
        naming:
          implicit-strategy: org.springframework.boot.orm.jpa.hibernate.SpringImplicitNamingStrategy
          physical-strategy: org.springframework.boot.orm.jpa.hibernate.SpringPhysicalNamingStrategyserver
0

Lớp controller phía trên sẽ xác định các api cần thiết, các thông số đầu vào và trả về reponse chứa dữ liệu được wrap trong các DTO như EmployeeResult ,EmployeeDto dựa trên thông tin của mỗi API . Các DTO này sẽ được định nghĩa phía dưới. Lưu ý rằng ở đây controller sẽ gọi trực tiếp đến repository không thông qua lớp service do hiện tại chưa có nhiều logic phức tạp để xử lý và đây là những xử lý đơn giản. Tùy theo logic và các yêu cầu mà các bạn có thể dùng cho phù hợp trong từng dự án.EmployeeResult ,EmployeeDto dựa trên thông tin của mỗi API . Các DTO này sẽ được định nghĩa phía dưới. Lưu ý rằng ở đây controller sẽ gọi trực tiếp đến repository không thông qua lớp service do hiện tại chưa có nhiều logic phức tạp để xử lý và đây là những xử lý đơn giản. Tùy theo logic và các yêu cầu mà các bạn có thể dùng cho phù hợp trong từng dự án.

Tạo một interface với tên là EmployeeRepository và EmployeeRepositoryCustom :EmployeeRepositoryEmployeeRepositoryCustom :

EmployeeRepository

spring:
  datasource:
    url: jdbc:mysql://localhost:3306/cloudsigning
    driver-class-name: com.mysql.cj.jdbc.Driver
    username: root
    password:
    jpa:
      hibernate:
        ddl-auto: create
        naming:
          implicit-strategy: org.springframework.boot.orm.jpa.hibernate.SpringImplicitNamingStrategy
          physical-strategy: org.springframework.boot.orm.jpa.hibernate.SpringPhysicalNamingStrategyserver
1

EmployeeRepositoryCustom

spring:
  datasource:
    url: jdbc:mysql://localhost:3306/cloudsigning
    driver-class-name: com.mysql.cj.jdbc.Driver
    username: root
    password:
    jpa:
      hibernate:
        ddl-auto: create
        naming:
          implicit-strategy: org.springframework.boot.orm.jpa.hibernate.SpringImplicitNamingStrategy
          physical-strategy: org.springframework.boot.orm.jpa.hibernate.SpringPhysicalNamingStrategyserver
2

Ở interface EmployeeRepositoryCustom chúng ta sẽ định cấu hình 3 method làEmployeeRepositoryCustom chúng ta sẽ định cấu hình 3 method là

  • fetchAllLastNameByLocation với param là location và trả về 1 list các LastName dựa trên location, với param là location và trả về 1 list các LastName dựa trên location,
  • countTotalUserByLocation với param là location và trả về 1 list các total user by location dựa trên DTO EmployeeResult với param là location và trả về 1 list các total user by location dựa trên DTO EmployeeResult
  • calculateSalaryByAgeAndLocation với param là location và trả về 1 list các salary tương dựa trên age và loaction với param là location và trả về 1 list các salary tương dựa trên age và loaction

EmployeeResult : :

spring:
  datasource:
    url: jdbc:mysql://localhost:3306/cloudsigning
    driver-class-name: com.mysql.cj.jdbc.Driver
    username: root
    password:
    jpa:
      hibernate:
        ddl-auto: create
        naming:
          implicit-strategy: org.springframework.boot.orm.jpa.hibernate.SpringImplicitNamingStrategy
          physical-strategy: org.springframework.boot.orm.jpa.hibernate.SpringPhysicalNamingStrategyserver
3

EmployeeDto::

spring:
  datasource:
    url: jdbc:mysql://localhost:3306/cloudsigning
    driver-class-name: com.mysql.cj.jdbc.Driver
    username: root
    password:
    jpa:
      hibernate:
        ddl-auto: create
        naming:
          implicit-strategy: org.springframework.boot.orm.jpa.hibernate.SpringImplicitNamingStrategy
          physical-strategy: org.springframework.boot.orm.jpa.hibernate.SpringPhysicalNamingStrategyserver
4

Tạo một lớp có tên là EmployeeRepositoryCustomImpl và thực hiện implement lại EmployeeRepositoryCustom như phía dưới:EmployeeRepositoryCustomImpl và thực hiện implement lại EmployeeRepositoryCustom như phía dưới:

spring:
  datasource:
    url: jdbc:mysql://localhost:3306/cloudsigning
    driver-class-name: com.mysql.cj.jdbc.Driver
    username: root
    password:
    jpa:
      hibernate:
        ddl-auto: create
        naming:
          implicit-strategy: org.springframework.boot.orm.jpa.hibernate.SpringImplicitNamingStrategy
          physical-strategy: org.springframework.boot.orm.jpa.hibernate.SpringPhysicalNamingStrategyserver
5

Chúng ta sẽ thực hiện tính toán trong 3 method này sử dụng Aggregation dựa vào MongoTemplate. Vậy MongoTemplate là gì ? Lớp MongoTemplate, nằm trong gói org.springframework.data.document.mongodb, cung cấp các tính năng phong phú được thiết lập để tương tác với cơ sở dữ liệu. MongoTemplate cung cấp các method dùng để tạo, cập nhật, xóa và truy vấn cho các tài liệu MongoDB và cung cấp ánh xạ giữa các model và document. Đây là một lớp rất quan trọng trong việc thao tác với mongoDbAggregation dựa vào MongoTemplate. Vậy MongoTemplate là gì ?
Lớp MongoTemplate, nằm trong gói org.springframework.data.document.mongodb, cung cấp các tính năng phong phú được thiết lập để tương tác với cơ sở dữ liệu. MongoTemplate cung cấp các method dùng để tạo, cập nhật, xóa và truy vấn cho các tài liệu MongoDB và cung cấp ánh xạ giữa các modeldocument. Đây là một lớp rất quan trọng trong việc thao tác với mongoDb

Tích hợp MongoTemplate vào EmployeeRepositoryCustom như sau:MongoTemplate vào EmployeeRepositoryCustom như sau:

spring:
  datasource:
    url: jdbc:mysql://localhost:3306/cloudsigning
    driver-class-name: com.mysql.cj.jdbc.Driver
    username: root
    password:
    jpa:
      hibernate:
        ddl-auto: create
        naming:
          implicit-strategy: org.springframework.boot.orm.jpa.hibernate.SpringImplicitNamingStrategy
          physical-strategy: org.springframework.boot.orm.jpa.hibernate.SpringPhysicalNamingStrategyserver
6

Đối với method fetchAllLastNameByLocation sẽ tiến hành xử lý như sau :fetchAllLastNameByLocation sẽ tiến hành xử lý như sau :

spring:
  datasource:
    url: jdbc:mysql://localhost:3306/cloudsigning
    driver-class-name: com.mysql.cj.jdbc.Driver
    username: root
    password:
    jpa:
      hibernate:
        ddl-auto: create
        naming:
          implicit-strategy: org.springframework.boot.orm.jpa.hibernate.SpringImplicitNamingStrategy
          physical-strategy: org.springframework.boot.orm.jpa.hibernate.SpringPhysicalNamingStrategyserver
7

Ở đây chúng ta có một lớp gọi là Criteria . Vậy Criteria là gì ? Đây là một lớp nằm trong package org.springframework.data.mongodb.core.query cung cấp nhiều phương thức để thực hiện truy vấn như WHERE , IS , LT, GT ... Nó cũng cung cấp một cách tuần tự việc thực hiện các truy vấn đó.Criteria . Vậy Criteria là gì ? Đây là một lớp nằm trong package org.springframework.data.mongodb.core.query cung cấp nhiều phương thức để thực hiện truy vấn như WHERE , IS , LT, GT ... Nó cũng cung cấp một cách tuần tự việc thực hiện các truy vấn đó.

spring:
  data:
    mongodb:
      authentication-database: admin
      database: test
      host: localhost
      port: 27017 
8 dùng để tạo ra một criteria chứa điều kiện là các location trong database phải bằng với location từ param đã input vào từ Controller.criteria chứa điều kiện là các location trong database phải bằng với location từ param đã input vào từ Controller.

spring:
  datasource:
    url: jdbc:mysql://localhost:3306/cloudsigning
    driver-class-name: com.mysql.cj.jdbc.Driver
    username: root
    password:
    jpa:
      hibernate:
        ddl-auto: create
        naming:
          implicit-strategy: org.springframework.boot.orm.jpa.hibernate.SpringImplicitNamingStrategy
          physical-strategy: org.springframework.boot.orm.jpa.hibernate.SpringPhysicalNamingStrategyserver
8

Câu lệnh trên dùng để thực hiện tạo một Pipeline với toán tử match(criteria) với criteria đã định nghĩa phía trên và group theo lastName

Kế đến câu lệnh :

spring:
  data:
    mongodb:
      authentication-database: admin
      database: test
      host: localhost
      port: 27017 
9
spring:
  data:
    mongodb:
      authentication-database: admin
      database: test
      host: localhost
      port: 27017 
9

sử dụng mongoTemplate thao tác với database với aggregation đã định nghĩa phía trên, tên collections là Employees và trả về DTO là EmployeeDto.classaggregation đã định nghĩa phía trên, tên collections là Employees và trả về DTO là EmployeeDto.class

Cuối cùng các câu lệnh như phía dưới thực hiện tạo ra 1 list các LastName với kiểu String, lấy kết quả từ results đã trả về , lặp và trả ra list các LastNameLastName với kiểu String, lấy kết quả từ results đã trả về , lặp và trả ra list các LastName

Kết quả khi thực hiện gọi API trên bằng postman :

Hướng dẫn spring boot mongodb validation - xác thực mongodb khởi động mùa xuân

Nhìn vào log trong spring chúng ta có thể thấy dễ dàng Spring Data Mongo đã thực hiện parse các lệnh chúng ta viết phía trên ra cú pháp MongoDb theo dạng Pipeline tuần tự : match -> group và thực hiện thao tác với MongoDb.

spring:
  datasource:
    url: jdbc:mysql://localhost:3306/cloudsigning
    driver-class-name: com.mysql.cj.jdbc.Driver
    username: root
    password:
    jpa:
      hibernate:
        ddl-auto: create
        naming:
          implicit-strategy: org.springframework.boot.orm.jpa.hibernate.SpringImplicitNamingStrategy
          physical-strategy: org.springframework.boot.orm.jpa.hibernate.SpringPhysicalNamingStrategyserver
9

Đối với method countTotalUserByLocation sẽ thực hiện tính toán như sau :countTotalUserByLocation sẽ thực hiện tính toán như sau :

spring:
  data:
    mongodb:
      authentication-database: admin
      database: test
      host: localhost
      port: 27017 
0

Cách thực hiện tương tự như ví dụ trên : Thực hiện dựa tạo một Criteria để thực hiện tìm kiếm dựa trên Location và dùng Aggregation để tạo các Pipeline để thực hiện tuần tự : Group location và thực hiện Count trên từng location đó. Tuy nhiên có 1 sự khác biệt là chúng ta sẽ dùng thêm một class là AggregationOperation để xử lý riêng cho từng toán tử như match , group ... Điều này khiến cho code chúng ta clear và dễ dàng maintain hơn. Ở đây chúng ta sẽ thực hiện lấy kết quả trực tiếp từ AggregationResults bằng method getMappedResults được định nghĩa sẵng trong api của mongoDb.Aggregation để tạo các Pipeline để thực hiện tuần tự : Group location và thực hiện Count trên từng location đó. Tuy nhiên có 1 sự khác biệt là chúng ta sẽ dùng thêm một class là AggregationOperation để xử lý riêng cho từng toán tử như match , group ... Điều này khiến cho code chúng ta clear và dễ dàng maintain hơn. Ở đây chúng ta sẽ thực hiện lấy kết quả trực tiếp từ AggregationResults bằng method getMappedResults được định nghĩa sẵng trong api của mongoDb.

Kết quả khi thực hiện api : Kết quả hiển thị trên log spring : Kết quả hiển thị trên log spring :

spring:
  data:
    mongodb:
      authentication-database: admin
      database: test
      host: localhost
      port: 27017 
1

Cũng tương tự ví dụ phía trên, Spring Data MongoDb dựa trên các lệnh chúng ta đã viết thực hiện tạo các Pipeline theo tuần tự : $match theo location , $group theo location và tính tổng dựa vào $sum

Đối với method calculateSalaryByAgeAndLocation thực hiện xử lý như sau:calculateSalaryByAgeAndLocation thực hiện xử lý như sau:

spring:
  data:
    mongodb:
      authentication-database: admin
      database: test
      host: localhost
      port: 27017 
2

Đối với method calculateSalaryByAgeAndLocation sẽ thực hiện tính toán dựa trên 2 đk: age và location . Criteria sẽ thực hiện lấy những đk với age lớn hơn hoặc bằng age được input vào và location bằng location từ param được input vào. Toán tử AggregationOperation sẽ thực hiện tương tự các trình tự như match, group by location , sum dựa vào salary và thực hiện tham chiếu đến từng lastName của Employee với lệnh push .calculateSalaryByAgeAndLocation sẽ thực hiện tính toán dựa trên 2 đk: age và location . Criteria sẽ thực hiện lấy những đk với age lớn hơn hoặc bằng age được input vào và location bằng location từ param được input vào. Toán tử AggregationOperation sẽ thực hiện tương tự các trình tự như match, group by location , sum dựa vào salary và thực hiện tham chiếu đến từng lastName của Employee với lệnh push .

Kết quả :

Log :

spring:
  data:
    mongodb:
      authentication-database: admin
      database: test
      host: localhost
      port: 27017 
3

Chúng ta có thể thấy : Spring Mongo Db đã parse các câu lệnh chúng ra viết ra dạng Pipeline , với các operator match : age lớn hơn hoặc bằng 23, location DN group theo location tính tổng dựa trên salary ...

Thao tác MongoDb dựa trên Aggregation với Cursor

Qua các ví dụ trên mongoTemplate cung cấp rất nhiều tiện ích cũng như method thao tác với mongoDb. Tuy nhiên có 1 hạn chế ở đây ? MongoTemplate không thể xử lý dữ liệu vượt quá 16MB cho một BSON. Vì sao như vậy ? Có rất nhiều lý do cho vấn đề này tuy nhiên nguyên nhân chính đó là MongoDb giới hạn một BSON không vượt quá 16MB để đảm bảo rằng nó không chiếm quá nhiều RAM của hệ thống khi xử lý và đảm bảo tốc độ truyền dữ liệu khi trả về ? Vậy nếu muốn sử lý dữ liệu vượt quá 16MB chúng ta phải làm thế nào ? Câu trả lời là dùng Cursor .

Về Cursor là gì cũng như có những method nào các bạn có thể tham khảo tài liệu của MongoDb : https://docs.mongodb.com/manual/reference/method/js-cursor/?searchProperty=current&query=Cursor . Lưu ý là Cursor có các method riêng để xử lý, không thể dùng các method của collection để xử lý với Cursor.

Lưu ý : Việc sử lý dựa vào Cursor chỉ áp dụng đối với lượng data vượt quá 16MB . Nếu data nhỏ hơn 16MB, hãy dùng mongoTemplate. Dùng Cursor sẽ khiến database của bạn chậm đi đáng kể dẫn đến hiệu suất không ổn định.

Thao tác Cursor trong Spring

Trước tiên chúng ta tạo 1 method trong lớp EmployeeController như thế này :

spring:
  data:
    mongodb:
      authentication-database: admin
      database: test
      host: localhost
      port: 27017 
4

Method này sẽ trả về 1 danh sách các lastName không trùng lặp

Tiếp theo tạo một method có tên fetchAllLastNameByLocationUsedCursor trong Interface EmployeeRepositoryCustom và EmployeeRepositoryCustomImpl :

EmployeeRepositoryCustom . .

@Entity
@Document(value = "user")
public class UserAccount {
    @Id
    private Integer id;

    private String userName;
0

EmployeeRepositoryCustomImpl

Tiến hành tìm tất cả các lastName dựa trên location bằng Cursor

spring:
  data:
    mongodb:
      authentication-database: admin
      database: test
      host: localhost
      port: 27017 
5

Thay vì dùng các lớp Employee để thực hiện ánh xạ, BasicDBObject thực hiện đọc trực tiếp vào Bson với tên trường là location, MongoClient xác định các kết nối như tên host, port, tên database. Phía trên tên host là localHost,port là 27017 và tên database là BaoTrung. Ở đây chúng ta dùng lớp DBCollection để get trực tiếp collection từ MongoClient . Collection được get lên ở đây là Employees. Thực hiện tạo một Set chứa các lastName mong muốn trả về. Cursor cursor = coll.find(query); dùng để xác định sẽ dùng cursor tìm kiếm dựa trên điều kiện đã định trước. Các method như cursor.hasNext() và cursor.next() dùng để thực hiện tìm kiếm tuần tự trong các bảng ghi. Cuối cùng lastNames.add(((String) instance.get("lastName"))); sẽ tìm kiếm ra các lastName phù hợp với location đã chỉ định parse về kiểu String và trả về kết quả.BasicDBObject thực hiện đọc trực tiếp vào Bson với tên trường là location, MongoClient xác định các kết nối như tên host, port, tên database. Phía trên tên host là localHost,port là 27017 và tên database là BaoTrung. Ở đây chúng ta dùng lớp DBCollection để get trực tiếp collection từ MongoClient . Collection được get lên ở đây là Employees. Thực hiện tạo một Set chứa các lastName mong muốn trả về. Cursor cursor = coll.find(query); dùng để xác định sẽ dùng cursor tìm kiếm dựa trên điều kiện đã định trước. Các method như cursor.hasNext() và cursor.next() dùng để thực hiện tìm kiếm tuần tự trong các bảng ghi. Cuối cùng lastNames.add(((String) instance.get("lastName"))); sẽ tìm kiếm ra các lastName phù hợp với location đã chỉ định parse về kiểu String và trả về kết quả.

Kết quả khi thực hiện gọi api

Log trong spring:

spring:
  data:
    mongodb:
      authentication-database: admin
      database: test
      host: localhost
      port: 27017 
6

Nhìn vào log có thể thấy lúc nào Spring Data MongoDb đã k còn parse ra các Pipeline nữa mà việc thực hiện hoàn toàn dựa vào Cursor thao tác trên bộ nhớ của mongoDB và trả về kết quả.

Kết luận

Qua bài viết trên , mình đã giới thiệu cách sử dụng Aggregation trong spring, cách sử dụng mongoTemplate và Cursor. Sự khác nhau và khi nào nên dùng Cursor khi nào không. Hy vọng sẽ giúp mọi người hiểu thêm về Aggregation và tích hợp vào dự án. Hẹn gặp lại mọi người ở các bài viết tiếp theoAggregation trong spring, cách sử dụng mongoTemplateCursor. Sự khác nhau và khi nào nên dùng Cursor khi nào không. Hy vọng sẽ giúp mọi người hiểu thêm về Aggregation và tích hợp vào dự án. Hẹn gặp lại mọi người ở các bài viết tiếp theo