Neo4j - OGM 在 Kotlin 中抛出的不是实体
Neo4j - OGM throws not an Entity in Kotlin
如上图所示,我一直在尝试使用 neo4j-ogm 和 kotlin,但没有成功。如果我尝试保存我的数据,Neo4j 会抛出异常,"Class xxxx is not a valid Entity".
package com.asofttz.micros.administrator.users.testmodels
import org.neo4j.ogm.annotation.GeneratedValue
import org.neo4j.ogm.annotation.Id
import org.neo4j.ogm.annotation.NodeEntity
import org.neo4j.ogm.annotation.Relationship
@NodeEntity
class Actor(var name: String = "") {
@Id
@GeneratedValue
open var id: Long? = null
@Relationship(type = "ACTS_IN", direction = "OUTGOING")
open val movies = hashSetOf<Movie>()
fun actsIn(movie: Movie) {
movies.add(movie)
movie.actors.plus(this)
}
}
@NodeEntity
class Movie(var title: String = "", var released: Int = 2000) {
@Id
@GeneratedValue
open var id: Long? = null
@Relationship(type = "ACTS_IN", direction = "INCOMING")
open var actors = setOf<Actor>()
}
有办法吗?是否有使用 kotlin 将数据持久保存到 Neo4j 数据库的替代方案?
N:B。我正在使用 kotlin 版本 1.2.60 和 Neo4j-OGM v3.2.1
更新
下面是我的其余代码
import com.asofttz.micros.administrator.users.testmodels.Actor
import com.asofttz.micros.administrator.users.testmodels.Movie
import org.neo4j.ogm.config.Configuration
import org.neo4j.ogm.session.SessionFactory
import java.util.*
object Neo4j {
val configuration = Configuration.Builder()
.uri("bolt://localhost")
.credentials("neo4j", "password")
.build()
val sessionFactory = SessionFactory(configuration, "test.movies.domain")
fun save() {
val session = sessionFactory.openSession()
val movie = Movie("The Matrix", 1999)
session.save(movie)
val matrix = session.load(Movie::class.java, movie.id)
for (actor in matrix.actors) {
println("Actor: " + actor.name)
}
}
}
build.gradle 文件看起来像这样
apply plugin: 'kotlin'
apply plugin: 'application'
apply plugin: "org.jetbrains.kotlin.plugin.noarg"
repositories {
jcenter()
mavenCentral()
maven { url "http://dl.bintray.com/kotlin/ktor" }
maven { url "https://dl.bintray.com/kotlin/kontlinx" }
}
noArg {
annotation("org.neo4j.ogm.annotation.NodeEntity")
annotation("org.neo4j.ogm.annotation.RelationshipEntity")
}
dependencies {
compile "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
compile "io.ktor:ktor:$ktor_version"
compile "io.ktor:ktor-server-netty:$ktor_version"
compile project(":asoftlibs:micros:administrator:users:users-jvm")
compile 'org.neo4j:neo4j-ogm-core:3.1.2'
compile 'org.neo4j:neo4j-ogm-bolt-driver:3.1.2'
}
kotlin {
experimental {
coroutines "enable"
}
}
compileKotlin {
kotlinOptions.jvmTarget = "1.8"
}
compileTestKotlin {
kotlinOptions.jvmTarget = "1.8"
}
sourceCompatibility = "1.8"
我得到 class 'com.asofttz.micros.administrator.users.testmodels.Movie is not a valid entity' 进一步的帮助将不胜感激。
注意:我还尝试让电影 class 在没有婴儿车构造器的情况下打开,但 id ddnt 也有帮助。另一个尝试是更改neo4j-ogm的版本,所以我测试了2.1.5、3.0.1和3.1.2。没有成功
OGM 要求 classes 是开放的并且有一个无参数的构造函数。 类 in Java 默认展示这些特征,但它们在 Kotlin 中没有。
您可以将 class 标记为打开,并手动添加默认构造函数,或者您可以使用 'no-args' 和 'kotlin-spring' gradle 插件。这是一个使用 Kotlin、Spring Data SDN 和 OGM 的 sample application。在我们的构建文件中注意:
noArg {
annotation("org.neo4j.ogm.annotation.NodeEntity")
annotation("org.neo4j.ogm.annotation.RelationshipEntity")
annotation("org.springframework.data.neo4j.annotation.QueryResult")
}
这与手动添加默认构造函数相同,但是:
- 代码更简洁
- 默认构造函数专门供库在运行时使用,否则会被隐藏。
作为替代方案,您可以使用螺栓驱动器并手动映射您的查询结果。当您对特定用例进行自定义查询时,这是一个很好的选择 - 例如具有高流量和仔细调整查询的应用程序。
这是 sample application showing the use of the bolt driver directly。
编辑:没有解释的超级简短回答是:在您的示例中,您为 class 扫描配置了错误的包。您使用 val sessionFactory = SessionFactory(configuration, "test.movies.domain")
打开 session,但根据模型的包声明判断它需要 val sessionFactory = SessionFactory(configuration, "com.asofttz.micros.administrator.users.testmodels")
。但除此之外,请参阅我的较长版本以获得一些最佳实践和解释:
在此处找到作为 Gist 的完整且有效的示例:Minimal Kotlin/Gradle Example for Neo4j OGM
让我来引导您:
在 build.gradle
中,将 No-arg compiler plugin 定义为构建脚本依赖项。
buildscript {
dependencies {
classpath "org.jetbrains.kotlin:kotlin-noarg:1.2.51"
}
}
然后使用 noArg
块来定义应该为哪些 classes 合成一个 no-arguments 构造函数:
noArg {
annotation("org.neo4j.ogm.annotation.NodeEntity")
annotation("org.neo4j.ogm.annotation.RelationshipEntity")
}
这意味着:所有用 @NodeEntity
和 @RelationshipEntity
注释的 class 都应该有一个合成的 no-args 构造函数。
我完全同意 Jasper 的观点,这是比默认域的所有构造函数参数更好的方法 class,作为参考,kotlin-noarg 文档:
The no-arg compiler plugin generates an additional zero-argument
constructor for classes with a specific annotation.
The generated constructor is synthetic so it can’t be directly called
from Java or Kotlin, but it can be called using reflection.
关于域 classes:类 由 Neo4j OGM 映射不需要是最终的。但是我们不支持 final 字段,因此,没有纯粹的不可变 classes。这就是目前的情况。
所以这两个域都是 classes:
@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
关键字。另请注意,我确实删除了 "real" 业务信息的默认参数(此处为标题和 release-year)。
我们必须特别注意集合:我删除了显式的 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 问题。
如上图所示,我一直在尝试使用 neo4j-ogm 和 kotlin,但没有成功。如果我尝试保存我的数据,Neo4j 会抛出异常,"Class xxxx is not a valid Entity".
package com.asofttz.micros.administrator.users.testmodels
import org.neo4j.ogm.annotation.GeneratedValue
import org.neo4j.ogm.annotation.Id
import org.neo4j.ogm.annotation.NodeEntity
import org.neo4j.ogm.annotation.Relationship
@NodeEntity
class Actor(var name: String = "") {
@Id
@GeneratedValue
open var id: Long? = null
@Relationship(type = "ACTS_IN", direction = "OUTGOING")
open val movies = hashSetOf<Movie>()
fun actsIn(movie: Movie) {
movies.add(movie)
movie.actors.plus(this)
}
}
@NodeEntity
class Movie(var title: String = "", var released: Int = 2000) {
@Id
@GeneratedValue
open var id: Long? = null
@Relationship(type = "ACTS_IN", direction = "INCOMING")
open var actors = setOf<Actor>()
}
有办法吗?是否有使用 kotlin 将数据持久保存到 Neo4j 数据库的替代方案?
N:B。我正在使用 kotlin 版本 1.2.60 和 Neo4j-OGM v3.2.1
更新
下面是我的其余代码import com.asofttz.micros.administrator.users.testmodels.Actor
import com.asofttz.micros.administrator.users.testmodels.Movie
import org.neo4j.ogm.config.Configuration
import org.neo4j.ogm.session.SessionFactory
import java.util.*
object Neo4j {
val configuration = Configuration.Builder()
.uri("bolt://localhost")
.credentials("neo4j", "password")
.build()
val sessionFactory = SessionFactory(configuration, "test.movies.domain")
fun save() {
val session = sessionFactory.openSession()
val movie = Movie("The Matrix", 1999)
session.save(movie)
val matrix = session.load(Movie::class.java, movie.id)
for (actor in matrix.actors) {
println("Actor: " + actor.name)
}
}
}
build.gradle 文件看起来像这样
apply plugin: 'kotlin'
apply plugin: 'application'
apply plugin: "org.jetbrains.kotlin.plugin.noarg"
repositories {
jcenter()
mavenCentral()
maven { url "http://dl.bintray.com/kotlin/ktor" }
maven { url "https://dl.bintray.com/kotlin/kontlinx" }
}
noArg {
annotation("org.neo4j.ogm.annotation.NodeEntity")
annotation("org.neo4j.ogm.annotation.RelationshipEntity")
}
dependencies {
compile "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
compile "io.ktor:ktor:$ktor_version"
compile "io.ktor:ktor-server-netty:$ktor_version"
compile project(":asoftlibs:micros:administrator:users:users-jvm")
compile 'org.neo4j:neo4j-ogm-core:3.1.2'
compile 'org.neo4j:neo4j-ogm-bolt-driver:3.1.2'
}
kotlin {
experimental {
coroutines "enable"
}
}
compileKotlin {
kotlinOptions.jvmTarget = "1.8"
}
compileTestKotlin {
kotlinOptions.jvmTarget = "1.8"
}
sourceCompatibility = "1.8"
我得到 class 'com.asofttz.micros.administrator.users.testmodels.Movie is not a valid entity' 进一步的帮助将不胜感激。
注意:我还尝试让电影 class 在没有婴儿车构造器的情况下打开,但 id ddnt 也有帮助。另一个尝试是更改neo4j-ogm的版本,所以我测试了2.1.5、3.0.1和3.1.2。没有成功
OGM 要求 classes 是开放的并且有一个无参数的构造函数。 类 in Java 默认展示这些特征,但它们在 Kotlin 中没有。
您可以将 class 标记为打开,并手动添加默认构造函数,或者您可以使用 'no-args' 和 'kotlin-spring' gradle 插件。这是一个使用 Kotlin、Spring Data SDN 和 OGM 的 sample application。在我们的构建文件中注意:
noArg {
annotation("org.neo4j.ogm.annotation.NodeEntity")
annotation("org.neo4j.ogm.annotation.RelationshipEntity")
annotation("org.springframework.data.neo4j.annotation.QueryResult")
}
这与手动添加默认构造函数相同,但是:
- 代码更简洁
- 默认构造函数专门供库在运行时使用,否则会被隐藏。
作为替代方案,您可以使用螺栓驱动器并手动映射您的查询结果。当您对特定用例进行自定义查询时,这是一个很好的选择 - 例如具有高流量和仔细调整查询的应用程序。
这是 sample application showing the use of the bolt driver directly。
编辑:没有解释的超级简短回答是:在您的示例中,您为 class 扫描配置了错误的包。您使用 val sessionFactory = SessionFactory(configuration, "test.movies.domain")
打开 session,但根据模型的包声明判断它需要 val sessionFactory = SessionFactory(configuration, "com.asofttz.micros.administrator.users.testmodels")
。但除此之外,请参阅我的较长版本以获得一些最佳实践和解释:
在此处找到作为 Gist 的完整且有效的示例:Minimal Kotlin/Gradle Example for Neo4j OGM
让我来引导您:
在 build.gradle
中,将 No-arg compiler plugin 定义为构建脚本依赖项。
buildscript {
dependencies {
classpath "org.jetbrains.kotlin:kotlin-noarg:1.2.51"
}
}
然后使用 noArg
块来定义应该为哪些 classes 合成一个 no-arguments 构造函数:
noArg {
annotation("org.neo4j.ogm.annotation.NodeEntity")
annotation("org.neo4j.ogm.annotation.RelationshipEntity")
}
这意味着:所有用 @NodeEntity
和 @RelationshipEntity
注释的 class 都应该有一个合成的 no-args 构造函数。
我完全同意 Jasper 的观点,这是比默认域的所有构造函数参数更好的方法 class,作为参考,kotlin-noarg 文档:
The no-arg compiler plugin generates an additional zero-argument constructor for classes with a specific annotation.
The generated constructor is synthetic so it can’t be directly called from Java or Kotlin, but it can be called using reflection.
关于域 classes:类 由 Neo4j OGM 映射不需要是最终的。但是我们不支持 final 字段,因此,没有纯粹的不可变 classes。这就是目前的情况。
所以这两个域都是 classes:
@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
关键字。另请注意,我确实删除了 "real" 业务信息的默认参数(此处为标题和 release-year)。
我们必须特别注意集合:我删除了显式的 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 问题。