我在我的应用程序中使用Neo4j(3.4.1版)和Spring-data-neo4j(5.0.10.RELEASE)。我也在使用OGM。
我有以下领域模型(车辆和零件):
车辆类
@NodeEntity
@Data
@NoArgsConstructor
public class Vehicle {
@Id
@GeneratedValue
private Long id;
@Relationship(type = "HAS_PART", direction = Relationship.OUTGOING)
private List<Part> parts = new ArrayList<>();
}
零件类
@NodeEntity
@Data
@NoArgsConstructor
public class Part {
@Id
private String name;
public Part(String name) {
this.name = name;
}
}
我能够为给定的parts
设置vehicle
,并使用spring data neo4j存储库界面将节点存储在Neo4j数据库中。
Vehicle myVehicle = new Vehicle();
Part brake = new Part("brake");
Part accelerator= new Part("accelerator");
Part clutch= new Part("clutch");
myVehicle.setParts(Arrays.asList(brake, accelerator, clutch));
Vehicle vehicle = vehicleRepository.save(myVehicle);
System.out.println("vehicle = " + vehicle);
我可以使用Neo4j浏览器在数据库中同时看到Vehicle
和Part
节点,如下所示:
我面临的问题是在更新现有车辆时。
我正在尝试更改parts
的{{1}}。我正在做以下事情:
vehicle
在IDE调试会话中,我可以看到 Vehicle myVehicle = vehicleRepository.findById(582L).get();
Part brake = new Part("brake");
Part accelerator= new Part("accelerator");
myVehicle.setParts(Arrays.asList(brake, accelerator));
Vehicle savedVehicle = vehicleRepository.save(myVehicle);
System.out.println("vehicle = " + savedVehicle);
对象(在savedVehicle
上调用save
之后返回)只有两个部分:“刹车”和“加速器”。它没有“离合器”部分,就像我希望的那样。
但是,当我使用浏览器在Neo4j数据库中检查相同的车辆对象时,我看到该车辆对象仍然具有3个部分(甚至是“离合器”)。
由于我已经覆盖了节点及其关系,因此我无法理解vehicleRepository
节点为何仍与clutch
节点相关。另外,为什么vehicle
对象与数据库中的对象不同。
有人能对此有所启发吗?另外,如何在保存/更新时删除与savedVehicle
节点的HAS_PART
关系。
关于, V
答案 0 :(得分:1)
了解发生了什么的一个好方法是将logging.level.org.neo4j.ogm.drivers.bolt=DEBUG
添加到application.properties。这将为您提供在日志中执行的实际密码。您会看到您的代码转换为MERGE
语句,如下所示。
Request: UNWIND {rows} as row MERGE (n:`Vehicle`{name: row.props.name}) SET n=row.props RETURN row.nodeRef as ref, ID(n) as id, {type} as type with params {type=node, rows=[{nodeRef=-1, props={name=Car}}]}
Request: UNWIND {rows} as row MERGE (n:`Part`{name: row.props.name}) SET n=row.props RETURN row.nodeRef as ref, ID(n) as id, {type} as type with params {type=node, rows=[{nodeRef=-3, props={name=brake}}, {nodeRef=-5, props={name=accelerator}}, {nodeRef=-7, props={name=clutch}}]}
MERGE
子句确保图形中存在模式。该模式已经存在,或者需要创建。它不会删除任何现有节点。
来到该解决方案中,有几种不同的方法可以实现您想要的,天真的解决方案是执行以下操作:
Part p = new Part("brake");
partRepository.delete(p);
但是,请注意,这将针对所有车辆节点删除名称为“ brake”的所有零件。这可能不是您一直想要的。例如您可能只想将clutch
与名称为Car
的车辆分开。
假设您在车辆标签上拥有name
属性,则可以在代码中执行类似的操作,以从名称为Vehicle
的{{1}}分离并删除离合器(如果您没有车辆名称,使用ID或其他唯一属性):
Car
另一种选择是在您的VehicleRepository中编写一个带有Vehicle car = vehicleRepository.findByName("Car");
for(Part p: car.getParts()){
if("clutch".equalsIgnoreCase(p.getName())){
partRepository.delete(p);
}
}
批注的方法,以执行以下查询的等效操作:
@Query
然后,您可以简单地从服务类中调用它。
有关示例,请参见here。
编辑:添加有关删除仅车辆和零件之间关系的更多信息。
这可以通过在下面的vehicleRepository中编写自定义查询来完成(我假设您在Vehicle上也具有name属性,如果没有,则可以使用id代替name):
MATCH(n:Vehicle {name:"Car"})-[:HAS_PART]->(p:Part{ name:"brake"})
DETACH DELETE p
现在,您可以从代码中调用它,例如:
@Query("MATCH(n:Vehicle {name: {0}})-[r:HAS_PART]-(p:Part{ name:{1}}) DELETE r")
Vehicle detachPartFromVehicle(@Param("partName") String vehicleNameName, @Param("partName") String partName);
这应该导致删除vehicleRepository.detachPartFromVehicle("Car", "clutch");
关系-HAS_PART
部分将保留在图形中,但将与车辆clutch
断开连接。
此外,您可以通过将其转换为IN查询来断开多个部分的连接:
Car
。
@Query("MATCH(n:Vehicle {name: {0}})-[r:HAS_PART]-(p:Part) WHERE p.name IN {1} DELETE r")
Vehicle detachPartFromVehicle(@Param("partName") String vehicleNameName, @Param("partNames") String[] partNames);