Редактировать: Супер короткий ответ без объяснения: В вашем примере вы настраиваете неправильный пакет для сканирования классов. Вы открываете сеанс с val sessionFactory = SessionFactory(configuration, "test.movies.domain")
, но это должно быть val sessionFactory = SessionFactory(configuration, "com.asofttz.micros.administrator.users.testmodels")
, судя по объявлению пакета ваших моделей. Но кроме того, пожалуйста, посмотрите мою более длинную версию для некоторых лучших практик и объяснений:
Найдите полный и рабочий пример в виде гистограммы здесь: Минимальный пример Kotlin / Gradle для Neo4j OGM
Позвольте мне провести вас через это:
В build.gradle
определите плагин компилятора без аргументов как зависимость сценария сборки.
buildscript {
dependencies {
classpath "org.jetbrains.kotlin:kotlin-noarg:1.2.51"
}
}
И затем используйте блок noArg
, чтобы определить, для каких классов должен быть синтезирован конструктор без аргументов:
noArg {
annotation("org.neo4j.ogm.annotation.NodeEntity")
annotation("org.neo4j.ogm.annotation.RelationshipEntity")
}
Это означает, что все классы, отмеченные @NodeEntity
и @RelationshipEntity
, должны иметь синтетический конструктор без аргументов.
Я абсолютно согласен с Джаспером в том, что это лучший подход, чем использование по умолчанию всех параметров конструктора класса вашего домена, для справки: kotlin-noarg docs:
Плагин компилятора без аргументов генерирует дополнительный нулевой аргумент
конструктор для классов с определенной аннотацией.
Сгенерированный конструктор является синтетическим, поэтому его нельзя вызвать напрямую
из Java или Kotlin, но его можно вызвать с помощью отражения.
В отношении классов доменов: классы, отображаемые с помощью Neo4j OGM, не обязательно должны быть окончательными. Но мы не поддерживаем конечные поля и, как таковые, чистых неизменяемых классов. Так обстоят дела сейчас.
Итак, оба класса доменов:
@NodeEntity
class Actor(var name: String) {
@Id
@GeneratedValue
var id: Long? = null
@Relationship(type = "ACTS_IN", direction = "OUTGOING")
var movies = mutableSetOf<Movie>()
fun actsIn(movie: Movie) {
movies.add(movie)
movie.actors.add(this)
}
}
@NodeEntity
class Movie(var title: String, var released: Int) {
@Id
@GeneratedValue
var id: Long? = null
@Relationship(type = "ACTS_IN", direction = "INCOMING")
var actors = mutableSetOf<Actor>()
}
Обратите внимание, что все поля var
, а не val
. Вы можете спокойно пропустить ключевое слово open
здесь. Также обратите внимание, что я удалил параметры по умолчанию для «реальной» деловой информации (здесь, название и год выпуска).
Мы должны позаботиться о наборах: я удалил явное hashSetOf
и вместо этого использовал mutableSetOf
. Мы можем использовать #add
, чтобы изменить сами наборы.
Если вы предпочитаете более идиоматический способ Kotlin, используйте setOf
и используйте тот факт, что наши атрибуты больше не являются окончательными, и измените сами поля:
@NodeEntity
class Actor(var name: String) {
@Id
@GeneratedValue
var id: Long? = null
@Relationship(type = "ACTS_IN", direction = "OUTGOING")
var movies = setOf<Movie>()
fun actsIn(movie: Movie) {
movies += movie
movie.actors += this
}
}
@NodeEntity
class Movie(var title: String, var released: Int) {
@Id
@GeneratedValue
var id: Long? = null
@Relationship(type = "ACTS_IN", direction = "INCOMING")
var actors = setOf<Actor>()
}
Обратите внимание: в исходном примере у вас есть утверждение типа movie.actors.plus(this)
. Это не изменяет набор, но создает новый, точно так же, как оператор +
для наборов.
На уровне моделирования: лично я не стал бы отображать отношения в обоих направлениях. Это имеет тенденцию укусить вас рано или поздно, так же, как это происходит в мире JPA / ORM. Отобразите направление, необходимое для вашей логики, и выполните другие запросы для путей и т. Д. Отдельно.
Пожалуйста, дайте мне знать, если это поможет. Я закрываю проблему GH, которую вы создали сейчас.