1. Tổng quan
Trong hướng dẫn này, chúng ta sẽ khám phá một số tính năng cốt lõi của Spring Data MongoDB - lập chỉ mục, chú thích phổ biến và trình chuyển đổi.
2. Chỉ mục
2.1. @Indexed
Chú thích này đánh dấu trường là đã được lập chỉ mục trong MongoDB:
@QueryEntity
@Document
public class User {
@Indexed
private String name;
...
}
Bây giờ tên trường được lập chỉ mục - hãy xem các chỉ mục trong MongoDB shell:
db.user.getIndexes();
Đây là những gì chúng tôi nhận được:
[
{
"v" : 1,
"key" : {
"_id" : 1
},
"name" : "_id_",
"ns" : "test.user"
}
]
Chúng tôi có thể ngạc nhiên khi không có dấu hiệu của tên trường ở bất cứ đâu!
Điều này là do kể từ Spring Data MongoDB 3.0, tính năng tạo chỉ mục tự động bị tắt theo mặc định .
Tuy nhiên, chúng tôi có thể thay đổi hành vi đó bằng cách ghi đè rõ ràng autoIndexCreation () trong MongoConfig của chúng tôi :
public class MongoConfig extends AbstractMongoClientConfiguration {
// rest of the config goes here
@Override
protected boolean autoIndexCreation() {
return true;
}
}
Hãy kiểm tra lại các chỉ mục trong MongoDB shell:
[
{
"v" : 1,
"key" : {
"_id" : 1
},
"name" : "_id_",
"ns" : "test.user"
},
{
"v" : 1,
"key" : {
"name" : 1
},
"name" : "name",
"ns" : "test.user"
}
]
Như chúng ta có thể thấy, lần này, chúng ta có hai chỉ mục - một trong số đó là _id - được tạo theo mặc định do @Id chú thích và chú thích thứ hai là tên của chúng tôi trường.
Ngoài ra, nếu chúng tôi sử dụng Spring Boot, chúng tôi có thể đặt spring.data.mongodb.auto-index-create tài sản thành true .
2.2. Tạo chỉ mục có lập trình
Chúng tôi cũng có thể tạo chỉ mục theo chương trình:
mongoOps.indexOps(User.class).
ensureIndex(new Index().on("name", Direction.ASC));
Bây giờ chúng tôi đã tạo chỉ mục cho trường tên và kết quả sẽ giống như trong phần trước.
2.3. Chỉ mục tổng hợp
MongoDB hỗ trợ các chỉ mục phức hợp, trong đó một cấu trúc chỉ mục duy nhất chứa các tham chiếu đến nhiều trường.
Hãy xem một ví dụ nhanh bằng cách sử dụng các chỉ mục ghép:
@QueryEntity
@Document
@CompoundIndexes({
@CompoundIndex(name = "email_age", def = "{'email.id' : 1, 'age': 1}")
})
public class User {
//
}
Chúng tôi đã tạo một chỉ mục kết hợp với email và tuổi lĩnh vực. Bây giờ chúng ta hãy kiểm tra các chỉ mục thực tế:
{
"v" : 1,
"key" : {
"email.id" : 1,
"age" : 1
},
"name" : "email_age",
"ns" : "test.user"
}
Lưu ý rằng một DBRef không thể đánh dấu trường bằng @Index - trường đó chỉ có thể là một phần của chỉ mục kết hợp.
3. Chú thích chung
3.1. @Transient
Như chúng tôi mong đợi, chú thích đơn giản này loại trừ trường được duy trì trong cơ sở dữ liệu:
public class User {
@Transient
private Integer yearOfBirth;
// standard getter and setter
}
Hãy chèn người dùng với trường cài đặt yearOfBirth :
User user = new User();
user.setName("Alex");
user.setYearOfBirth(1985);
mongoTemplate.insert(user);
Bây giờ, nếu chúng ta xem xét trạng thái của cơ sở dữ liệu, chúng ta thấy rằng yearOfBirth đã được nộp không được lưu:
{
"_id" : ObjectId("55d8b30f758fd3c9f374499b"),
"name" : "Alex",
"age" : null
}
Vì vậy, nếu chúng tôi truy vấn và kiểm tra:
mongoTemplate.findOne(Query.query(Criteria.where("name").is("Alex")), User.class).getYearOfBirth()
Kết quả sẽ là null .
3.2. @Field
@Field chỉ ra khóa được sử dụng cho trường trong tài liệu JSON:
@Field("email")
private EmailAddress emailAddress;
Bây giờ emailAddress sẽ được lưu trong cơ sở dữ liệu bằng cách sử dụng khóa email:
User user = new User();
user.setName("Brendan");
EmailAddress emailAddress = new EmailAddress();
emailAddress.setValue("[email protected]");
user.setEmailAddress(emailAddress);
mongoTemplate.insert(user);
Và trạng thái của cơ sở dữ liệu:
{
"_id" : ObjectId("55d076d80bad441ed114419d"),
"name" : "Brendan",
"age" : null,
"email" : {
"value" : "[email protected]"
}
}
3.3. @PersistenceConstructor và @Value
@PersistenceConstructor đánh dấu một phương thức khởi tạo, ngay cả một phương thức được gói bảo vệ, trở thành phương thức khởi tạo chính được sử dụng bởi logic bền vững. Các đối số của hàm tạo được ánh xạ theo tên với các giá trị khóa trong DBObject được truy xuất .
Hãy xem hàm tạo này cho Người dùng của chúng tôi lớp:
@PersistenceConstructor
public User(String name, @Value("#root.age ?: 0") Integer age, EmailAddress emailAddress) {
this.name = name;
this.age = age;
this.emailAddress = emailAddress;
}
Lưu ý việc sử dụng Spring tiêu chuẩn @Value chú thích ở đây. Với sự trợ giúp của chú thích này, chúng ta có thể sử dụng Biểu thức mùa xuân để chuyển đổi giá trị của khóa được truy xuất từ cơ sở dữ liệu trước khi nó được sử dụng để tạo một đối tượng miền. Đó là một tính năng rất mạnh mẽ và rất hữu ích ở đây.
Trong ví dụ của chúng tôi nếu tuổi chưa được đặt, nó sẽ được đặt thành 0 theo mặc định.
Bây giờ hãy xem nó hoạt động như thế nào:
User user = new User();
user.setName("Alex");
mongoTemplate.insert(user);
Cơ sở dữ liệu của chúng tôi sẽ có dạng:
{
"_id" : ObjectId("55d074ca0bad45f744a71318"),
"name" : "Alex",
"age" : null
}
Vì vậy, tuổi tác trường là null , nhưng khi chúng tôi truy vấn tài liệu và truy xuất tuổi :
mongoTemplate.findOne(Query.query(Criteria.where("name").is("Alex")), User.class).getAge();
Kết quả sẽ là 0.
4. Người chuyển đổi
Bây giờ chúng ta hãy xem xét một tính năng rất hữu ích khác trong Spring Data MongoDB - trình chuyển đổi, và cụ thể là tại MongoConverter .
Điều này được sử dụng để xử lý ánh xạ của tất cả các loại Java tới DBObjects khi lưu trữ và truy vấn các đối tượng này.
Chúng tôi có hai tùy chọn - chúng tôi có thể làm việc với MappingMongoConverter - hoặc SimpleMongoConverter trong các phiên bản trước (tính năng này không được dùng nữa trong Spring Data MongoDB M3 và chức năng của nó đã được chuyển sang MappingMongoConverter ) .
Hoặc chúng tôi có thể viết trình chuyển đổi tùy chỉnh của riêng mình. Để làm được điều đó, chúng tôi cần triển khai Công cụ chuyển đổi giao diện và đăng ký việc triển khai trong MongoConfig.
Hãy xem một ví dụ nhanh . Như chúng ta đã thấy trong một số đầu ra JSON ở đây, tất cả các đối tượng được lưu trong cơ sở dữ liệu đều có trường _class được lưu tự động. Tuy nhiên, nếu chúng tôi muốn bỏ qua trường cụ thể đó trong thời gian duy trì, chúng tôi có thể thực hiện việc đó bằng cách sử dụng MappingMongoConverter .
Đầu tiên - đây là cách triển khai trình chuyển đổi tùy chỉnh:
@Component
public class UserWriterConverter implements Converter<User, DBObject> {
@Override
public DBObject convert(User user) {
DBObject dbObject = new BasicDBObject();
dbObject.put("name", user.getName());
dbObject.put("age", user.getAge());
if (user.getEmailAddress() != null) {
DBObject emailDbObject = new BasicDBObject();
emailDbObject.put("value", user.getEmailAddress().getValue());
dbObject.put("email", emailDbObject);
}
dbObject.removeField("_class");
return dbObject;
}
}
Lưu ý cách chúng ta có thể dễ dàng đạt được mục tiêu là không kiên trì _class cụ thể bằng cách xóa trường trực tiếp tại đây.
Bây giờ chúng ta cần đăng ký trình chuyển đổi tùy chỉnh:
private List<Converter<?,?>> converters = new ArrayList<Converter<?,?>>();
@Override
public MongoCustomConversions customConversions() {
converters.add(new UserWriterConverter());
return new MongoCustomConversions(converters);
}
Tất nhiên, chúng ta cũng có thể đạt được kết quả tương tự với cấu hình XML, nếu chúng ta cần:
<bean id="mongoTemplate"
class="org.springframework.data.mongodb.core.MongoTemplate">
<constructor-arg name="mongo" ref="mongo"/>
<constructor-arg ref="mongoConverter" />
<constructor-arg name="databaseName" value="test"/>
</bean>
<mongo:mapping-converter id="mongoConverter" base-package="org.baeldung.converter">
<mongo:custom-converters base-package="com.baeldung.converter" />
</mongo:mapping-converter>
Bây giờ, khi chúng tôi lưu một người dùng mới:
User user = new User();
user.setName("Chris");
mongoOps.insert(user);
Tài liệu kết quả trong cơ sở dữ liệu không còn chứa thông tin lớp:
{
"_id" : ObjectId("55cf09790bad4394db84b853"),
"name" : "Chris",
"age" : null
}