我正在使用Spring Boot和Kotlin构建API。我正在尝试通过以下方式在MongoDB中生成结构。
我知道在MongoDb中不存在实体之间关系的概念,因此我将使用嵌入式文档的策略。也就是说,将Reunión嵌入Proyecto,将Participante嵌入Reunión。
我有一个名为Proyecto
和NewProyecto
的主类,该类包含作为属性的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”,我有两个类,
Reunion
和NewReunion
。对应于创建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
不会生成任何值,但是objetivo
和fecha
的其他数据将填充来自请求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,我以以下方式发送请求。
在创建新的
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'
}
最佳答案
您正在使用的库可能不是在考虑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);
}
该库可能执行以下操作之一:
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)
在从输入层过渡到业务层之前,您需要为将来的尴尬做好准备。
输入层是: