1配置
1.1Maven依赖
<!--neo4j -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-neo4j</artifactId>
</dependency>
1.2yml配置
spring:
data:
neo4j:
uri: bolt://localhost:7687
username: neo4j
password: 123456
database: neo4j
1.3neo扫包
@SpringBootConfiguration
@EnableNeo4jRepositories(basePackages = "com.**.repo")
public class NeoConfig {
}
2入门代码
2.1代码结构
2.2node
如下所示,这是我们的一个实际的节点,我们以该节点为例,创建一个UserNode
//如果你没有使用lombook,请自己手动生成对应的get/set
@Data
@NodeEntity("User")
public class UserNode {
@Id
@GeneratedValue
private Long id;
private String userName;
private int userAge;
private String userSex;
}
2.3repo
@Repository
public interface UserRepository extends Neo4jRepository<UserNode, Long> {
}
2.4service
public interface UserService {
void saveUser(UserNode userNode);
UserNode getUserById(Long id);
}
@Service
public class UserServiceImpl implements UserService{
@Autowired
private UserRepository userRepository;
@Override
public void saveUser(UserNode userNode) {
userRepository.save(userNode);
}
@Override
public UserNode getUserById(Long id) {
return userRepository.findById(id).orElse(null);
}
}
2.5controller
@RestController
public class UserController {
@Autowired
private UserService userService;
@PostMapping("/user")
@ApiOperationSupport(order = 1)
@ApiOperation(value = "新增用户节点")
public R<String> savePerson(@RequestBody UserNode userNode) {
userService.saveUser(userNode);
return R.ok();
}
@GetMapping("/user/id{id}")
@ApiOperationSupport(order = 2)
@ApiOperation(value = "根据ID获取用户节点")
public R<UserNode> getUserById(@PathVariable Long id) {
UserNode user = userService.getUserById(id);
return R.ok(user);
}
}
如果出现以下错误
The client is unauthorized due to authentication failure.
解决方法:找到你安装neo4j的路径下的conf文件夹,neo4j.conf,找到
#dbms.security.auth_enabled=false
将前面的注释#去掉,然后重启neo4j,在重启项目即可。
2.6验证
通过接口创建User节点,老王 ,然后再neo4j中查询,看能否查到
根据上面neo4j为我们生成的ID,使用这个ID进行查询,看能否查询到数据
3spring data neo详解
3.1节点
下面是我们实体类节点中常用的注解。
//表明这个类和节点进行映射
@NodeEntity
//节点的主键ID
@Id
//如果ID为null,生成默认的ID值
@GeneratedValue
//节点的关系
@Relationship
/*
Relationship.UNDIRECTED关系的起点和终点之间没有方向性
Relationship.OUTGOING 表示关系只能从起点指向终点(默认值)
Relationship.INCOMING 表示关系只能从终点指向起点
*/
//表明这个类和关系进行映射
@RelationshipEntity
//开始节点
@StartNode
//结束节点
@EndNode
接下来我们看一个例子。
我们定义了一个UserNode类和User标签节点进行映射,并且,该用户和其他用户存在RELATION的关系
@Data
@NodeEntity("User")
public class UserNode {
@Id
@GeneratedValue
private Long id;
private String userName;
private int userAge;
private String userSex;
//和当前这个用户有关系的用户
@Relationship(type = "RELATION")
private List<UserNode> userNodes;
}
现在我们要实现,根据西子找出关系为RELATION的User节点
使用我们之前的接口,根据西子的id,查询西子信息即可
如果我们使用无向的关系,如下所示,就会出现循环嵌套(西子->老司->西子->老司),然后堆栈溢出。
@Relationship(type = "RELATION", direction = Relationship.UNDIRECTED)
private List<UserNode> userNodes;
如果我们使用反向的关系,那么根据西子查询,就查询不到了,但是根据念念,能查到西子和老司,因为西子和老司都指向念念,此时关系是逆向的,从终点到起点进行匹配。
@Relationship(type = "RELATION", direction = Relationship.INCOMING)
private List<UserNode> userNodes;
3.2Neo4jRepository
加粗样式还记得上面我们的入门案例嘛,我们编写了一个UserRepository来继承了Neo4jRepository,然而我们却没有任何的实现,这是因为Neo4jRepository默认提供了一些常用的方法来满足我们的增删查改
@Repository
public interface UserRepository extends Neo4jRepository<UserNode, Long> {
}
使用save方法将实体对象保存到Neo4j数据库中。
- 如果实体对象的ID为空,则新建一个节点并将实体对象保存到该节点上;
- 如果实体对象的ID不为空,则更新该节点的属性值。
<S extends T> S save(S entity);
<S extends T> S save(S s, int depth);
<S extends T> Iterable<S> save(Iterable<S> ses, int depth);
<S extends T> Iterable<S> saveAll(Iterable<S> entities);
因此当我们定义主键id,尽量使用包装类型,比如如果你的主键id设置为数字的,那么不要使用基本数据类型的int、long,而是使用包装类型的Integer、Long,并且最好是建议将新增和修改的UserNode拆成两个,在新增的时候没有属性id,在修改的时候有属性id,防止在新增的时候误传了id,变成修改操作。当然你也可通过其他方式,来进行区分新增或者更新,然后再新增的时候,将id设置为null。
那么S save(S entity)和S save(S s, int depth)有什么区别?
我们先看如下这个关系
@Data
@NodeEntity("User")
public class UserNode {
@Id
@GeneratedValue
private Long id;
private String userName;
private int userAge;
private String userSex;
//和当前这个用户有关系的用户
@Relationship(type = "老公", direction = Relationship.OUTGOING)
private UserNode husband;
@Relationship(type = "同事", direction = Relationship.OUTGOING)
private UserNode colleague;
}
match(u1:User{userName:"西子"})-[f1:老公]->(u2)-[f2:同事]->(u3) return u1,u2,u3
可以看到现在西子18岁,小跟班12岁,孙悟空无年龄
UserNode 西子 = userRepository.findByUserName("西子").get(0);
//将西子和西子老公小跟班的年龄都设置为15岁
西子.setUserAge(15);
西子.getHusband().setUserAge(15);
//Neo4jRepository 只能查询直接关联,因此查询不到小跟班的同事孙悟空
UserNode 孙悟空 = userRepository.findByUserName("孙悟空").get(0);
孙悟空.setUserAge(15);
西子.getHusband().setColleague(孙悟空);
//我们这里将西子、小跟班、孙悟空的年龄都修改为18岁,然后只保存西子
userRepository.save(西子);
我们发现结果中,我们虽然只保存西子这个节点,但是西子的子节点,以及子节点的子节点都会被保存00.
现在我们使用S save(S s, int depth),先设置为0,发现只保存西子,没有保存小跟班和孙悟空
西子.setUserAge(18);
西子.getHusband().setUserAge(18);
孙悟空.setUserAge(18);
userRepository.save(西子, 0);
<S extends T> S save(S entity); //会将关联到的子节点,甚至子节点的子节点也会保存
<S extends T> S save(S s, int depth); //按照深度保存,0代表不保存子节点,1代表保存1级子节点
3.3自定义查询