1. Ikhtisar
Tutorial ini akan terus mengeksplorasi beberapa fitur inti dari Spring Data MongoDB – @DBRef anotasi dan peristiwa siklus hidup.
2. @DBRef
Kerangka kerja pemetaan tidak mendukung menyimpan hubungan orang tua-anak dan dokumen yang disematkan di dalam dokumen lain. Apa yang dapat kami lakukan adalah – kami dapat menyimpannya secara terpisah dan menggunakan DBRef untuk merujuk ke dokumen.
Saat objek dimuat dari MongoDB, referensi tersebut akan diselesaikan dengan penuh semangat, dan kami akan mendapatkan kembali objek yang dipetakan yang terlihat sama seperti jika telah disimpan di dalam dokumen master kami.
Mari kita lihat beberapa kode:
@DBRef
private EmailAddress emailAddress;
Alamat Email terlihat seperti:
@Document
public class EmailAddress {
@Id
private String id;
private String value;
// standard getters and setters
}
Perhatikan bahwa kerangka kerja pemetaan tidak menangani operasi bertingkat . Jadi – misalnya – jika kita memicu simpan pada orang tua, anak tidak akan disimpan secara otomatis – kita harus secara eksplisit memicu penyimpanan pada anak jika kita ingin menyimpannya juga.
Di sinilah peristiwa siklus hidup berguna .
3. Peristiwa Siklus Hidup
Spring Data MongoDB menerbitkan beberapa peristiwa siklus hidup yang sangat berguna – seperti onBeforeConvert, onBeforeSave, onAfterSave, onAfterLoad dan onAfterConvert.
Untuk mencegat salah satu peristiwa, kita perlu mendaftarkan subkelas AbstractMappingEventListener dan timpa salah satu metode di sini. Saat acara dikirim, pendengar kami akan dipanggil dan objek domain diteruskan.
3.1. Penghematan Kaskade Dasar
Mari kita lihat contoh yang kita miliki sebelumnya – menyimpan pengguna dengan alamat email . Sekarang kita dapat mendengarkan onBeforeConvert event yang akan dipanggil sebelum objek domain masuk ke konverter:
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());
}
}
}
Sekarang kita hanya perlu mendaftarkan pendengar ke MongoConfig :
@Bean
public UserCascadeSaveMongoEventListener userCascadingMongoEventListener() {
return new UserCascadeSaveMongoEventListener();
}
Atau sebagai XML:
<bean class="org.baeldung.event.UserCascadeSaveMongoEventListener" />
Dan kami telah menyelesaikan semantik berjenjang – meskipun hanya untuk pengguna.
3.2. Implementasi Kaskade Umum
Sekarang mari kita tingkatkan solusi sebelumnya dengan membuat fungsionalitas kaskade menjadi generik. Mari kita mulai dengan mendefinisikan anotasi khusus:
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface CascadeSave {
//
}
Sekarang mari kita mengerjakan pendengar khusus untuk menangani bidang ini secara umum dan tidak harus melakukan transmisi ke entitas tertentu:
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));
}
}
Jadi kami menggunakan utilitas refleksi dari Spring, dan kami menjalankan panggilan balik kami di semua bidang yang memenuhi kriteria kami:
@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);
}
}
}
Seperti yang Anda lihat, kami mencari bidang yang memiliki DBRef anotasi serta CascadeSave . Setelah kami menemukan bidang ini, kami menyimpan entitas anak.
Mari kita lihat FieldCallback kelas yang kami gunakan untuk memeriksa apakah anak memiliki @Id anotasi:
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;
}
}
Terakhir, untuk membuat semuanya bekerja sama, kita tentu saja perlu emailAddress bidang untuk sekarang dianotasi dengan benar:
@DBRef
@CascadeSave
private EmailAddress emailAddress;
3.3. Tes Kaskade
Sekarang mari kita lihat skenario – kita menyimpan Pengguna dengan alamat email , dan operasi simpan mengalir ke entitas tersemat ini secara otomatis:
User user = new User();
user.setName("Brendan");
EmailAddress emailAddress = new EmailAddress();
emailAddress.setValue("[email protected]");
user.setEmailAddress(emailAddress);
mongoTemplate.insert(user);
Yuk cek database kita:
{
"_id" : ObjectId("55cee9cc0badb9271768c8b9"),
"name" : "Brendan",
"age" : null,
"email" : {
"value" : "[email protected]"
}
}