3

Trong Phần 2 của loạt bài này, chúng tôi sẽ cấu trúc lại ứng dụng được viết trong Phần 1 để sử dụng cơ sở dữ liệu. Chúng tôi sẽ xem xét ngắn gọn các lựa chọn mà chúng tôi có khi chọn cơ sở dữ liệu kết hợp với Spring WebFlux, sử dụng phiên bản nhúng của cơ sở dữ liệu, cấu trúc lại các nguồn và tìm giải pháp cho các vấn đề chúng tôi gặp phải. Mã này có thể được tìm thấy tại GitHub trong nhánh mongodb .

Chọn một cơ sở dữ liệu

Khi chúng tôi xem qua Spring Initializr , chúng tôi nhận thấy rằng chỉ có cơ sở dữ liệu NoQuery có hỗ trợ phản ứng. Tại thời điểm viết này là:

  • Phản ứng Redis
  • MongoDB phản ứng
  • Cassandra phản ứng
  • Couchbase phản ứng

Tại sao chỉ có cơ sở dữ liệu NoQuery có hỗ trợ phản ứng chứ không phải cơ sở dữ liệu quan hệ truyền thống? Câu trả lời khá đơn giản: cơ sở dữ liệu quan hệ sử dụng JDBC và JDBC là một API chặn. Ngoài ra, chúng tôi sử dụng các giao dịch cơ sở dữ liệu và các tài nguyên khối này không phản ứng nhiều. Tuy nhiên, có một số sáng kiến ​​để phát triển trình điều khiển không đồng bộ cho cơ sở dữ liệu quan hệ. Nhưng hiện tại, chỉ có cơ sở dữ liệu NoQuery được hỗ trợ chính thức vì chúng phù hợp nhất trong thế giới phản ứng. Kết quả là, không có hỗ trợ phản ứng cho Hibernate hoặc JPA.

Trong ví dụ của chúng tôi, chúng tôi sẽ chọn Reactive MongoDB và cụ thể hơn là cho phiên bản nhúng, vì điều này sẽ cho phép chúng tôi chạy và kiểm tra ứng dụng mà không cần phải cài đặt MongoDB.

Trong các phần sau, chúng tôi sẽ thực hiện các bước tái cấu trúc nhỏ và kiểm tra xem ứng dụng có còn biên dịch và khởi động mà không có bất kỳ lỗi nào không.

Thích ứng pom.xml

Trước hết, chúng ta sẽ loại bỏ sự phụ thuộc vào lò xo dữ liệu chung. Chúng tôi đã thêm phụ thuộc này để sử dụng  giao diện ReactiveCrudRep repository  .

<dependency>
  <groupId>org.springframework.data</groupId>
  <artifactId>spring-data-commons</artifactId>
  <version>2.0.5.RELEASE</version>
</dependency>

Tiếp theo, chúng tôi thêm một phụ thuộc cho Reactive MongoDB, bao gồm spring-data-mongodb và trình điều khiển phản ứng:

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-data-mongodb-reactive</artifactId>
</dependency>

Để sử dụng MongoDB được nhúng, chúng tôi thêm phụ thuộc vào MongoDB được nhúng:

<dependency>
  <groupId>de.flapdoodle.embed</groupId>
  <artifactId>de.flapdoodle.embed.mongo</artifactId>
  <scope>runtime</scope>
</dependency>

Chạy ứng dụng với spring-boot: chạy và ứng dụng vẫn khởi động thành công.

Điều chỉnh kho lưu trữ

Đối với kho lưu trữ, bây giờ chúng ta có thể sử dụng giao diện ReactiveMongoRep repository  . Mã của chúng tôi là như sau:

public class ReactiveShowRepository implements ReactiveCrudRepository<Show, String> {
  // a lot of code
}

Và bây giờ trở thành:

@Repository
public interface ReactiveShowRepository extends ReactiveMongoRepository<Show, String> {

}

Vì vậy, chúng tôi chỉ cần mở rộng giao diện và sau đó, tự động, kho lưu trữ của chúng tôi hoạt động? Vâng, nó làm. Spring Boot sẽ tự động cắm vào một triển khai ReactiveShowRep repository dựa trên lớp SimpleReactiveMongoRep repository khi chạy. Lớp này cung cấp các phương thức CRUD phổ biến nhất.

Chúng tôi cũng đã thêm phần sau vào module-info.java:

requires spring.data.mongodb;

Chạy ứng dụng, URL của chúng tôi,  http: // localhost: 8080 / , không trả về gì, điều mà chúng tôi mong đợi vì chúng tôi không có gì trong cơ sở dữ liệu của chúng tôi.

Điều chỉnh đối tượng miền hiển thị

Chúng tôi chú thích đối tượng miền Hiển thị của chúng tôi dưới dạng Tài liệu và xóa hàm tạo mà chúng tôi đã tạo.

@Document
public class Show {
  @Id
  private String id;
  private String title;
  ...
}

Điền vào cơ sở dữ liệu

Mọi thứ hiện đang hoạt động, nhưng chúng tôi cần một số dữ liệu trong cơ sở dữ liệu của chúng tôi. Để làm như vậy, tôi sẽ tạo một lớp  DataImportConfiguration như được trình bày trong hội thảo trực tuyến Spring Boot của Phil Webb. Lớp này đọc một tệp YAML chứa dữ liệu, chuyển đổi nó thành các thuộc tính và chuyển đổi nó thành một danh sách các đối tượng Show . Khi ứng dụng được khởi động, dữ liệu sẽ được đưa vào cơ sở dữ liệu.

Vì chúng tôi đang sử dụng YamlProperIESFactoryBean , chúng tôi cũng thêm phụ thuộc vào spring.beans trong module-info.java của chúng tôi .

Lớp  DataImportConfiguration như sau:

@Configuration
public class DataImportConfiguration {

  @Bean
  public CommandLineRunner initData(MongoOperations mongo) {
    return (String... args) -> {
      mongo.dropCollection(Show.class);
      mongo.createCollection(Show.class);
      getShows().forEach(mongo::save);
    };
  }

  private List<Show> getShows() {
    Properties yaml = loadShowsYaml();
    MapConfigurationPropertySource source = new MapConfigurationPropertySource(yaml);
    return new Binder(source).bind("shows", Bindable.listOf(Show.class)).get();
  }

  private Properties loadShowsYaml() {
    YamlPropertiesFactoryBean properties = new YamlPropertiesFactoryBean();
    properties.setResources(new ClassPathResource("shows.yml"));
    return properties.getObject();
  }

}

Chúng tôi tạo tệp show.yml sau  vào thư mục tài nguyên của chúng tôi:

shows:
- title: "Title 1"
- title: "Title 2"
- title: "Title 3"
- title: "Title 4"
- title: "Title 5"

Chạy ứng dụng và gọi  http: // localhost: 8080 / chương trình . Đầu ra sau đây được hiển thị trong trình duyệt của chúng tôi:

[{"id": "5aad24d7c568b82764d592e8","title": "Title 1"},
{"id": "5aad24d7c568b82764d592e9","title": "Title 2"},
{"id": "5aad24d7c568b82764d592ea","title": "Title 3"},
{"id": "5aad24d7c568b82764d592eb","title": "Title 4"},
{"id": "5aad24d7c568b82764d592ec","title": "Title 5"}]

Gọi URL  http: // localhost: 8080 / shows / 5aad24d7c568b82764d592e8 để lấy dữ liệu cho một chương trình. Đầu ra là:

{
"id": "5aad24d7c568b82764d592e8",
"title": "Title 1"
}

Và chúng tôi có thể gọi URL để truy xuất các sự kiện cho một chương trình: http: // localhost: 8080 / shows / 5aad24d7c568b82764d592e8 / event . Mỗi giây một sự kiện được thêm vào đầu ra:

data:{"id":"5aad24d7c568b82764d592e8","date":1521296867227}

data:{"id":"5aad24d7c568b82764d592e8","date":1521296868263}

data:{"id":"5aad24d7c568b82764d592e8","date":1521296869297}

data:{"id":"5aad24d7c568b82764d592e8","date":1521296870307}

data:{"id":"5aad24d7c568b82764d592e8","date":1521296871335}

Tại thời điểm này, chúng tôi đã cấu trúc lại ứng dụng của mình để sử dụng MongoDB được nhúng và các điểm cuối của chúng tôi vẫn hoạt động như mong đợi.

Điều chỉnh bài kiểm tra đơn vị

Điều cuối cùng cần kiểm tra là liệu bài kiểm tra đơn vị của chúng tôi có còn hoạt động hay không. Chạy thử nghiệm đơn vị  MySpringWebfluxCrudPlanetApplicationTests từ trong IDE của bạn. Tôi đã dự kiến ​​rằng bài kiểm tra đơn vị vẫn hoạt động vì chúng tôi không thay đổi bất cứ điều gì đối với logic ứng dụng. Tuy nhiên, 100 lỗi đã được hiển thị! Dưới đây là một đoạn mã, nhưng tất cả các lỗi đã cho tương tự như các lỗi được hiển thị bên dưới:

Error:java: the unnamed module reads package com.mongodb from both mongodb.driver.core and mongodb.driver
Error:java: the unnamed module reads package com.mongodb.client from both mongodb.driver.core and mongodb.driver
...

Sau một số điều tra, hóa ra những lỗi này xảy ra do chúng tôi đang sử dụng các mô-đun Java 9. Vô hiệu hóa việc sử dụng Java 9 module bằng cách đổi tên các module-info.java để mô-đun-info.java_ và chạy thử nghiệm một lần nữa. Kết quả kiểm tra hiện đã thành công.

Vậy chuyện gì đã xảy ra? Lý do cho lỗi được gọi là 'các gói tách': hai gói có cùng tên tồn tại trong các mô-đun khác nhau và điều này không được phép, ngoại trừ các mô-đun không tên.

Hãy xem xét vấn đề. Mongodb-driver-3.6.3.jar chứa các gói sau và chúng tôi nhận thấy rằng jar này không có bộ mô tả mô-đun, do đó mô-đun tự động được tạo.

No module descriptor found. Derived automatic module.

mongodb.driver@3.6.3 automatic
requires java.base mandated
contains com.mongodb
contains com.mongodb.client
contains com.mongodb.client.gridfs
contains com.mongodb.client.jndi
contains com.mongodb.client.model
contains com.mongodb.gridfs
contains com.mongodb.util
contains org.bson
contains org.bson.io
contains org.bson.types
contains org.bson.util

Chúng tôi nhận được một đầu ra tương tự cho mongodb-driver-core-3.6.3.jar.

No module descriptor found. Derived automatic module.

mongodb.driver.core@3.6.3 automatic
requires java.base mandated
contains com.mongodb
contains com.mongodb.annotations
contains com.mongodb.assertions
contains com.mongodb.async
contains com.mongodb.binding
contains com.mongodb.bulk
contains com.mongodb.client
contains com.mongodb.client.gridfs.codecs
contains com.mongodb.client.gridfs.model
contains com.mongodb.client.model
contains com.mongodb.client.model.changestream
contains com.mongodb.client.model.geojson
contains com.mongodb.client.model.geojson.codecs
contains com.mongodb.client.result
contains com.mongodb.connection
contains com.mongodb.connection.netty
contains com.mongodb.diagnostics.logging
contains com.mongodb.event
contains com.mongodb.internal
contains com.mongodb.internal.async
contains com.mongodb.internal.authentication
contains com.mongodb.internal.connection
contains com.mongodb.internal.dns
contains com.mongodb.internal.event
contains com.mongodb.internal.management.jmx
contains com.mongodb.internal.session
contains com.mongodb.internal.thread
contains com.mongodb.internal.validator
contains com.mongodb.management
contains com.mongodb.operation
contains com.mongodb.selector
contains com.mongodb.session

Chúng ta có thể thấy rằng các gói com.mongodbcom.mongodb.client có mặt trong các mô-đun tự động mongodb.drivermongodb.driver.core . Nhưng, chúng là các mô-đun không tên và trong trường hợp đó, các gói phân chia được cho phép. Do đó, điều kỳ lạ là các lỗi xảy ra khi chạy thử nghiệm từ IDE.

Chạy thử nghiệm với  module-info.java và bằng phương pháp thử nghiệm mục tiêu Maven  là thành công. Do đó, tôi đoán có gì đó không ổn với cấu hình chạy IntelliJ của tôi, nhưng tôi chưa tìm ra cái gì.

Tóm lược

Trong bài đăng này, chúng tôi đã cấu trúc lại ứng dụng CRUD cơ bản từ Phần 1 để sử dụng cơ sở dữ liệu MongoDB được nhúng. Chúng tôi đã sử dụng  YamlProperIESFactoryBean  để ban đầu điền vào cơ sở dữ liệu và gặp phải sự cố khi chạy thử nghiệm đơn vị.

|