Springboot连接Neo4j
本文档介绍利用Springboot连接Neo4j数据库实现节点、关系的增删改查。所有代码均可查看官方指导:https://docs.spring.io/spring-data/neo4j/docs/6.1.2/reference/html/#reference以及官方手册https://docs.spring.io/spring-data/neo4j/docs/6.1.2/api/
0 事前配置
pom.xml中:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-neo4j</artifactId>
</dependency>
版本为6.1.2,6+版本应该通用。
application.properties中:
# neo4j配置
spring.neo4j.uri = bolt://localhost:7687
spring.neo4j.authentication.username = usrname
spring.neo4j.authentication.password = pwd
项目结构:
- controller
- domain
-
- node
- relationship
- repository
- service
1 domain包
domain中为Neo4j数据库中存放的实体类,包括节点类和关系类,分别置于node包和relationship包中
1.1 node包
对应Neo4j数据库中的节点。以Country.java为例:
@Node(primaryLabel = "Country")
@Data
@Builder
public class Country {
@Id
@GeneratedValue
Long id;
@Property(name = "name")
private String name;
@Relationship(type = "buildIn", direction = Relationship.Direction.INCOMING)
private List<BuildIn> buildIn;
}
- @Node注解,声明这是一个节点类,primaryLabel参数为该节点的主标签;
- @Id与@GeneratedValue注解,声明该成员是一个自动生成的id,Neo4j中的id为长整型类Long;
- @Property注解,声明该成员是本节点的一个属性,name参数为该属性的名称;
- @Relationship注解,声明该成员是与本节点有关的关系的列表,
- type参数为该关系的名称;
- direction参数为该关系的方向,离开本节点(OUTGOING,默认)或进入本节点(INCOMING)。
- 注意:当关系一端的节点声明了此关系,另一端的节点一定不能声明同一关系,否侧会由于循环嵌套出现stackOverflow错误!
1.2 relationship包
对应Neo4j数据库中的关系。以BuildIn.java为例:
@RelationshipProperties
@Data
@Builder
public class BuildIn {
@Id
@GeneratedValue
Long id;
@TargetNode
private Ship ship;
private int year;
}
- @RelationshipProperties注解,声明这是一个关系类,其中必须包含一个@Id和@GeneratedValue注解声明的id;
- @TargetNode注解,声明该关系的另一端节点。
2 repository包
包含各个节点类与数据库的交互,每个接口均继承Neo4jRepository接口。以CountryRepository为例:
@Repository
public interface CountryRepository extends Neo4jRepository<Country, Long> {
/*
按照name查询国家
*/
Country findFirstByName(String name);
}
findAll、save、delete等基础方法已默认包含,无需额外声明。
简单查询可以自动生成,复杂查询等语句需要用@Query注解写查询语句,如:
@Query(value = "match p=(s:Ship)-[]-() where s.name={0} return p")
List<PathValue> getRelationshipByName(String name);
查询某一艘船及其对应的所有关系,得到的结果为org.neo4j.driver.internal.value.PathValue的列表
Spring Data Neo4j 6+版本中不在支持将关系作为查询结果,自动映射的对象必须为有主标签的节点或节点的映射,或Neo4j的原始数据类(如Path或Node)。
3 Service包
service包中为系统提供的服务,涉及部分逻辑与repository接口中方法的封装,以CountryService.java为例:
@Service
public class CountryService {
@Autowired
private CountryRepository countryRepository;
@Autowired
private ShipRepository shipRepository;
public List<Country> getAllCountry(){
return countryRepository.findAll();
}
public Country getCountryByName(String name){
return countryRepository.findFirstByName(name);
}
public Country addCountry(String name){
return countryRepository.save(Country.builder().name(name).build());
}
public void deleteCountry(String name){
countryRepository.delete(getCountryByName(name));
}
public Country addBuildInRelationship(String shipName, String countryName, int year){
Country country = countryRepository.findFirstByName(countryName);
Ship ship = shipRepository.findFirstByName(shipName);
for (BuildIn b : country.getBuildIn()){
if (b.getShip().getName().equals(shipName)){
return country;
}
}
country.getBuildIn().add(BuildIn.builder().ship(ship).year(year).build());
return countryRepository.save(country);
}
}
自动注入repository后,封装数据库的增删改查。
其中addBuildInRelationship为添加关系的方法,此处先判断该关系是否已存在,若不存在则在该类的关系列表中添加该关系,即可实现数据库中的关系添加,删除关系同理,之后覆盖对应节点。
4 controller包
controller包中为springboot的控制器,以CountryController.java为例:
@RestController
@RequestMapping("/country")
public class CountryController {
@Autowired
private CountryService countryService;
@GetMapping("/get_all_country")
public List<Country> getAllCountry(){
return countryService.getAllCountry();
}
@GetMapping("/get_country")
public Country getCountry(@RequestParam("name")String name){
return countryService.getCountryByName(name);
}
@PostMapping("/add_country")
public Country addCountry(@RequestParam("name")String name){
return countryService.addCountry(name);
}
@DeleteMapping("/delete_country")
public void deleteCountry(@RequestParam("name")String name){
countryService.deleteCountry(name);
}
@PostMapping("/add_build_in_relationship")
public Country addBuildInRelationship(
@RequestParam("shipName")String shipName,
@RequestParam("countryName")String countryName,
@RequestParam("year")int year){
return countryService.addBuildInRelationship(shipName, countryName, year);
}
}
自动注入ControllerService服务类即可调用对应方法。
5 运行结果与高级方法
查询所有country运行结果:
[
{
"name": "泛美",
"buildIn": []
},{
"name": "美国",
"buildIn": [
{
"id": 88,
"ship": {
"name": "得梅因",
"tier": 10
},
"year": 1941
},
...
]
},
...
]
看到查询输出的是之前设定的Country.java类中的成员,包括所有的关系,此时可以通过projection映射对输出结果进行筛选与重组。
以CountryProjection.java接口为例:
public interface CountryProjection {
String getName();
}
设置只得到Country类中的name成员;修改CountryRepository.java中的方法,添加:
List<CountryProjection> findAllBy();
得到全部Country的映射,即只得到name,增加对应Service方法与Controller方法,运行结果:
[
{
"name": "泛美"
},
{
"name": "美国"
},
...
]
就不再有关系输出了。
2022.11.20
这个demo是挺久之前写的了,代码规范部分现在看问题挺大的,最近也没有碰过neo4j了,因此现在翻出来的这个代码说实话有点处刑的感觉,但是能用,姑且一看,代码在这。