【发布时间】:2020-11-02 10:23:18
【问题描述】:
我有两个名为 persons 和 addresses 的集合。这个想法是让人们在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<Person> 来拦截保存过程并从此处的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是不同的集合? -
这个想法是将
person和address分成两个单独的集合。请不要问原因,因为这是一个最小的例子,我不想用需求背后的概念细节来膨胀问题 -
我正在阅读有关@DBRef 的信息,它看起来正是我所需要的。如果它按我需要的方式工作,我会重申一个答案。
-
我认为设计应该是地址嵌入到个人文档中。这是文档模型的主要功能之一。不建议使用地址集合。请参阅此Data Model Design。符合相关数据存储在一起,可以一起访问的原则。
-
@prasad_ 这种方法的问题是,如果我想更改“地址”,我必须在嵌入它的每个文档中更改它以保持一致性。我知道这是关系数据库通过设计解决的问题,但由于我想要 MongoDB 的其他功能而不是文档设计,所以除了返回关系设计之外别无他法
标签: spring mongodb kotlin spring-data-mongodb