Класс данных Kotlin: как создать объектный идентификатор MongoDB для внедренного документа - PullRequest
0 голосов
/ 11 ноября 2018

Я создаю API с помощью Spring Boot и Kotlin. Я пытаюсь создать структуру следующим образом в MongoDB.

enter image description here

Я понимаю, что в MongoDb концепция отношений между сущностями не существует, поэтому я буду использовать стратегию встроенных документов. То есть встраивать Реюньон в Проект, а Участника в Реюньон.

У меня есть главный класс с именами Proyecto и NewProyecto, который содержит в качестве свойства список воссоединений типа 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>?
){}

Теперь, чтобы создать 'воссоединения', у меня есть два класса, 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>?
) {}

Вот где у меня проблема. Я хочу сгенерировать ObjectId для этого NewReunion класса, чтобы каждый вложенный в него объект имел id. Проблема в том, что ObjectId ().ToHexString() не генерирует никакого значения во время создания объекта типа NewReunion, но другие данные, objetivo и fecha, заполняются данными, полученными из запроса 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)
}

POST с почтальоном:

Для отправки информации я использую Почтальон, и отправляю запрос следующим образом. enter image description here

Во время создания нового Proyecto я возвращаю его, чтобы увидеть результат, который возвращает результат с id=null, но все остальные поля присваивают соответствующее значение:

enter image description here

Теперь я попытался инициализировать все параметры конструктора класса NewReunion, чтобы посмотреть, что произошло.

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

значение для id генерируется правильно вместе с другими значениями. Именно из-за этого поведения я не понимаю, почему мне нужно инициализировать параметры конструктора класса NewReunion.

Результат POST с инициализированными параметрами.

enter image description here

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

1 Ответ

0 голосов
/ 11 ноября 2018

Библиотека, которую вы используете, вероятно, написана не с учетом 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 [Свойство], соответствующего аннотациям / соглашению.
  • Вызов конструктора ближайшего совпадения: 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)

Если ваша библиотека использует первую опцию, вам может понадобиться ленивая инициализация поля id в геттере (также преобразовать класс данных в обычный класс).

В сторону

Большинство подобных проблем возникают из-за того, что разработчики используют одну и ту же объектную модель для коммуникации и бизнес-логики. Всякий раз, когда я вижу обнуляемое id на объекте, это как громкий призыв, что ошибки в движении.

Любые данные, которые вы получаете из внешнего источника (даже если они принадлежат серверу, которым вы управляете), должны обрабатываться так, как если бы они были помещены туда вашим самым злобным врагом, но многие разработчики просто засасывают его и используют по мере необходимости.

Если у вас нет ничего похожего на

val cleanData = validate(inputData)

перед тем, как перейти от входного уровня к бизнес-уровню, вы настраиваете себя на будущее.

Входные слои:

  • Пользовательский интерфейс
  • Веб-сервисы
  • Все, что приходит из-за пределов вашего непосредственного контроля
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...