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

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

我知道在MongoDb中不存在实体之间关系的概念,因此我将使用嵌入式文档的策略。也就是说,将Reunión嵌入Proyecto,将Participante嵌入Reunión。

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

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>?
){}

现在,要创建“reuniones”,我有两个类,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的 Controller 处理

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)
}

使用邮递员进行 POST:

要发送信息,我使用的是Postman,我以以下方式发送请求。
mongodb - Kotlin数据类:如何为嵌入式文档生成MongoDB ObjectId-LMLPHP

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

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

现在,我尝试初始化NewReunion类的所有构造函数参数,以查看发生了什么。
data class NewReunion(
        @Id
        var id: String? = ObjectId().toHexString(),
        var objetivo: String = "",
        var fecha: String = ""
) {}
id的值与其他值一起正确生成。正是这种行为使我不明白为什么需要初始化NewReunion类的构造函数参数。

初始化参数后的POST结果。

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

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'
}

最佳答案

您正在使用的库可能不是在考虑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时,就好像是一个 bug 即将到来的号角声。

    您从外部来源(即使是来自您控制的服务器)获得的任何数据都应视为最残酷的敌人将其放入那里,但是许多开发人员只是将其吸收并随其使用。

    如果您没有类似的东西
    val cleanData = validate(inputData)
    

    在从输入层过渡到业务层之前,您需要为将来的尴尬做好准备。

    输入层是:
  • 用户界面
  • Web服务
  • 来自您的直接控制范围之外的任何内容
  • 09-15 16:54