【问题标题】:How to tell Spring Data MongoDB to store nested fields of a document that are also documents in their own collection?如何告诉 Spring Data MongoDB 存储文档的嵌套字段,这些字段也是它们自己的集合中的文档?
【发布时间】:2020-11-02 10:23:18
【问题描述】:

我有两个名为 personsaddresses 的集合。这个想法是让人们在address字段中拥有一个地址。我使用 Spring Data MongoDB 来持久化那些提到的文档。

我通常在 Person > Address 之间制作“关系”的方法是存储地址的 ID 并将其提供给 person 对象。后来,当我find() 一个人时,我通过它的 id 解析地址对象,瞧,我有了我的人 + 地址。

但是我发现这有点麻烦,因为在我的代码中我只想添加整个 Address 对象,而不仅仅是它的 ID,这样我就可以使用它,同时还可以随时将它保存到存储库中。

因此,我开始了一个小单元测试,以了解 Spring Data MongoDB 如何保存 Address 对象,如果它只是 Person 的一个字段并且不是由它自己的 Repository 保存的。

这是我想出的:

import org.springframework.data.mongodb.core.mapping.Document
import org.springframework.data.mongodb.repository.MongoRepository
import org.springframework.stereotype.Repository

@Document("person")
data class Person(
    val id: String,
    val name: String,
    val age: Int,
    var address: Address
)

@Document("addresses")
data class Address(
    val id: String,
    val street: String?,
    val number: Int?
)

@Repository
interface PersonRepository : MongoRepository<Person, String>

@Repository
interface AddressRepository : MongoRepository<Address, String>

这是单元测试——正如我所料,最后一步失败了:

internal class FooTest @Autowired constructor(
    private val personRepository: PersonRepository,
    private val addressRepository: AddressRepository
) {

    @Test
    fun `some experiment`() {
        val testPerson = Person("001", "Peter", 25, Address("011","Lumberbumber", 12))
        personRepository.save(testPerson)

        val person = personRepository.findAll()[0]

        assertThat(person).isNotNull
        assertThat(person.address).isNotNull
        assertThat(person.address.street).isEqualTo("Lumberbumber")
        assertThat(person.address.number).isEqualTo(12)
        // works because address was just copied into the object structure
        // of `person` and was not seen as a standalone document

        val address = addressRepository.findAll()[0]

        assertThat(address.street).isEqualTo("Lumberbumber") // fails
        assertThat(address.number).isEqualTo(12) // fails
        // As expected `address` was not persisted alongside the `person` document.

    }
}

所以我考虑使用AbstractMongoEventListener&lt;Person&gt; 来拦截保存过程并从此处的Person 中选择Address 对象并执行addressRepository.save(addressDocument),同时将轻量级地址对象(只有ID)放回@987654335 @ 文档。 在查找 Person 并再次将 Person 和 Address 组合在一起时,我会反过来做同样的事情。

@Component
class MongoSaveInterceptor(
    val addressRepository: AddressRepository

) : AbstractMongoEventListener<Person>() {
    override fun onBeforeConvert(event: BeforeConvertEvent<Person>) {
        val personToSave = event.source
        val extractedAddress = personToSave.address
        val idOfAddress = addressRepository.save(extractedAddress).id
        personToSave.address = Address(idOfAddress, null, null)
    }

    override fun onAfterConvert(event: AfterConvertEvent<Person>) {
        val person = event.source
        val idOfAddress = person.address.id
        val foundAddress = addressRepository.findById(idOfAddress)
        foundAddress.ifPresent {
            person.address = it
        }
    }
}

它以这种方式工作,可能是满足我要求的变通解决方案。

但是

我觉得必须有类似的东西已经在工作,我可能只需要找到合适的配置。

这就是我卡在 atm 并且需要一些指导的地方。

【问题讨论】:

  • @Document("addresses") - addresses 是不同的集合?
  • 这个想法是将personaddress 分成两个单独的集合。请不要问原因,因为这是一个最小的例子,我不想用需求背后的概念细节来膨胀问题
  • 我正在阅读有关@DBRef 的信息,它看起来正是我所需要的。如果它按我需要的方式工作,我会重申一个答案。
  • 我认为设计应该是地址嵌入到个人文档中。这是文档模型的主要功能之一。不建议使用地址集合。请参阅此Data Model Design。符合相关数据存储在一起,可以一起访问的原则。
  • @prasad_ 这种方法的问题是,如果我想更改“地址”,我必须在嵌入它的每个文档中更改它以保持一致性。我知道这是关系数据库通过设计解决的问题,但由于我想要 MongoDB 的其他功能而不是文档设计,所以除了返回关系设计之外别无他法

标签: spring mongodb kotlin spring-data-mongodb


【解决方案1】:

另一项研究表明我必须使用 @DBRef (https://www.baeldung.com/cascading-with-dbref-and-lifecycle-events-in-spring-data-mongodb)。这样 Spring Data MongoDB 存储嵌入的文档类,并在从数据库加载父文档对象时解析它。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-07-26
    • 1970-01-01
    • 2021-02-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-04-13
    相关资源
    最近更新 更多