使用Spring Boot Neo4j Data时关系创建后被删除的问题
你遇到的关系创建后被删除的问题,核心原因有两个:
实体关联集合未被正确加载/初始化
你的MemberNode类使用@Builder构造器,但构造器参数中没有包含subRoles集合,同时也没有为该集合提供setter方法。Spring Data Neo4j(SDN)在从数据库加载MemberNode实例时,无法将已存在的关系填充到subRoles集合中,导致main.getSubRoles()始终返回你初始化的空ArrayList。SDN的关系同步机制
SDN在保存实体时,会对比当前实体中持有的关系集合与数据库中的关系:- 删除所有不在当前集合中的数据库关系
- 添加所有在当前集合中但数据库不存在的新关系
由于你查询
main时,subRoles是空集合,添加新关系后,集合里的新MemberRelationship还未生成持久化ID(id为null)。SDN生成Cypher时,$__knownRelationShipIds__参数为空,因此执行DELETE语句时会删除该节点下所有ACTED_IN关系——包括刚创建的新关系。
方案1:修改MemberNode类,允许SDN填充关联集合
在MemberNode的构造器中添加subRoles参数,并更新Builder:
@Node("Member") @Getter public class MemberNode { @Id @GeneratedValue private final Long memberId; @Property("name") private final String name; @Property("grade") @ConvertWith(converter = GradeTypeConverter.class) private final GradeType gradeType; @Relationship(type = "ACTED_IN", direction = Relationship.Direction.OUTGOING) private List<MemberRelationship> subRoles = new ArrayList<>(); @Builder public MemberNode(Long memberId, String name, GradeType gradeType, List<MemberRelationship> subRoles) { this.memberId = memberId; this.name = name; this.gradeType = gradeType; if (subRoles != null) { this.subRoles = subRoles; } } }
方案2:查询时强制加载关联集合
在memberRepository的findByName方法中,使用@Query配合MATCH语句强制加载subRoles集合,确保查询到的MemberNode包含已有的关系:
@Repository public interface MemberRepository extends Neo4jRepository<MemberNode, Long> { @Query("MATCH (m:Member {name: $name}) OPTIONAL MATCH (m)-[:ACTED_IN]->(sub) RETURN m, collect(sub) as subRoles") MemberNode findByName(String name); }
方案3:添加集合的setter方法
给MemberNode的subRoles添加setter,让SDN可以通过反射或setter填充集合:
@Node("Member") @Getter public class MemberNode { // ... 其他属性不变 @Relationship(type = "ACTED_IN", direction = Relationship.Direction.OUTGOING) private List<MemberRelationship> subRoles = new ArrayList<>(); // 单独的setter public void setSubRoles(List<MemberRelationship> subRoles) { this.subRoles = subRoles; } @Builder public MemberNode(Long memberId, String name, GradeType gradeType) { this.memberId = memberId; this.name = name; this.gradeType = gradeType; } }
修改后重新运行接口,SDN生成的Cypher将不会出现多余的DELETE语句,而是只会执行创建关系的操作,新的ACTED_IN关系会被正确持久化到数据库中。
内容的提问来源于stack exchange,提问作者zaim k

