Neo4j의 NoSQL 그래프 기반 데이터 보관을 사용해 임베드된 Neo4j 서버를 만든 후 entity랑 relationship을 저장하고 마지막으로 그것들에 대한 query까지 해볼 것이다.
neo4j에서 개발한(...) 그래프 DB 관리 시스템이다. NoSQL의 일종이다.
@Relationship
neo4j의 경우 entity 정의 자체랑 entity 사이의 관계를 동등하게 중요시한다.
그래프 기반 DB의 경우 이 관계를 edge로 표현하며, Spring에서는 entity의 정의 뿐만 아니라 이 관계도 아주 간단하게 표현하는 것이 가능하다.
예를들어 '사람'이라는 entity에 동료라는 관계에 대한 정보를 저장하고 싶다 해보자. Spring에서 이를 하는 방법은 간단한데, entity에 대한 Set
을 지정한다음에 거기에 @Relationship
이라는 annotation을 붙이면 된다. 여기서의 type은 단순히 이름을 말하는게 아니라 말 그대로 type을 의미하는 것이다. 이에 관해선 이링크 참고. 여기서는 undirected graph로 취급.
또 Entity 정의 자체는 Neo4j에서 Java object에 대해 제공하는 프레임웍인 Object Graph Mapping 프레임웍을 사용하면 된다. 이때 Entity가 될 class에 @Node
라는 annotation을 붙여야 한다.
Entity인 class에는 private인, argument를 받지 않는 생성자가 필요한데 그 이유는 프레임웍에서 DB에 있는 데이터를 가져올 때 이를 저장할 instance를 만들 때 사용하기 때문이다.
public인 생성자는 DB에 저장할 instance를 만들 때 사용하는 생성자다.
@Id
는 일반적으로 entity의 identifier을 명시할 때 사용된다. neo4j의 경우에도 node를 neo4j에서 identify할 때 사용된다.
@GeneratedValue
는 이 값이 자동으로 생성될 것이라는 것을 의미한다. doc을 보면 알겠지만 세부 설정이 가능하며 여기서는 기본으로 세부설정했다. 내부적으로 자동 생성을 한다는 것을 의미. DB에서 생성하거나 외부 생성자를 통해 자동으로 생성하는 것도 가능하다.
Spring의 Neo4j의 경우에는 neo4j에다가 data를 저장하는 것에 관해서만 신경쓰나, Spring Data를 기본적으로 내포하고 있기 때문에 query 유도하는 것도 사용이 가능하다.
그럴려면 일단 우리가 지정한 entity를 query하는 인터페이스를 만들어야 한다. 튜토리얼의 경우 PersonRepository
interface가 여기에 해당된다. Neo4jRepository
를 확장하는데, 여러 CRUD operation을 지원해주며, 우리들만의 query operation을 만드는것도 가능하다. 여기서는 후자를 선택함.
spring.neo4j.uri=bolt://localhost:7687
spring.neo4j.authentication.username=neo4j
spring.neo4j.authentication.password=secret
위와 같은 설정을 application.properties에 해가지고 neo4j에 접근할 수 있는 인증을 설정하는 것이 가능하다.
다만 위는 예시라서 그런것이고, 현실에서는 runtime에 위 secret을 설정하는 것이 좋다. 이 때는 Spring Boot의 porperty override 기능을 활용하는게 좋다.
Neo4j repository를 활성화 시킬려면 무조건 필요한 annotation이다.
이때 Spring Data Repository interface 양식을 갖춘 class를 현재 package를 기준으로 탐색하게 된다. 이 예시의 경우에는 PersonRepository
가 여기에 해당된다.
그런데 만약 타 package에 해당 interface를 가진 class가 존재하면 어떻게 해야할까? 이 경우 basePackageClasses
를 사용하면 된다. @EnableNeo4jRepositories(basePackageClasses=MyRepository.class)
를 사용할 경우, MyRepository.class
가 존재하는 package를 탐색하게 된다. 이 때 해당 class를 가지는 모든 package가 탐색이 된다. 그리고 이 특징 때문인지, 혹시 모를 비정상적인 상황을 대비하기 위해 각 package마다 no-op혹은 아무 역할을 하지 않는 해당 이름의 인터페이스를 만들어 놓으라고 documentation에선 추천 중이다.
basePackageClasses
는 꼭 이 annotation에서만 쓰이는게 아니다. @ComponentScan
에서 쓰이는 예시
Neo4jRepository
의 경우 interface를 설정한 다음에 그것을 구현할 필요가 없다. autoconfiguration이 됨.
method의 이름, method의 signature을 기반으로 유도를 하기 때문에 naming convention을 잘 지켜야 한다. Neo4j의 경우 Cypher이라는 언어로 형성된 query를 활용하는데, 실제로 이 언어 기반의 query를 Spring에서 자동으로 만들어준다... 허어...
docker로 Neo4j를 돌리고 튜토리얼대로 수행을 한 다음에 실행을 하면 원하는 출력물이 나옴을 확인할 수 있다.
https://github.com/baekrang256/Backend-Practice/tree/main/SpringTutorial/AccessingDataNeo4j