1. Tổng quan
Trong hướng dẫn này, chúng ta sẽ hiểu cách sử dụng Morphia, một Object Document Mapper (ODM) cho MongoDB trong Java.
Trong quá trình này, chúng tôi cũng sẽ hiểu ODM là gì và cách nó tạo điều kiện để làm việc với MongoDB.
2. ODM là gì ?
Đối với những người chưa bắt đầu trong lĩnh vực này, MongoDB là cơ sở dữ liệu hướng tài liệu được xây dựng để phân phối theo bản chất . Nói một cách đơn giản, cơ sở dữ liệu hướng tài liệu quản lý tài liệu, không có gì khác ngoài cách tổ chức dữ liệu bán cấu trúc không có giản đồ . Chúng thuộc phạm vi rộng hơn và được xác định lỏng lẻo của cơ sở dữ liệu NoSQL, được đặt tên theo sự xuất phát rõ ràng của chúng khỏi tổ chức cơ sở dữ liệu SQL truyền thống.
MongoDB cung cấp trình điều khiển cho hầu hết các ngôn ngữ lập trình phổ biến như Java . Các trình điều khiển này cung cấp một lớp trừu tượng để làm việc với MongoDB để chúng tôi không làm việc trực tiếp với Wire Protocol. Hãy coi điều này giống như việc Oracle cung cấp triển khai trình điều khiển JDBC cho cơ sở dữ liệu quan hệ của họ.
Tuy nhiên, nếu nhớ lại những ngày làm việc trực tiếp với JDBC, chúng ta có thể đánh giá cao việc nó có thể trở nên lộn xộn như thế nào - đặc biệt là trong mô hình hướng đối tượng. May mắn thay, chúng tôi có các khung công tác Lập bản đồ quan hệ đối tượng (ORM) như Hibernate để giải cứu. Nó không khác lắm đối với MongoDB.
Mặc dù chúng tôi chắc chắn có thể làm việc với trình điều khiển cấp thấp, nhưng nó đòi hỏi nhiều bản soạn thảo hơn để hoàn thành nhiệm vụ. Ở đây, chúng tôi có một khái niệm tương tự với ORM được gọi là Trình lập bản đồ tài liệu đối tượng (ODM) . Morphia lấp đầy chính xác không gian đó cho ngôn ngữ lập trình Java và hoạt động trên trình điều khiển Java cho MongoDB.
3. Thiết lập phụ thuộc
Chúng tôi đã thấy đủ lý thuyết để đưa chúng tôi vào một số mã. Đối với các ví dụ của chúng tôi, chúng tôi sẽ lập mô hình thư viện sách và xem cách chúng tôi có thể quản lý nó trong MongoDB bằng cách sử dụng Morphia.
Nhưng trước khi bắt đầu, chúng ta sẽ cần thiết lập một số phụ thuộc.
3.1. MongoDB
Chúng ta cần có một phiên bản MongoDB đang chạy để làm việc. Có một số cách để có được điều này và cách đơn giản nhất là tải xuống và cài đặt phiên bản cộng đồng trên máy cục bộ của chúng tôi.
Chúng ta nên để nguyên tất cả các cấu hình mặc định, bao gồm cả cổng mà MongoDB chạy trên đó.
3.2. Morphia
Chúng tôi có thể tải xuống các JAR được tạo sẵn cho Morphia từ Maven Central và sử dụng chúng trong dự án Java của chúng tôi.
Tuy nhiên, cách đơn giản nhất là sử dụng một công cụ quản lý phụ thuộc như Maven:
<dependency>
<groupId>dev.morphia.morphia</groupId>
<artifactId>core</artifactId>
<version>1.5.3</version>
</dependency>
4. Làm thế nào để kết nối bằng Morphia?
Bây giờ chúng tôi đã cài đặt và chạy MongoDB và đã thiết lập Morphia trong dự án Java của mình, chúng tôi đã sẵn sàng kết nối với MongoDB bằng cách sử dụng Morphia.
Hãy xem chúng ta có thể thực hiện điều đó như thế nào:
Morphia morphia = new Morphia();
morphia.mapPackage("com.baeldung.morphia");
Datastore datastore = morphia.createDatastore(new MongoClient(), "library");
datastore.ensureIndexes();
Nó khá là nhiều! Hãy hiểu điều này tốt hơn. Chúng tôi cần hai thứ để hoạt động lập bản đồ của chúng tôi hoạt động:
- Người lập bản đồ:Điều này chịu trách nhiệm ánh xạ các POJO Java của chúng tôi với Bộ sưu tập MongoDB . Trong đoạn mã của chúng tôi ở trên, Morphia là lớp chịu trách nhiệm về điều đó. Lưu ý cách chúng tôi định cấu hình gói nơi nó sẽ tìm các POJO của chúng tôi.
- Kết nối:Đây là kết nối đến cơ sở dữ liệu MongoDB mà trên đó trình ánh xạ có thể thực thi các hoạt động khác nhau. Lớp Kho dữ liệu nhận dưới dạng tham số, một phiên bản của MongoClient (từ trình điều khiển Java MongoDB) và tên của cơ sở dữ liệu MongoDB, trả về kết nối hoạt động để làm việc với .
Vì vậy, chúng tôi đã sẵn sàng sử dụng Kho dữ liệu này và làm việc với các tổ chức của chúng tôi.
5. Làm thế nào để làm việc với các đối tượng?
Trước khi chúng tôi có thể sử dụng Kho dữ liệu mới đúc của mình , chúng tôi cần xác định một số thực thể miền để làm việc với.
5.1. Thực thể Đơn giản
Hãy bắt đầu bằng cách xác định một Sách đơn giản thực thể có một số thuộc tính:
@Entity("Books")
public class Book {
@Id
private String isbn;
private String title;
private String author;
@Property("price")
private double cost;
// constructors, getters, setters and hashCode, equals, toString implementations
}
Có một số điều thú vị cần lưu ý ở đây:
- Lưu ý đến chú thích @ Thực thể đủ điều kiện POJO này cho ánh xạ ODM bởi Morphia
- Morphia, theo mặc định, ánh xạ một thực thể tới một bộ sưu tập trong MongoDB theo tên của lớp của nó, nhưng chúng tôi có thể ghi đè rõ ràng điều này (giống như chúng tôi đã làm cho thực thể Sách tại đây)
- Morphia, theo mặc định, ánh xạ các biến trong một thực thể với các khóa trong bộ sưu tập MongoDB theo tên của biến, nhưng một lần nữa chúng tôi có thể ghi đè điều này (giống như chúng tôi đã làm đối với biến cost tại đây)
- Cuối cùng, chúng tôi cần đánh dấu một biến trong thực thể để hoạt động như khóa chính bằng chú thích @ Id (giống như chúng tôi đang sử dụng ISBN cho cuốn sách của mình ở đây)
5.2. Các thực thể có mối quan hệ
Tuy nhiên, trong thế giới thực, các thực thể hầu như không đơn giản như vẻ bề ngoài và có mối quan hệ phức tạp với nhau. Ví dụ:thực thể đơn giản của chúng tôi Sách có thể có một Nhà xuất bản và có thể tham khảo các sách đồng hành khác. Làm thế nào để chúng tôi mô hình hóa chúng?
MongoDB cung cấp hai cơ chế để xây dựng mối quan hệ - Tham chiếu và Nhúng . Như tên cho thấy, với việc tham chiếu, MongoDB lưu trữ dữ liệu liên quan dưới dạng một tài liệu riêng biệt trong cùng một bộ sưu tập hoặc một bộ sưu tập khác và chỉ tham chiếu nó bằng cách sử dụng id của nó.
Ngược lại, với việc nhúng, MongoDB lưu trữ hoặc đúng hơn là nhúng mối quan hệ vào chính tài liệu gốc.
Hãy xem chúng ta có thể sử dụng chúng như thế nào. Hãy bắt đầu bằng cách nhúng Nhà xuất bản trong Sách của chúng tôi :
@Embedded
private Publisher publisher;
Đủ đơn giản. Bây giờ chúng ta hãy tiếp tục và thêm tài liệu tham khảo cho các sách khác:
@Reference
private List<Book> companionBooks;
Vậy là xong - Morphia cung cấp các chú thích thuận tiện để mô hình hóa các mối quan hệ do MongoDB hỗ trợ. Tuy nhiên, lựa chọn tham chiếu so với nhúng phải dựa trên độ phức tạp, dự phòng và nhất quán của mô hình dữ liệu trong số các cân nhắc khác.
Bài tập tương tự như chuẩn hóa trong cơ sở dữ liệu quan hệ.
Bây giờ, chúng tôi đã sẵn sàng thực hiện một số thao tác trên Sách sử dụng Kho dữ liệu .
6. Một số hoạt động cơ bản
Hãy xem cách làm việc với một số thao tác cơ bản khi sử dụng Morphia.
6.1. Lưu
Hãy bắt đầu với các thao tác đơn giản nhất, tạo một bản sao của Sách trong cơ sở dữ liệu MongoDB thư viện của chúng tôi :
Publisher publisher = new Publisher(new ObjectId(), "Awsome Publisher");
Book book = new Book("9781565927186", "Learning Java", "Tom Kirkman", 3.95, publisher);
Book companionBook = new Book("9789332575103", "Java Performance Companion",
"Tom Kirkman", 1.95, publisher);
book.addCompanionBooks(companionBook);
datastore.save(companionBook);
datastore.save(book);
Điều này đủ để cho phép Morphia tạo một bộ sưu tập trong cơ sở dữ liệu MongoDB của chúng tôi, nếu nó không tồn tại và thực hiện thao tác nâng cấp.
6.2. Truy vấn
Hãy xem liệu chúng tôi có thể truy vấn cuốn sách chúng tôi vừa tạo trong MongoDB không:
List<Book> books = datastore.createQuery(Book.class)
.field("title")
.contains("Learning Java")
.find()
.toList();
assertEquals(1, books.size());
assertEquals(book, books.get(0));
Truy vấn tài liệu trong Morphia bắt đầu bằng việc tạo truy vấn bằng Kho dữ liệu và sau đó thêm các bộ lọc một cách khai báo, để làm hài lòng những người yêu thích lập trình chức năng!
Morphia hỗ trợ xây dựng truy vấn phức tạp hơn nhiều với các bộ lọc và toán tử. Hơn nữa, Morphia cho phép giới hạn, bỏ qua và sắp xếp thứ tự các kết quả trong truy vấn.
Hơn nữa, Morphia cho phép chúng tôi sử dụng các truy vấn thô được viết bằng trình điều khiển Java cho MongoDB để kiểm soát nhiều hơn, nếu cần.
6.3. Cập nhật
Mặc dù thao tác lưu có thể xử lý các bản cập nhật nếu khóa chính khớp, Morphia cung cấp các cách để cập nhật tài liệu một cách có chọn lọc:
Query<Book> query = datastore.createQuery(Book.class)
.field("title")
.contains("Learning Java");
UpdateOperations<Book> updates = datastore.createUpdateOperations(Book.class)
.inc("price", 1);
datastore.update(query, updates);
List<Book> books = datastore.createQuery(Book.class)
.field("title")
.contains("Learning Java")
.find()
.toList();
assertEquals(4.95, books.get(0).getCost());
Ở đây, chúng tôi đang xây dựng một truy vấn và thao tác cập nhật để tăng một giá của tất cả các sách mà truy vấn trả về.
6.4. Xóa
Cuối cùng, cái đã được tạo phải bị xóa! Một lần nữa, với Morphia, nó khá trực quan:
Query<Book> query = datastore.createQuery(Book.class)
.field("title")
.contains("Learning Java");
datastore.delete(query);
List<Book> books = datastore.createQuery(Book.class)
.field("title")
.contains("Learning Java")
.find()
.toList();
assertEquals(0, books.size());
Chúng tôi tạo truy vấn tương tự như trước đây và chạy thao tác xóa trên Kho dữ liệu .
7. Sử dụng nâng cao
MongoDB có một số hoạt động nâng cao như Tổng hợp, Lập chỉ mục và nhiều hoạt động khác . Mặc dù không thể thực hiện tất cả những điều đó bằng cách sử dụng Morphia, nhưng bạn chắc chắn có thể đạt được một số điều đó. Đối với những người khác, thật đáng buồn, chúng tôi sẽ phải quay lại trình điều khiển Java cho MongoDB.
Hãy tập trung vào một số hoạt động nâng cao mà chúng ta có thể thực hiện thông qua Morphia.
7.1. Tổng hợp
Tính năng tổng hợp trong MongoDB cho phép chúng tôi xác định một loạt hoạt động trong một quy trình có thể hoạt động trên một tập hợp tài liệu và tạo ra kết quả tổng hợp .
Morphia có một API để hỗ trợ một đường dẫn tổng hợp như vậy.
Giả sử chúng tôi muốn tổng hợp dữ liệu thư viện của mình theo cách mà chúng tôi có tất cả các sách được nhóm theo tác giả của chúng:
Iterator<Author> iterator = datastore.createAggregation(Book.class)
.group("author", grouping("books", push("title")))
.out(Author.class);
Vì vậy, làm thế nào để điều này làm việc? Chúng tôi bắt đầu bằng cách tạo một đường dẫn tổng hợp sử dụng cùng một Kho dữ liệu cũ . Chúng tôi phải cung cấp pháp nhân mà chúng tôi muốn thực hiện các hoạt động tổng hợp, chẳng hạn như Sách tại đây.
Tiếp theo, chúng tôi muốn nhóm các tài liệu theo “tác giả” và tổng hợp “tên sách” của chúng dưới một khóa gọi là “sách”. Cuối cùng, chúng tôi đang làm việc với ODM ở đây. Vì vậy, chúng tôi phải xác định một thực thể để thu thập dữ liệu tổng hợp của chúng tôi - trong trường hợp của chúng tôi, đó là Tác giả .
Tất nhiên, chúng ta phải xác định một thực thể có tên là Tác giả với một biến được gọi là books:
@Entity
public class Author {
@Id
private String name;
private List<String> books;
// other necessary getters and setters
}
Tất nhiên, điều này chỉ làm trầy xước bề mặt của một cấu trúc rất mạnh mẽ do MongoDB cung cấp và có thể được khám phá thêm để biết thêm chi tiết.
7.2. Phép chiếu
Phép chiếu trong MongoDB cho phép chúng tôi chỉ chọn các trường chúng tôi muốn tìm nạp từ tài liệu trong truy vấn của mình . Trong trường hợp cấu trúc tài liệu phức tạp và nặng, điều này có thể thực sự hữu ích khi chúng ta chỉ cần một vài trường.
Giả sử chúng ta chỉ cần tìm nạp sách có tên sách trong truy vấn của chúng ta:
List<Book> books = datastore.createQuery(Book.class)
.field("title")
.contains("Learning Java")
.project("title", true)
.find()
.toList();
assertEquals("Learning Java", books.get(0).getTitle());
assertNull(books.get(0).getAuthor());
Ở đây, như chúng ta thấy, chúng tôi chỉ lấy lại tiêu đề trong kết quả của chúng tôi chứ không phải tác giả và các trường khác. Tuy nhiên, chúng ta nên cẩn thận trong việc sử dụng đầu ra dự kiến để lưu trở lại MongoDB. Điều này có thể dẫn đến mất dữ liệu!
7.3. Lập chỉ mục
Các chỉ mục đóng một vai trò rất quan trọng trong việc tối ưu hóa truy vấn với cơ sở dữ liệu - quan hệ cũng như nhiều cơ sở dữ liệu không quan hệ.
MongoDB xác định chỉ mục ở cấp bộ sưu tập với chỉ mục duy nhất được tạo trên khóa chính theo mặc định . Hơn nữa, MongoDB cho phép tạo chỉ mục trên bất kỳ trường hoặc trường con nào trong tài liệu. Chúng ta nên chọn tạo chỉ mục trên một khóa tùy thuộc vào truy vấn mà chúng ta muốn tạo.
Ví dụ:trong ví dụ của chúng tôi, chúng tôi có thể muốn tạo một chỉ mục trên trường “tiêu đề” của Sách như chúng tôi thường kết thúc truy vấn về nó:
@Indexes({
@Index(
fields = @Field("title"),
options = @IndexOptions(name = "book_title")
)
})
public class Book {
// ...
@Property
private String title;
// ...
}
Tất nhiên, chúng tôi có thể chuyển các tùy chọn lập chỉ mục bổ sung để điều chỉnh các sắc thái của chỉ mục được tạo. Lưu ý rằng trường phải được chú thích bởi @ Thuộc tính được sử dụng trong một chỉ mục.
Hơn nữa, ngoài chỉ mục cấp độ lớp, Morphia còn có chú thích để xác định chỉ mục cấp độ trường.
7.4. Xác thực giản đồ
Chúng tôi có một tùy chọn để cung cấp quy tắc xác thực dữ liệu cho một tập hợp mà MongoDB có thể sử dụng trong khi thực hiện cập nhật hoặc thao tác chèn . Morphia hỗ trợ điều này thông qua các API của họ.
Giả sử rằng chúng tôi không muốn chèn một cuốn sách mà không có giá hợp lệ. Chúng tôi có thể tận dụng xác thực lược đồ để đạt được điều này:
@Validation("{ price : { $gt : 0 } }")
public class Book {
// ...
@Property("price")
private double cost;
// ...
}
Có một bộ xác thực phong phú do MongoDB cung cấp có thể được sử dụng tại đây.
8. Các ODM MongoDB thay thế
Morphia không phải là MongoDB ODM duy nhất dành cho Java. Có một số khác mà chúng tôi có thể xem xét để sử dụng trong các ứng dụng của mình. Không thể thảo luận về việc so sánh với Morphia ở đây, nhưng luôn hữu ích nếu biết các lựa chọn của chúng tôi:
- Dữ liệu mùa xuân:Cung cấp mô hình lập trình dựa trên mùa xuân để làm việc với MongoDB
- MongoJack:Cung cấp ánh xạ trực tiếp từ JSON đến các đối tượng MongoDB
Đây không phải là danh sách đầy đủ các ODM MongoDB cho Java, nhưng có một số thay thế thú vị có sẵn!