一聚教程网:一个值得你收藏的教程网站

最新下载

热门教程

Java Neo4j 实战指南:从图模型与Cypher 到 Spring Boot 关系查询

时间:2026-07-19 08:38:02 编辑:袖梨 来源:一聚教程网

简介

Neo4j 是一个图数据库。

Java Neo4j 实战指南:从图模型、Cypher 到 Spring Boot 关系查询

关系型数据库习惯用表、行、列描述数据,复杂关系通常靠外键和 JOIN 拼起来。

Neo4j 换了一个角度:直接用节点和关系描述数据。

 复制代码(张三:User)-[:FRIEND_OF]->(李四:User)
(张三:User)-[:BOUGHT]->(手机:Product)
(手机:Product)-[:BELONGS_TO]->(数码:Category)

这种模型很适合关系密集的数据。

比如社交好友、知识图谱、推荐系统、权限关系、风控链路、组织架构、物流路径。数据之间的连接本身就是业务重点时,图数据库会比一堆 JOIN 更自然。

Neo4j 在 Java 项目里常见两种用法:

用法适合场景
Neo4j Java Driver直接写 Cypher,控制力强,适合复杂查询
Spring Data Neo4jSpring Boot 项目里做对象映射和 Repository 开发

图数据库解决什么问题

假设要查一个用户的三层好友。

关系型数据库里通常需要一张用户表,再加一张好友关系表:

 复制代码user
id | name
---|------
1  | 张三
2  | 李四
3  | 王五friend
user_id | friend_id
--------|----------
1       | 2
2       | 3

查多层关系时,SQL 会变成多次自连接:

 复制代码SELECT f3.friend_id
FROM friend f1
JOIN friend f2 ON f1.friend_id = f2.user_id
JOIN friend f3 ON f2.friend_id = f3.user_id
WHERE f1.user_id = 1;

层级越深,SQL 越难维护。

Neo4j 里可以直接表达路径:

 复制代码MATCH (:User {userId: 1})-[:FRIEND_OF*1..3]->(friend:User)
RETURN DISTINCT friend

[:FRIEND_OF*1..3] 表示沿着 FRIEND_OF 关系走 1 到 3 层。

路径查询、关系遍历、共同好友、最短路径这类需求,是 Neo4j 比较擅长的地方。

核心概念

Neo4j 的概念不多,关键是把它们组合起来。

概念含义示例
Node节点,表示一个实体用户、商品、电影、公司
Label标签,表示节点类型UserProductMovie
Relationship关系,表示节点之间的连接FRIEND_OFBOUGHT
Type关系类型ACTED_INWORKS_AT
Property属性,节点和关系都可以有nameagescore
CypherNeo4j 查询语言类似 SQL 的声明式查询语言

一个节点可以有多个标签:

 复制代码CREATE (:User:Vip {userId: 1, name: "张三"})

关系有方向,也可以带属性:

 复制代码MATCH (u:User {userId: 1}), (p:Product {skuId: 1001})
CREATE (u)-[:BOUGHT {quantity: 2, amount: 1999.00}]->(p)

关系方向不是说业务只能单向理解,而是查询时需要清楚从哪里出发、沿着什么关系走。

本地启动 Neo4j

本地开发可以用 Docker 起一个 Neo4j。

 复制代码docker run -d 
  --name neo4j-demo 
  -p 7474:7474 
  -p 7687:7687 
  -e NEO4J_AUTH=neo4j/secretgraph 
  neo4j:latest

端口含义:

端口作用
7474Neo4j Browser Web 控制台
7687Bolt 协议,Java 应用连接使用

浏览器访问:

 复制代码

连接信息:

 复制代码uri: bolt://localhost:7687
username: neo4j
password: secretgraph

Cypher 基础语法

Cypher 是 Neo4j 的查询语言。

它看起来不像 SQL,但思路很直观:用括号表示节点,用箭头表示关系。

创建节点

 复制代码CREATE (:User {userId: 1, name: "张三", age: 25});
CREATE (:User {userId: 2, name: "李四", age: 26});
CREATE (:User {userId: 3, name: "王五", age: 24});

查询节点

 复制代码MATCH (u:User)
RETURN u;

按属性查询:

 复制代码MATCH (u:User {userId: 1})
RETURN u.name AS name, u.age AS age;

创建关系

 复制代码MATCH (a:User {userId: 1}), (b:User {userId: 2})
CREATE (a)-[:FRIEND_OF {since: date("2024-01-01")}]->(b);

查询好友:

 复制代码MATCH (u:User {userId: 1})-[:FRIEND_OF]->(friend:User)
RETURN friend;

MERGE:有就匹配,没有就创建

CREATE 每次都会创建新数据。

MERGE 更像“按模式查找,找不到再创建”。

 复制代码MERGE (u:User {userId: 1})
ON CREATE SET u.name = "张三", u.createdAt = datetime()
ON MATCH SET u.updatedAt = datetime()
RETURN u;

关系也可以用 MERGE

 复制代码MATCH (a:User {userId: 1}), (b:User {userId: 2})
MERGE (a)-[:FRIEND_OF]->(b);

使用 MERGE 前,常见做法是先给业务唯一字段建唯一约束,避免并发写入时出现重复节点。

更新和删除

更新属性:

 复制代码MATCH (u:User {userId: 1})
SET u.age = 26, u.updatedAt = datetime()
RETURN u;

删除关系:

 复制代码MATCH (:User {userId: 1})-[r:FRIEND_OF]->(:User {userId: 2})
DELETE r;

删除节点和它的关系:

 复制代码MATCH (u:User {userId: 3})
DETACH DELETE u;

DELETE 只能删除没有关系的节点。

DETACH DELETE 会连同关系一起删除,使用时要确认业务含义。

索引和唯一约束

频繁按 userId 查用户时,可以建唯一约束:

 复制代码CREATE CONSTRAINT user_id_unique IF NOT EXISTS
FOR (u:User)
REQUIRE u.userId IS UNIQUE;

按商品编号查商品:

 复制代码CREATE CONSTRAINT product_sku_unique IF NOT EXISTS
FOR (p:Product)
REQUIRE p.skuId IS UNIQUE;

约束带来两个好处:

好处说明
保证唯一性避免业务唯一字段重复
提升查询性能MATCHMERGE 可以走索引

MERGE 高频写入时,唯一约束尤其重要。

Java Driver 方式

Neo4j 官方 Java Driver 适合直接执行 Cypher。

Maven 依赖:

 复制代码<dependency>
    <groupId>org.neo4j.driver</groupId>
    <artifactId>neo4j-java-driver</artifactId>
    <version>${neo4j-java-driver.version}</version>
</dependency>

Neo4j Java Driver 6.x 需要 Java 17 或更高版本。

连接配置:

 复制代码package com.example.neo4j;import org.neo4j.driver.AuthTokens;
import org.neo4j.driver.Driver;
import org.neo4j.driver.GraphDatabase;public class Neo4jDriverFactory {    public Driver createDriver() {
        return GraphDatabase.driver(
                "bolt://localhost:7687",
                AuthTokens.basic("neo4j", "secretgraph")
        );
    }
}

Driver 是线程安全对象,通常整个应用只创建一个。

Session 是轻量级会话,用完关闭。

Java Driver 写入节点和关系

 复制代码package com.example.neo4j;import org.neo4j.driver.Driver;
import org.neo4j.driver.Session;import static org.neo4j.driver.Values.parameters;public class UserGraphRepository implements AutoCloseable {    private final Driver driver;    public UserGraphRepository(Driver driver) {
        this.driver = driver;
    }    public void saveUser(Long userId, String name, Integer age) {
        try (Session session = driver.session()) {
            session.executeWrite(tx -> {
                tx.run("""
                        MERGE (u:User {userId: $userId})
                        SET u.name = $name,
                            u.age = $age,
                            u.updatedAt = datetime()
                        """, parameters(
                        "userId", userId,
                        "name", name,
                        "age", age
                )).consume();
                return null;
            });
        }
    }    public void addFriend(Long userId, Long friendId) {
        try (Session session = driver.session()) {
            session.executeWrite(tx -> {
                tx.run("""
                        MATCH (a:User {userId: $userId})
                        MATCH (b:User {userId: $friendId})
                        MERGE (a)-[:FRIEND_OF]->(b)
                        """, parameters(
                        "userId", userId,
                        "friendId", friendId
                )).consume();
                return null;
            });
        }
    }    @Override
    public void close() {
        driver.close();
    }
}

这里用参数传值,而不是把变量拼到 Cypher 字符串里。

参数化查询可以减少注入风险,也方便 Neo4j 复用查询计划。

Java Driver 查询结果

 复制代码package com.example.neo4j;import org.neo4j.driver.Driver;
import org.neo4j.driver.Record;
import org.neo4j.driver.Session;import java.util.List;import static org.neo4j.driver.Values.parameters;public class UserGraphQueryRepository {    private final Driver driver;    public UserGraphQueryRepository(Driver driver) {
        this.driver = driver;
    }    public List<UserFriendView> findFriends(Long userId) {
        try (Session session = driver.session()) {
            return session.executeRead(tx -> tx.run("""
                            MATCH (:User {userId: $userId})-[:FRIEND_OF]->(friend:User)
                            RETURN friend.userId AS userId,
                                   friend.name AS name,
                                   friend.age AS age
                            ORDER BY friend.name
                            """, parameters("userId", userId))
                    .list(this::toUserFriendView));
        }
    }    private UserFriendView toUserFriendView(Record record) {
        return new UserFriendView(
                record.get("userId").asLong(),
                record.get("name").asString(),
                record.get("age").isNull() ? null : record.get("age").asInt()
        );
    }
}

返回对象:

 复制代码package com.example.neo4j;public record UserFriendView(
        Long userId,
        String name,
        Integer age
) {
}

Java Driver 的优点是灵活。

复杂路径查询、动态图谱查询、性能调优时,直接写 Cypher 往往更清楚。

Spring Boot 集成 Spring Data Neo4j

Spring Boot 项目里更常用的是 Spring Data Neo4j。

Maven 依赖:

 复制代码<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-neo4j</artifactId>
</dependency>

application.yml

 复制代码spring:
  neo4j:
    uri: bolt://localhost:7687
    authentication:
      username: neo4j
      password: secretgraph

Spring Data Neo4j 的调用链大概是:

 复制代码Controller
  -> Service
      -> Neo4jRepository
          -> Spring Data Neo4j
              -> Neo4j Java Driver
                  -> Neo4j

节点实体映射

用户节点:

 复制代码package com.example.graph.user;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.Property;
import org.springframework.data.neo4j.core.schema.Relationship;import java.util.HashSet;
import java.util.Set;@Node("User")
public class UserNode {    @Id
    @GeneratedValue
    private Long id;    @Property("userId")
    private Long userId;    private String name;    private Integer age;    @Relationship(type = "FRIEND_OF", direction = Relationship.Direction.OUTGOING)
    private Set<UserNode> friends = new HashSet<>();    protected UserNode() {
    }    public UserNode(Long userId, String name, Integer age) {
        this.userId = userId;
        this.name = name;
        this.age = age;
    }    public Long getId() {
        return id;
    }    public Long getUserId() {
        return userId;
    }    public String getName() {
        return name;
    }    public Integer getAge() {
        return age;
    }    public Set<UserNode> getFriends() {
        return friends;
    }
}

商品节点:

 复制代码package com.example.graph.product;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.Property;@Node("Product")
public class ProductNode {    @Id
    @GeneratedValue
    private Long id;    @Property("skuId")
    private Long skuId;    private String name;    private String category;    protected ProductNode() {
    }    public ProductNode(Long skuId, String name, String category) {
        this.skuId = skuId;
        this.name = name;
        this.category = category;
    }    public Long getId() {
        return id;
    }    public Long getSkuId() {
        return skuId;
    }    public String getName() {
        return name;
    }    public String getCategory() {
        return category;
    }
}

@Node 对应节点标签。

@Property 可以指定 Neo4j 里的属性名。

@Relationship 表示节点之间的关系。

Repository 查询

 复制代码package com.example.graph.user;import org.springframework.data.neo4j.repository.Neo4jRepository;
import org.springframework.data.neo4j.repository.query.Query;
import org.springframework.data.repository.query.Param;import java.util.List;
import java.util.Optional;public interface UserNodeRepository extends Neo4jRepository<UserNode, Long> {    Optional<UserNode> findByUserId(Long userId);    List<UserNode> findByNameContaining(String keyword);    @Query("""
            MATCH (u:User {userId: $userId})-[:FRIEND_OF]->(friend:User)
            RETURN friend
            ORDER BY friend.name
            """)
    List<UserNode> findFriends(@Param("userId") Long userId);    @Query("""
            MATCH (u:User {userId: $userId})
            MATCH (friend:User {userId: $friendId})
            MERGE (u)-[:FRIEND_OF]->(friend)
            """)
    void addFriend(@Param("userId") Long userId, @Param("friendId") Long friendId);    @Query("""
            MATCH (u:User {userId: $userId})-[r:FRIEND_OF]->(friend:User {userId: $friendId})
            DELETE r
            """)
    void removeFriend(@Param("userId") Long userId, @Param("friendId") Long friendId);
}

Neo4jRepository 提供基础 CRUD。

简单查询可以用方法名派生。

复杂关系查询直接写 @Query,可读性通常更好。

Service 示例

 复制代码package com.example.graph.user;import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;import java.util.List;@Service
public class UserGraphService {    private final UserNodeRepository userNodeRepository;    public UserGraphService(UserNodeRepository userNodeRepository) {
        this.userNodeRepository = userNodeRepository;
    }    @Transactional
    public UserNode create(UserCreateRequest request) {
        UserNode user = new UserNode(request.userId(), request.name(), request.age());
        return userNodeRepository.save(user);
    }    @Transactional
    public void addFriend(Long userId, Long friendId) {
        userNodeRepository.addFriend(userId, friendId);
    }    @Transactional(readOnly = true)
    public List<UserNode> findFriends(Long userId) {
        return userNodeRepository.findFriends(userId);
    }    @Transactional
    public void removeFriend(Long userId, Long friendId) {
        userNodeRepository.removeFriend(userId, friendId);
    }
}

请求对象:

 复制代码package com.example.graph.user;public record UserCreateRequest(
        Long userId,
        String name,
        Integer age
) {
}

Controller 示例

接口层不直接返回复杂的节点对象,避免关系对象递归序列化。

 复制代码package com.example.graph.user;import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;import java.util.List;@RestController
@RequestMapping("/api/graph/users")
public class UserGraphController {    private final UserGraphService userGraphService;    public UserGraphController(UserGraphService userGraphService) {
        this.userGraphService = userGraphService;
    }    @PostMapping
    public UserView create(@RequestBody UserCreateRequest request) {
        UserNode user = userGraphService.create(request);
        return UserView.from(user);
    }    @PostMapping("/{userId}/friends/{friendId}")
    public void addFriend(@PathVariable Long userId, @PathVariable Long friendId) {
        userGraphService.addFriend(userId, friendId);
    }    @GetMapping("/{userId}/friends")
    public List<UserView> findFriends(@PathVariable Long userId) {
        return userGraphService.findFriends(userId)
                .stream()
                .map(UserView::from)
                .toList();
    }
}

返回对象:

 复制代码package com.example.graph.user;public record UserView(
        Long userId,
        String name,
        Integer age
) {    public static UserView from(UserNode user) {
        return new UserView(user.getUserId(), user.getName(), user.getAge());
    }
}

这个接口已经能完成:

接口作用
POST /api/graph/users创建用户节点
POST /api/graph/users/{userId}/friends/{friendId}创建好友关系
GET /api/graph/users/{userId}/friends查询好友列表

关系属性:用户给商品评分

Neo4j 的关系也可以带属性。

比如用户给商品评分:

 复制代码(User)-[:RATED {score: 5, comment: "好用"}]->(Product)

Spring Data Neo4j 可以用 @RelationshipProperties 映射这种关系。

 复制代码package com.example.graph.rating;import com.example.graph.product.ProductNode;
import org.springframework.data.neo4j.core.schema.RelationshipId;
import org.springframework.data.neo4j.core.schema.RelationshipProperties;
import org.springframework.data.neo4j.core.schema.TargetNode;import java.time.LocalDateTime;@RelationshipProperties
public class RatingRelation {    @RelationshipId
    private Long id;    @TargetNode
    private ProductNode product;    private Integer score;    private String comment;    private LocalDateTime createdAt;    protected RatingRelation() {
    }    public RatingRelation(ProductNode product, Integer score, String comment) {
        this.product = product;
        this.score = score;
        this.comment = comment;
        this.createdAt = LocalDateTime.now();
    }    public ProductNode getProduct() {
        return product;
    }    public Integer getScore() {
        return score;
    }    public String getComment() {
        return comment;
    }    public LocalDateTime getCreatedAt() {
        return createdAt;
    }
}

UserNode 上增加关系:

 复制代码@Relationship(type = "RATED", direction = Relationship.Direction.OUTGOING)
private Set<RatingRelation> ratings = new HashSet<>();

这种写法适合关系本身有业务含义的场景。

好友关系只有连接意义时,可以直接用节点集合。

评分、购买、转账、任职、参与项目这类关系,通常都需要关系属性。

路径查询:共同好友和最短路径

共同好友:

 复制代码MATCH (a:User {userId: $userId})-[:FRIEND_OF]->(common:User)<-[:FRIEND_OF]-(b:User {userId: $otherUserId})
RETURN common.userId AS userId, common.name AS name
ORDER BY common.name

好友推荐:

 复制代码MATCH (u:User {userId: $userId})-[:FRIEND_OF]->(:User)-[:FRIEND_OF]->(candidate:User)
WHERE NOT (u)-[:FRIEND_OF]->(candidate)
  AND candidate.userId <> u.userId
RETURN candidate.userId AS userId,
       candidate.name AS name,
       count(*) AS score
ORDER BY score DESC, name
LIMIT 10

最短路径:

 复制代码MATCH path = shortestPath(
  (start:User {userId: $startUserId})-[:FRIEND_OF*1..6]-(end:User {userId: $endUserId})
)
RETURN path

路径查询要控制最大深度。

没有上限的变长关系查询,数据量大时容易产生非常高的遍历成本。

用 Neo4jClient 写灵活查询

Spring Data Neo4j 除了 Repository,也提供 Neo4jClient

它适合查询结果不是完整实体,而是报表、推荐列表、路径统计这类 DTO。

 复制代码package com.example.graph.recommend;import org.springframework.data.neo4j.core.Neo4jClient;
import org.springframework.stereotype.Repository;import java.util.List;@Repository
public class UserRecommendRepository {    private final Neo4jClient neo4jClient;    public UserRecommendRepository(Neo4jClient neo4jClient) {
        this.neo4jClient = neo4jClient;
    }    public List<UserRecommendView> recommendFriends(Long userId) {
        return neo4jClient.query("""
                        MATCH (u:User {userId: $userId})-[:FRIEND_OF]->(:User)-[:FRIEND_OF]->(candidate:User)
                        WHERE NOT (u)-[:FRIEND_OF]->(candidate)
                          AND candidate.userId <> u.userId
                        RETURN candidate.userId AS userId,
                               candidate.name AS name,
                               count(*) AS score
                        ORDER BY score DESC, name
                        LIMIT 10
                        """)
                .bind(userId).to("userId")
                .fetchAs(UserRecommendView.class)
                .mappedBy((typeSystem, record) -> new UserRecommendView(
                        record.get("userId").asLong(),
                        record.get("name").asString(),
                        record.get("score").asLong()
                ))
                .all()
                .stream()
                .toList();
    }
}

返回对象:

 复制代码package com.example.graph.recommend;public record UserRecommendView(
        Long userId,
        String name,
        Long score
) {
}

Repository 适合实体 CRUD。

Neo4jClient 适合复杂查询和自定义结果映射。

事务处理

Spring Data Neo4j 可以直接使用 Spring 的 @Transactional

 复制代码@Transactional
public void createUserAndFriend(UserCreateRequest userRequest, UserCreateRequest friendRequest) {
    UserNode user = userNodeRepository.save(
            new UserNode(userRequest.userId(), userRequest.name(), userRequest.age())
    );
    UserNode friend = userNodeRepository.save(
            new UserNode(friendRequest.userId(), friendRequest.name(), friendRequest.age())
    );
    userNodeRepository.addFriend(user.getUserId(), friend.getUserId());
}

Java Driver 方式则使用 executeWriteexecuteRead 管理读写事务。

 复制代码try (Session session = driver.session()) {
    session.executeWrite(tx -> {
        tx.run("MERGE (:User {userId: $userId})", parameters("userId", 1L)).consume();
        tx.run("MERGE (:User {userId: $userId})", parameters("userId", 2L)).consume();
        tx.run("""
                MATCH (a:User {userId: $aId}), (b:User {userId: $bId})
                MERGE (a)-[:FRIEND_OF]->(b)
                """, parameters("aId", 1L, "bId", 2L)).consume();
        return null;
    });
}

读写事务分开写,语义更清楚,也方便驱动做重试和路由。

建模建议

图数据库建模的重点不是“把表搬成节点”,而是把业务问题里的关系表达出来。

业务问题图模型思路
查共同好友User 节点 + FRIEND_OF 关系
商品推荐UserProductCategory + BOUGHTBELONGS_TO
权限判断UserRolePermission + HAS_ROLEHAS_PERMISSION
风控排查AccountDevicePhoneIp + 登录、绑定、使用关系
知识图谱实体节点 + 语义关系

几个建模原则:

原则说明
节点表示实体用户、商品、公司、账号、设备
关系表示动作或连接关注、购买、登录、绑定、属于
高频过滤字段建索引userIdskuIdaccountId
关系有业务属性时建关系属性评分、金额、时间、角色
控制变长路径深度避免无上限遍历

Neo4j 和 MySQL 怎么选

Neo4j 不是 MySQL 的替代品,更像是补充。

场景更适合
标准订单 CRUDMySQL
财务流水、强报表聚合MySQL
多层关系查询Neo4j
好友推荐、共同关系Neo4j
知识图谱查询Neo4j
风控关系网络Neo4j

常见架构是:

 复制代码MySQL 保存核心业务数据
Neo4j 保存关系网络和图查询模型

比如订单主数据仍然在 MySQL,用户、商品、类目、购买关系同步到 Neo4j,用于推荐和关系分析。

常见问题

查询越来越慢

常见原因有三个:

原因处理方式
按属性过滤但没有索引给高频查询字段建索引或唯一约束
变长路径没有限制深度使用 *1..3*1..6 这类上限
返回了过多节点和关系只返回需要的字段,配合 LIMIT

可以用 EXPLAINPROFILE 看执行计划:

 复制代码PROFILE
MATCH (u:User {userId: 1})-[:FRIEND_OF*1..3]->(friend:User)
RETURN DISTINCT friend
LIMIT 20;

MERGE 创建了重复数据

常见原因是 MERGE 的模式没有覆盖唯一业务字段,或者缺少唯一约束。

推荐先创建约束:

 复制代码CREATE CONSTRAINT user_id_unique IF NOT EXISTS
FOR (u:User)
REQUIRE u.userId IS UNIQUE;

再按唯一字段 MERGE

 复制代码MERGE (u:User {userId: $userId})
SET u.name = $name;

返回实体导致 JSON 无限嵌套

节点之间互相关联时,直接把实体返回给接口层,可能出现 JSON 循环或数据过大。

处理方式是返回 DTO:

 复制代码public record UserView(Long userId, String name, Integer age) {
}

Controller 里把实体转换成 DTO,再返回给前端。

Spring Data Neo4j 保存关系时覆盖了旧关系

Spring Data Neo4j 以聚合对象为单位保存数据。

如果实体里的关系集合不是完整状态,直接 save 可能让关系结果和预期不一致。

关系增删比较复杂时,可以用自定义 Cypher 精确控制:

 复制代码@Query("""
        MATCH (u:User {userId: $userId})
        MATCH (friend:User {userId: $friendId})
        MERGE (u)-[:FRIEND_OF]->(friend)
        """)
void addFriend(Long userId, Long friendId);

实践建议

场景建议
Spring Boot 标准项目优先使用 spring-boot-starter-data-neo4j
复杂图查询直接写 Cypher 或使用 Neo4jClient
高频唯一字段创建唯一约束
路径查询限制关系层数
接口返回返回 DTO,避免直接暴露节点实体
写入关系简单关系用对象映射,复杂关系用 @Query
性能分析使用 EXPLAINPROFILE
数据同步MySQL 负责主数据,Neo4j 负责图查询模型

小结

Neo4j 的核心不是“另一种存表方式”,而是把关系当成一等公民。

节点表示实体,关系表示实体之间的连接,属性描述节点和关系的细节,Cypher 用图模式表达查询。

Java 项目里,简单直接的方式是 Neo4j Java Driver,适合手写 Cypher 和复杂查询。Spring Boot 项目里更常见的是 Spring Data Neo4j,适合 Repository、对象映射、事务管理和常规 CRUD。

如果业务重点是多层关系、路径、推荐、知识图谱、风控网络,Neo4j 能把模型和查询写得更贴近业务本身。

热门栏目