使用neo4j操作图数据

2018/07/04 Neo4j

使用Neo4j操作图数据

文档型数据库会将数据存储到粗粒度的文档中,而图数据库会将数据存储到多个细粒度的节点中,这些节点之间通过关系建立关联。图数据库中的一个节点通常会对应数据库中的一个概念(concept),它会具备描述节点状态的属性。连接两个节点的关联关系可能也会带有属性。

Spring Data Neo4j

Spring Data Neo4j提供了很多与Spring Data JPA和Spring Data MongoDB相同的功能,当然所针对的是Neo4j图数据库。它提供了将Java对象映射到节点和关联关系的注解、面向模板的Neo4j访问方式以及Repository实现的自动化生成功能。

添加Neo4j依赖

创建项目,pom文件中引入依赖,如下:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-neo4j</artifactId>
</dependency>

配置文件

在配置文件中配置Neo4j相关配置,如下:

# neo4j配置
spring.data.neo4j.uri= bolt://localhost:7687
spring.data.neo4j.username=neo4j
spring.data.neo4j.password=neo4j

使用注解标注图实体

Neo4j定义了两种类型的实体:节点(node)和关联关系(relationship)。一般来讲,节点反映了应用中的事物,而关联关系定义了这些事物是如何联系在一起的。

图数据库使用

创建实体类节点

创建entity包,添加实体类:PersonEntity和MovieEntity


import lombok.Data;
import org.springframework.data.neo4j.core.schema.GeneratedValue;
import org.springframework.data.neo4j.core.schema.Id;
import org.springframework.data.neo4j.core.schema.Node;

/**
 * 定义演员信息
 */
@Node("Person")
@Data
public class PersonEntity {
    @Id
    @GeneratedValue
    private Long id;
    private String name;
    private Integer born;
    public PersonEntity(Integer born, String name) {
        this.name = name;
        this.born = born;
    }

}

import lombok.Data;
import org.springframework.data.neo4j.core.schema.*;

import java.util.ArrayList;
import java.util.List;

/**
 * 定义电影信息,标签名,labels可以缺省
 */
@Node(labels = "Movie")
@Data
public class MovieEntity {
    /**
     * Id自增
     */
    @Id
    @GeneratedValue
    private Long id;

    private final String title;

    /**
     * 映射到neo4j的属性名
     */
    @Property("tagline")
    private final String description;

    /**
     * 生成node时自动生成
     *
     * @param title
     * @param description
     */
    public MovieEntity(String title, String description) {
        this.id = null;
        this.title = title;
        this.description = description;
    }

    /**
     * 定义一个关系(参演)[direction]
     */
    @Relationship(type = "ACTED_IN", direction = Relationship.Direction.INCOMING)
    private List<Roles> actorsAndRoles = new ArrayList<>();

    /**
     * 定义一个关系(导演)
     * 注意这些关系最终的箭头指向是当前实体,即TargetNode(PersonEntity)->当前定义Relationship的实体(MovieEntity)
     */
    @Relationship(type = "DIRECTED", direction = Relationship.Direction.INCOMING)
    private List<PersonEntity> directors = new ArrayList<>();

    /**
     * 用户指定特定的Id
     *
     * @param id
     * @return
     */
    public MovieEntity withId(Long id) {
        if (this.id != null && this.id.equals(id)) {
            return this;
        } else {
            MovieEntity newObject = new MovieEntity(this.title, this.description);
            newObject.id = id;
            return newObject;
        }
    }

}

节点间的关系创建Roles


import org.springframework.data.neo4j.core.schema.Id;
import org.springframework.data.neo4j.core.schema.RelationshipProperties;
import org.springframework.data.neo4j.core.schema.TargetNode;

import java.util.List;

/**
 * 定义一个关系属性
 */
@RelationshipProperties
public class Roles {

    @Id
    private Long id;

    private final List<String> roles;

    @TargetNode // 相当于@StartNode
    private final PersonEntity person;

    // 参数1是目标关系实体节点 参数2是关系属性
    //    Roles 参数1:Person实体,演员的出生年和姓名;参数2:演员名字列表(考虑到一个演员可能参演多个角色)
    public Roles(PersonEntity person, List<String> roles) {
        this.person = person;
        this.roles = roles;
    }

    public List<String> getRoles() {
        return roles;
    }
}

使用Neo4jTemplate对图数据进行CRUD

import com.neo4j.entity.MovieEntity;
import com.neo4j.entity.PersonEntity;
import com.neo4j.entity.Roles;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.neo4j.core.Neo4jTemplate;
import org.springframework.data.neo4j.repository.query.QueryFragmentsAndParameters;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.*;

/**
 * 使用Neo4jTemplate操作Neo4j
 */
@Component
@Slf4j
public class Neo4jTemplateOp {

    @Resource
    private Neo4jTemplate neo4jTemplate;

    /**
     * 删除所有节点和关系(删除节点会响应删除关联关系),避免后续创建节点重复影响
     */
    public void deleteAll() {
        neo4jTemplate.deleteAll(MovieEntity.class);
        neo4jTemplate.deleteAll(PersonEntity.class);
    }

    /**
     * 存储图数据库
     */
    public void save() {
        MovieEntity movie = new MovieEntity("流浪地球", "一个叫地球的小孩流浪的故事。");
        Roles roles1 = new Roles(new PersonEntity(1998, "张三"), Collections.singletonList("三哥"));
        Roles roles2 = new Roles(new PersonEntity(1993, "李四"), Collections.singletonList("四哥"));
        PersonEntity director = new PersonEntity(1973, "导演1");
        // 添加movie的演员实体,加入(参演)关系
        movie.getActorsAndRoles().add(roles1);
        movie.getActorsAndRoles().add(roles2);
        movie.getDirectors().add(director);

        // 存入图数据库持久化
        neo4jTemplate.save(movie);
    }

    /**
     * 通过ID查询信息
     */
    public void findById() {
        Optional<PersonEntity> person = neo4jTemplate.findById(5, PersonEntity.class);
        log.info("person:{}", person);
    }

    /**
     * 通过属性查询节点,如name 需要手写cypherQuery语句
     * 两种写法如下
     * MATCH (n:Person {name: $name}) RETURN n
     * MATCH (n:Person) WHERE n.name = $name RETURN n
     */
    public void findOne() {
        Map<String, Object> map = new HashMap<>();
        map.put("name", "张三");
        Optional<PersonEntity> person1 = neo4jTemplate.findOne("MATCH (n:Person {name: $name}) RETURN n", map, PersonEntity.class);
        Optional<PersonEntity> person2 = neo4jTemplate.findOne("MATCH (n:Person) WHERE n.name = $name RETURN n", map, PersonEntity.class);
        log.info("查询名字为张三的Person节点:{}", person1);
        log.info("查询名字为张三的Person节点:{}", person2);
    }

    /**
     * 通过属性关系查询节点,使用toExecutableQuery查询
     */
    public void findExecutableQuery() {
        Map<String, Object> map = new HashMap<>(8);
        map.put("roles", Collections.singletonList("三哥"));
        QueryFragmentsAndParameters parameters = new QueryFragmentsAndParameters(
                "MATCH (person:Person) -[ relation:ACTED_IN]-> (movie:Movie) \n" +
                        "WHERE relation.roles = $roles\n" +
                        "RETURN person");
        parameters.setParameters(map);
        List<PersonEntity> roles = neo4jTemplate.toExecutableQuery(PersonEntity.class, parameters).getResults();
        log.info("查询角色为“三哥”的演员:{}", roles);
    }

    /**
     * 更新节点信息
     */
    public void update() {
        Map<String, Object> map = new HashMap<>();
        Long userId = 0L;
        map.put("name", "张三");
        map.put("usedName", "小张三");
        QueryFragmentsAndParameters queryFragmentsAndParameters =
                new QueryFragmentsAndParameters(
                        "MATCH (n:Person{name: $name}) SET n.name = $usedName");
        queryFragmentsAndParameters.setParameters(map);
        neo4jTemplate.toExecutableQuery(
                PersonEntity.class,
                queryFragmentsAndParameters).getResults();
        Optional<PersonEntity> person1 = neo4jTemplate.findById(userId, PersonEntity.class);
        log.info("查询张三信息:{}", person1);
        person1.get().setName("新海诚");
        neo4jTemplate.save(person1.get());
        Optional<PersonEntity> person2 = neo4jTemplate.findById(userId, PersonEntity.class);
        System.out.println("\n更新“新津诚”的name为“新海诚”:\n" + person2);
    }


}

使用repository对图数据进行CRUD

新建repository包,创建PersonRepository和MovieRepository

import com.example.neo4jdemo.entity.PersonEntity;
import org.springframework.data.neo4j.repository.Neo4jRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface PersonRepository extends Neo4jRepository<PersonEntity, Long> {
}

import com.example.neo4jdemo.entity.MovieEntity;
        import org.springframework.data.neo4j.repository.Neo4jRepository;
        import org.springframework.data.neo4j.repository.query.Query;
        import org.springframework.stereotype.Repository;

        import java.util.List;

@Repository
public interface MovieRepository extends Neo4jRepository<MovieEntity, Long> {
}

问题

低版本和高版本Neo4j兼容性问题

这两天学习SpringBoot时碰到了很多问题

springboot集合neo4j引用了org.neo4j的包,报错Required identifier property not found for class 用SpringBoot集成neo4j,查询报错Could not find mappable nodes or relationships inside Record org.springframework.data.neo4j.core.schema中没有@NodeEntity,@StartNode,@EndNode RelationShip无法注解在实体关系类中 nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.neo4j.ogm.session.SessionFactory] sessionFactory找不到

Search

    微信好友

    博士的沙漏

    Table of Contents