MongoDB
 sql >> Cơ Sở Dữ Liệu >  >> NoSQL >> MongoDB

Xếp tầng tùy chỉnh trong dữ liệu mùa xuân MongoDB

1. Tổng quan

Hướng dẫn này sẽ tiếp tục khám phá một số tính năng cốt lõi của Spring Data MongoDB - @DBRef chú thích và các sự kiện trong vòng đời.

2. @DBRef

Khung ánh xạ không hỗ trợ lưu trữ quan hệ cha-con và các tài liệu nhúng trong các tài liệu khác. Những gì chúng tôi có thể làm là - chúng tôi có thể lưu trữ chúng riêng biệt và sử dụng DBRef để tham khảo các tài liệu.

Khi đối tượng được tải từ MongoDB, các tham chiếu đó sẽ được giải quyết nhanh chóng và chúng tôi sẽ lấy lại một đối tượng được ánh xạ trông giống như thể nó đã được lưu trữ nhúng trong tài liệu chính của chúng tôi.

Hãy xem một số đoạn mã:

@DBRef
private EmailAddress emailAddress;

Địa chỉ email trông giống như:

@Document
public class EmailAddress {
    @Id
    private String id;
    
    private String value;
    
    // standard getters and setters
}

Lưu ý rằng khung ánh xạ không xử lý các hoạt động xếp tầng . Vì vậy - ví dụ - nếu chúng tôi kích hoạt lưu đối với cha mẹ, đứa trẻ sẽ không được lưu tự động - chúng tôi sẽ cần phải kích hoạt lưu đứa trẻ một cách rõ ràng nếu chúng tôi cũng muốn lưu nó.

Đây chính là nơi các sự kiện trong vòng đời có ích .

3. Sự kiện vòng đời

Spring Data MongoDB xuất bản một số sự kiện vòng đời rất hữu ích - chẳng hạn như onBeforeConvert, onBeforeSave, onAfterSave, onAfterLoad onAfterConvert.

Để chặn một trong các sự kiện, chúng tôi cần đăng ký một lớp con của AbstractMappingEventListener và ghi đè một trong các phương thức tại đây. Khi sự kiện được gửi đi, trình lắng nghe của chúng tôi sẽ được gọi và đối tượng miền được chuyển vào.

3.1. Lưu tầng cơ bản

Hãy xem ví dụ mà chúng ta đã có trước đó - lưu người dùng với emailAddress . Bây giờ chúng ta có thể nghe onBeforeConvert sự kiện sẽ được gọi trước khi một đối tượng miền đi vào trình chuyển đổi:

public class UserCascadeSaveMongoEventListener extends AbstractMongoEventListener<Object> {
    @Autowired
    private MongoOperations mongoOperations;

    @Override
    public void onBeforeConvert(BeforeConvertEvent<Object> event) { 
        Object source = event.getSource(); 
        if ((source instanceof User) && (((User) source).getEmailAddress() != null)) { 
            mongoOperations.save(((User) source).getEmailAddress());
        }
    }
}

Bây giờ chúng ta chỉ cần đăng ký trình nghe vào MongoConfig :

@Bean
public UserCascadeSaveMongoEventListener userCascadingMongoEventListener() {
    return new UserCascadeSaveMongoEventListener();
}

Hoặc dưới dạng XML:

<bean class="org.baeldung.event.UserCascadeSaveMongoEventListener" />

Và chúng tôi đã hoàn thành tất cả các ngữ nghĩa theo tầng - mặc dù chỉ dành cho người dùng.

3.2. Triển khai phân tầng chung

Bây giờ chúng ta hãy cải thiện giải pháp trước đó bằng cách làm cho chức năng thác nước chung chung. Hãy bắt đầu bằng cách xác định chú thích tùy chỉnh:

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface CascadeSave {
    //
}

Bây giờ, hãy làm việc trên trình nghe tùy chỉnh của chúng tôi để xử lý các trường này một cách chung chung và không phải truyền tới bất kỳ thực thể cụ thể nào:

public class CascadeSaveMongoEventListener extends AbstractMongoEventListener<Object> {

    @Autowired
    private MongoOperations mongoOperations;

    @Override
    public void onBeforeConvert(BeforeConvertEvent<Object> event) { 
        Object source = event.getSource(); 
        ReflectionUtils.doWithFields(source.getClass(), 
          new CascadeCallback(source, mongoOperations));
    }
}

Vì vậy, chúng tôi đang sử dụng tiện ích phản chiếu của Spring và chúng tôi đang chạy lệnh gọi lại trên tất cả các trường đáp ứng tiêu chí của chúng tôi:

@Override
public void doWith(Field field) throws IllegalArgumentException, IllegalAccessException {
    ReflectionUtils.makeAccessible(field);

    if (field.isAnnotationPresent(DBRef.class) && 
      field.isAnnotationPresent(CascadeSave.class)) {
    
        Object fieldValue = field.get(getSource());
        if (fieldValue != null) {
            FieldCallback callback = new FieldCallback();
            ReflectionUtils.doWithFields(fieldValue.getClass(), callback);

            getMongoOperations().save(fieldValue);
        }
    }
}

Như bạn có thể thấy, chúng tôi đang tìm kiếm các trường có cả DBRef chú thích cũng như CascadeSave . Khi chúng tôi tìm thấy các trường này, chúng tôi sẽ lưu thực thể con.

Hãy xem xét FieldCallback lớp mà chúng tôi đang sử dụng để kiểm tra xem đứa trẻ có @Id chú thích:

public class FieldCallback implements ReflectionUtils.FieldCallback {
    private boolean idFound;

    public void doWith(Field field) throws IllegalArgumentException, IllegalAccessException {
        ReflectionUtils.makeAccessible(field);

        if (field.isAnnotationPresent(Id.class)) {
            idFound = true;
        }
    }

    public boolean isIdFound() {
        return idFound;
    }
}

Cuối cùng, để tất cả hoạt động cùng nhau, tất nhiên, chúng tôi cần emailAddress trường bây giờ được chú thích chính xác:

@DBRef
@CascadeSave
private EmailAddress emailAddress;

3.3. Kiểm tra phân tầng

Bây giờ chúng ta hãy xem xét một kịch bản - chúng tôi lưu một Người dùng với emailAddress và thao tác lưu tự động chuyển sang thực thể được nhúng này:

User user = new User();
user.setName("Brendan");
EmailAddress emailAddress = new EmailAddress();
emailAddress.setValue("[email protected]");
user.setEmailAddress(emailAddress);
mongoTemplate.insert(user);

Hãy kiểm tra cơ sở dữ liệu của chúng tôi:

{
    "_id" : ObjectId("55cee9cc0badb9271768c8b9"),
    "name" : "Brendan",
    "age" : null,
    "email" : {
        "value" : "[email protected]"
    }
}

  1. Redis
  2.   
  3. MongoDB
  4.   
  5. Memcached
  6.   
  7. HBase
  8.   
  9. CouchDB
  1. MongoDB Object.bsonSize ()

  2. tạo biểu mẫu đăng ký và đăng nhập trong node.js và mongodb

  3. xác thực tùy chỉnh mongoose sử dụng 2 trường

  4. Chọn Max () với nhóm theo mongodb

  5. Giảm hiệu suất nghiêm trọng với Luồng thay đổi MongoDB