Kotlin 数据类:如何为嵌入式文档生成 MongoDB ObjectId

Posted

技术标签:

【中文标题】Kotlin 数据类:如何为嵌入式文档生成 MongoDB ObjectId【英文标题】:Kotlin Data Class: How generate an MongoDB ObjectId for an embedded document 【发布时间】:2019-04-14 01:40:28 【问题描述】:

我正在使用 Spring Boot 和 Kotlin 构建 API。我正在尝试在 MongoDB 中通过以下方式生成结构。

我知道在MongoDb中不存在实体间关系的概念,所以我会使用嵌入文档的策略。也就是说,将 Reunión 嵌入 Proyecto,将 Participant 嵌入 Reunión。

我有一个名为ProyectoNewProyecto 的主类,其中包含NewReunion 类型的重聚列表作为属性。我使用两个不同的类来创建和返回数据。

Proyecto.kt

@Document(collection = "proyectos")
@TypeAlias("proyecto")
data class Proyecto (
        @Id
        val id: String,
        val nombre: String,
        val area: String,
        val fecha:String,
        val reuniones: List<Reunion>?
)

@Document(collection = "proyectos")
@TypeAlias("newproyecto")
data class NewProyecto (
        @Id
        val id: String?,//Es posiblemente nulo porqué se crea automáticamente
        var nombre: String,
        var area: String,
        var fecha:String,
        var reuniones: List<NewReunion>?
)

现在,要创建“重聚”,我有两个班级,ReunionNewReunion。对应于创建 MongoDB 嵌入文档的类是NewReunion

NewReunion.kt

@Document
data class Reunion(
        val objetivo: String,
        val fecha: String,
        val participantes: List<Participante>?
) 

@Document
data class NewReunion(
        var id: String? = ObjectId().toHexString(),
        var fecha: String,
        var participantes: List<NewParticipante>?
) 

这就是我的问题所在。我想为这个NewReunion 类生成一个ObjectId,以便嵌入其中的每个对象都有一个id。问题是ObjectId ().ToHexString() 在构建NewReunion 类型的对象时没有生成任何值,但是objetivofecha 的其他数据填充了来自请求POST 的数据.

我如何发送信息。

我通过POST发送的信息。这个请求由一个名为ProyectoController.kt的控制器处理

ProyectoController.kt

@PostMapping("/")
fun createProyecto(@RequestBody newProyecto: NewProyecto): NewProyecto = proyectoService.createProyecto(newProyecto)

ProyectoRepository.kt

interface ProyectoRepository : MongoRepository<Proyecto, String> 
    fun findById(id: ObjectId): Proyecto
    override fun findAll(): List<Proyecto>
    fun insert(proyecto: NewProyecto): NewProyecto
    fun save(proyect: Proyecto): Proyecto
    fun deleteById(id: ObjectId)

ProyectoService.kt

@Service("proyectoService")
class ProyectoServiceImpl : ProyectoService 
    @Autowired
    lateinit var proyectoRepository: ProyectoRepository

    //Obtener un proyecto
    override fun findById(id: ObjectId): Proyecto = proyectoRepository.findById(id)

    //Obtener todos los proyectos
    override fun findAll(): List<Proyecto> = proyectoRepository.findAll()

    //Crear un proyecto
    override fun createProyecto(newProyecto: NewProyecto): NewProyecto = proyectoRepository.insert(newProyecto)

    //Actualizar un proyecto
    override fun updateProyecto(proyecto: Proyecto):Proyecto = proyectoRepository.save(proyecto)

    //Eliminar un proyecto
    override fun deleteProyecto(id:ObjectId) = proyectoRepository.deleteById(id)

使用 Postman 发布:

为了发送信息,我正在使用 Postman,并通过以下方式发送请求。

在创建新的Proyecto时,我返回它以查看返回结果为id=null的结果,但所有其他字段都分配了相应的值:

现在,我尝试初始化 NewReunion 类的所有构造函数参数,看看发生了什么。

data class NewReunion(
        @Id
        var id: String? = ObjectId().toHexString(),
        var objetivo: String = "",
        var fecha: String = ""
) 

id 的值与其他值一起正确生成。就是这种行为我不明白为什么要初始化NewReunion类的构造函数参数。

参数初始化后的 POST 结果。

build.gradle

buildscript 
    ext.kotlin_version = '1.2.71'
    ext 
        kotlinVersion = '1.2.71'
        springBootVersion = '2.0.6.RELEASE'
    
    repositories 
        mavenCentral()
    
    dependencies 
        classpath("org.springframework.boot:spring-boot-gradle-plugin:$springBootVersion")
        classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion")
        classpath("org.jetbrains.kotlin:kotlin-allopen:$kotlinVersion")
        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
    


apply plugin: 'kotlin'
apply plugin: 'kotlin-spring'
apply plugin: 'eclipse-wtp'
apply plugin: 'org.springframework.boot'
apply plugin: 'io.spring.dependency-management'
apply plugin: 'war'

group = 'com.gibranlara'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = 1.8
compileKotlin 
    kotlinOptions 
        freeCompilerArgs = ["-Xjsr305=strict"]
        jvmTarget = "1.8"
    

compileTestKotlin 
    kotlinOptions 
        freeCompilerArgs = ["-Xjsr305=strict"]
        jvmTarget = "1.8"
    


repositories 
    mavenCentral()


configurations 
    providedRuntime


dependencies 
    compile "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" // Required for Kotlin integration
    compile "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"
    compile "org.springframework.boot:spring-boot-starter-data-mongodb"
    compile 'org.springframework.boot:spring-boot-starter-web'

【问题讨论】:

如果objetivofecha 为空,它在哪里获取值?我认为这些例子并不完整。另外我认为这篇文章缺少有关使用过的库的信息 @asm0dey objetivofecha 在创建新实例时不应该为空,所以我没有标有? 的属性。该信息来自邮递员的帖子。 @asm0dey 我已经编辑了我的问题以提供更多背景信息。 @Gibrán,我已经展示了我认为您想要实现的目标,请查看。 github.com/andre-artus/kotlin-optional-param-spring-demo 如果这看起来像你想要的,我可以在这里进一步解释。 酷。您可能应该编辑您的问题以反映您在 Spring Boot 中使用 Kotlin 默认参数时遇到问题。现在的问题可能对其他人没有太大帮助,这可能就是有人否决它的原因。如果您想进一步讨论这个问题,您可以在我发布的 GitHub 存储库上提出问题。或者带它去聊天。 【参考方案1】:

您使用的库可能不是用 Kotlin 编写的。

Kotlin 生成一个合成构造函数,在调用实际构造函数之前加载默认值,例如

   // Java 
   public NewReunion(String var1, String var2, String var3, int var4, DefaultConstructorMarker var5) 
      if ((var4 & 1) != 0) 
         var1 = ObjectId().toHexString();
      
      this(var1, var2, var3);
   

该库可能正在执行以下操作之一:

调用默认构造函数,然后调用匹配注解/约定的 set[Property]。 调用最接近的匹配构造函数:NewReunion(@Nullable String id, @NotNull String objetivo, @NotNull String fecha)NewReunion(null, "objetivo", "fecha")

如果你这样定义你的类:

data class NewReunion @JvmOverloads constructor(
    var id: String? = "",
    var objetivo: String,
    var fecha: String
)

您将获得额外的构造函数,例如

// Java
public NewReunion(@NotNull String objetivo, @NotNull String fecha)

如果您的库使用第一个选项,那么您可能需要延迟初始化 getter 中的 id 字段(也将数据类转换为普通类)。

旁白

大多数此类问题源于开发人员使用相同的对象模型进行通信和业务逻辑。每当我在实体上看到可以为空的id 时,它就像是在发出错误的号角声。

您从外部来源获得的任何数据(即使它来自您控制的服务器)都应该被视为是您最恶毒的敌人放在那里的,但是许多开发人员只是将其吸收进来并在它到来时使用它。

如果你没有类似的东西

val cleanData = validate(inputData)

在从输入层跨越到业务层之前,你就是在为未来的尴尬做好准备。

输入层是:

用户界面 网络服务 来自您直接控制范围之外的任何内容

【讨论】:

但我没有使用任何库。我只是初始化了一个新的 Spring Boot 应用程序,添加了 kotlin 依赖项,仅此而已。我仅将 NewReunion 模型用于创建。 id 为空,因为我没有从前端发送 ObjectId,但我希望在生成 NewReunion 对象时为该对象生成一个 objectid。为了返回信息,我使用了一个名为 Proyecto 的模型,它不允许空值。 如果您使用的是 Spring Boot,那么您使用的不是一个,而是许多 [Java] 库。请参阅上面的“旁白”下的我的建议。这两个模型,即使它们看起来[几乎]相同,也不应该被强行合二为一。你真的应该在两者之间有一些逻辑,从输入模型(NewReunion?)映射到输出模型(Proyecto?)。该映射函数应负责确保所有字段均有效,包括 ObjectId。使用类型系统对您有利。 目前我有我的模型、控制器、存储库和服务。我只是不知道应该在什么时候为嵌入文档(NewReunion)生成 id。如果您允许,我可以将我所有的代码放在更多上下文中,您怎么看? 那很酷,不过我得稍后再看,因为我该睡觉了。 感谢您的时间和帮助,休息一下。

以上是关于Kotlin 数据类:如何为嵌入式文档生成 MongoDB ObjectId的主要内容,如果未能解决你的问题,请参考以下文章

如何为 kotlin 委托属性使用不同的类型

如何为 MongoDB 中的嵌入式文档创建唯一 ID?

如何为现有数据库生成 Castle ActiveRecord C# 类

如何为 Ejabberd 自定义模块生成文档?

如何为 Firebase (Kotlin) 创建自定义身份验证

如何为嵌入文件编写mongo查询