Spring Data Neo4j - Neo4jデータの追加と参照
概要
Neo4jはグラフデータベースの一つであり、Spring DataはNeo4jを扱うためのインターフェースを提供するフレームワークを用意している。
Kotlin言語を使って、Spring Data Neo4jを活用する簡単なプロジェクトを作成してみる。
Neo4jサーバーの準備
Neo4jはオープンソースサーバーなので、無料でインストールしたり、Dockerで実行したりできる。
Neo4jをmacOS環境にインストールする方法は3つある。
自分の好みの方法でインストールすればよい。
Neo4jプロジェクトの作成
次のようにcurlコマンドを使って、Spring Bootの初期プロジェクトを作成する。
curl https://start.spring.io/starter.tgz \
-d bootVersion=3.0.6 \
-d dependencies=data-neo4j \
-d baseDir=spring-data-neo4j \
-d groupId=com.devkuma \
-d artifactId=spring-data-neo4j \
-d packageName=com.devkuma.neo4j \
-d applicationName=Neo4jApplication \
-d packaging=jar \
-d language=kotlin \
-d javaVersion=17 \
-d type=gradle-project-kotlin | tar -xzvf -
このコマンドを実行すると、Java 17、Spring Boot 3.0.6のプロジェクトが作成される。
ビルドスクリプト
Neo4jを動作させるためのライブラリを、ビルドスクリプトに次のように追加する。
/build.gradle.kts
dependencies {
implementation("org.springframework.boot:spring-boot-starter-data-neo4j")
implementation("org.jetbrains.kotlin:kotlin-reflect")
testImplementation("org.springframework.boot:spring-boot-starter-test")
}
依存ライブラリにSpring Data Neo4jライブラリ(spring-boot-starter-data-neo4j)が含まれていることを確認できる。
Entityの定義
Neo4jではエンティティとエンティティの関係が接続され、どちらの方向も同じように重要である。各人のレコードを保存するシステムをモデル化すると考えてみよう。ここでは、特定の人の同僚も追跡したい。たとえば、下のエンティティではteammatesがそれに該当する。
src/main/java/com/devkuma/neo4j/entity/Person.java
package com.devkuma.neo4j.entity
import org.springframework.data.neo4j.core.schema.GeneratedValue
import org.springframework.data.neo4j.core.schema.Id
import org.springframework.data.neo4j.core.schema.Node
import org.springframework.data.neo4j.core.schema.Relationship
import java.util.*
import java.util.stream.Collectors
@Node
class Person {
@Id
@GeneratedValue
var id: Long = 0
var name: String = ""
@Relationship(type = "TEAMMATE")
var teammates: MutableSet<Person>? = null
fun worksWith(person: Person) {
if (teammates == null) {
teammates = HashSet()
}
teammates!!.add(person)
}
override fun toString(): String {
return ("$name's teammates => " +
Optional
.ofNullable(teammates)
.orElse(mutableSetOf()).stream()
.map { obj: Person -> obj.name }
.collect(Collectors.toList()))
}
}
クエリRepositoryの作成
Spring Data Neo4jは、Neo4jへデータを保存することに重点を置いている。ただし、検索クエリ派生機能を含め、Spring Data Commonsプロジェクトの機能も継承している。基本的にはNeo4jのクエリ言語を学ぶ必要はない。代わりに、いくつかのメソッドを作成すれば、クエリが自動で作成されるようにできる。
src/main/java/com/devkuma/neo4j/repository/PersonRepository.java
package com.devkuma.neo4j.repository
import com.devkuma.neo4j.entity.Person
import org.springframework.data.neo4j.repository.Neo4jRepository
interface PersonRepository : Neo4jRepository<Person, Long> {
fun findByName(name: String): Person?
fun findByTeammatesName(name: String): List<Person>
}
PersonRepositoryインターフェースはNeo4jRepositoryを拡張し、操作したい型(Person)を結び付ける。このインターフェースには、標準CRUD(作成、読み取り、更新、削除)操作を含む多くの操作が用意されている。
Neo4jアクセス権限
Neo4j Community Editionへアクセスするには、資格情報の設定が必要である。
Springの基本設定ファイルであるapplication.propertiesをapplication.ymlに変更し、次のように設定を追加する。
/src/main/resources/application.yml
spring:
neo4j:
uri: bolt://localhost:7687
authentication:
username: neo4j
password: secret123
ログ設定
ログを表示するために、依存関係としてkotlin-loggingライブラリを追加する。
dependencies {
// .. 省略 ..
implementation("io.github.microutils:kotlin-logging:3.0.5")
}
そして、Spring設定ファイルにログレベルを設定する。
logging:
level:
org.springframework.data.neo4j.cypher: ERROR
この設定がないと、Cypher関連のWARNが発生する。(どうやらSpring Data Neo4jの更新が必要そうだ。)
アプリケーションクラスの作成
Spring Initializrは、アプリケーション用の簡単なクラスを生成してくれる。
src/main/java/com/devkuma/neo4j/entity/Person.java
package com.devkuma.neo4j
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.runApplication
@SpringBootApplication
class Neo4Ajpplication
fun main(args: Array<String>) {
runApplication<Neo4Ajpplication>(*args)
}
生成されたアプリケーションクラスを次のように変更する。
package com.devkuma.neo4j
import com.devkuma.neo4j.entity.Person
import com.devkuma.neo4j.repository.PersonRepository
import mu.KotlinLogging
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.CommandLineRunner
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.runApplication
import org.springframework.context.annotation.Bean
import org.springframework.data.neo4j.repository.config.EnableNeo4jRepositories
import kotlin.system.exitProcess
private val log = KotlinLogging.logger {}
@SpringBootApplication
@EnableNeo4jRepositories
class Neo4jApplication {
@Bean
fun demo(@Autowired personRepository: PersonRepository): CommandLineRunner {
return CommandLineRunner {
personRepository.deleteAll()
var greg = Person()
greg.name = "Greg"
var roy = Person()
roy.name = "Roy"
val craig = Person()
craig.name = "Craig"
val team: List<Person> = listOf(greg, roy, craig)
log.info("Before linking up with Neo4j...")
team.stream().forEach { person: Person ->
log.info("\t${person}")
}
personRepository.save(greg)
personRepository.save(roy)
personRepository.save(craig)
greg = personRepository.findByName(greg.name)!!
greg.worksWith(roy)
greg.worksWith(craig)
personRepository.save(greg)
roy = personRepository.findByName(roy.name)!!
roy.worksWith(craig)
personRepository.save(roy)
log.info("Lookup each person by name...")
team.stream().forEach { person: Person ->
log.info("\t${personRepository.findByName(person.name)}")
}
val teammates = personRepository.findByTeammatesName(craig.name)
log.info("The following have ${craig.name} as a teammate...")
teammates.stream()
.forEach { person: Person -> log.info("\t${person.name}") }
}
}
}
fun main(args: Array<String>) {
runApplication<Neo4jApplication>(*args)
exitProcess(0)
}
@EnableNeo4jRepositories- このアノテーションによって、Neo4j設定が有効になる。
- 最初にすべてのデータを削除し、「Greg」、「Roy」、「Craing」を追加し、
TEAMMATEをworkWith(..)関数で追加していることを確認できる。 - 結果として、各人のteammatesを表示し、逆に「Craig」をteammateとして指定している人も表示している。
アプリケーションの実行
実行してみると、結果がログに表示される。
2023-05-13T01:53:34.640+09:00 INFO 36427 --- [ main] com.devkuma.neo4j.Neo4jApplication : Before linking up with Neo4j...
2023-05-13T01:53:34.640+09:00 INFO 36427 --- [ main] com.devkuma.neo4j.Neo4jApplication : Greg's teammates => []
2023-05-13T01:53:34.640+09:00 INFO 36427 --- [ main] com.devkuma.neo4j.Neo4jApplication : Roy's teammates => []
2023-05-13T01:53:34.640+09:00 INFO 36427 --- [ main] com.devkuma.neo4j.Neo4jApplication : Craig's teammates => []
2023-05-13T01:53:34.852+09:00 INFO 36427 --- [ main] com.devkuma.neo4j.Neo4jApplication : Lookup each person by name...
2023-05-13T01:53:34.865+09:00 INFO 36427 --- [ main] com.devkuma.neo4j.Neo4jApplication : Greg's teammates => [Craig, Roy]
2023-05-13T01:53:34.874+09:00 INFO 36427 --- [ main] com.devkuma.neo4j.Neo4jApplication : Roy's teammates => [Craig]
2023-05-13T01:53:34.881+09:00 INFO 36427 --- [ main] com.devkuma.neo4j.Neo4jApplication : Craig's teammates => []
2023-05-13T01:53:34.893+09:00 INFO 36427 --- [ main] com.devkuma.neo4j.Neo4jApplication : The following have Craig as a teammate...
2023-05-13T01:53:34.894+09:00 INFO 36427 --- [ main] com.devkuma.neo4j.Neo4jApplication : Greg
2023-05-13T01:53:34.894+09:00 INFO 36427 --- [ main] com.devkuma.neo4j.Neo4jApplication : Roy
Neo4j Browser(http://localhost:7474/browser/)に接続すると、Node Listを確認できる。

参考
- Getting Started | Accessing Data with Neo4j
- 公式ドキュメントにもかかわらず、内容とコードが合わない部分が多かった。
上記のサンプルコードはGitHubで確認できる。