Spring R2DBC多对多关系复合主键配置及更新问题
Spring R2DBC 复合主键配置与更新实现
问题描述
我有一个处理student和tutor表多对多关系的实体,对应数据库表tutor_student,主键为复合键(tutor_id和student_id),实体代码如下:
@Getter @Setter @NoArgsConstructor @Table("tutor_student") public class TutorStudentEntity implements Serializable { @Column("tutor_id") private Long tutorId; @Column("student_id") private String studentId; @Column("status") private String status; }
使用ReactiveCrudRepository#save方法更新记录时,Spring R2DBC因无法识别主键而尝试创建新记录。JPA中的@EmbeddedId和@IdClass方案不适用于Spring R2DBC,需要找到适配的配置方式。
解决方案
Spring R2DBC通过@Id注解识别主键,复合主键可通过以下两种方式配置,让save方法自动区分插入与更新:
1. 方式一:直接标注多个主键字段
在实体类的复合主键字段上分别添加@Id注解,Spring R2DBC会自动识别这是复合主键:
@Getter @Setter @NoArgsConstructor @Table("tutor_student") public class TutorStudentEntity implements Serializable { @Id @Column("tutor_id") private Long tutorId; @Id @Column("student_id") private String studentId; @Column("status") private String status; }
对应的Repository接口需要使用Tuple2作为主键类型:
public interface TutorStudentRepository extends ReactiveCrudRepository<TutorStudentEntity, Tuple2<Long, String>> { }
2. 方式二:使用自定义复合主键类
这种方式更易维护,适合复杂的复合主键场景:
步骤1:创建复合主键类
定义实现Serializable的主键类,并重写equals和hashCode方法(用于Spring判断记录唯一性):
@Getter @Setter @EqualsAndHashCode // Lombok注解自动生成equals和hashCode public class TutorStudentId implements Serializable { private Long tutorId; private String studentId; }
步骤2:修改实体类关联主键
将主键类作为实体字段,并用@Id注解标注:
@Getter @Setter @NoArgsConstructor @Table("tutor_student") public class TutorStudentEntity implements Serializable { @Id private TutorStudentId id; @Column("status") private String status; // 可选:添加单独字段的getter/setter,方便业务代码调用 public Long getTutorId() { return id != null ? id.getTutorId() : null; } public void setTutorId(Long tutorId) { if (id == null) { id = new TutorStudentId(); } id.setTutorId(tutorId); } public String getStudentId() { return id != null ? id.getStudentId() : null; } public void setStudentId(String studentId) { if (id == null) { id = new TutorStudentId(); } id.setStudentId(studentId); } }
步骤3:定义Repository接口
将自定义主键类作为Repository的主键泛型:
public interface TutorStudentRepository extends ReactiveCrudRepository<TutorStudentEntity, TutorStudentId> { }
验证更新逻辑
配置完成后,调用save方法时,Spring R2DBC会根据复合主键判断:
- 若主键对应的记录已存在,执行
UPDATE操作 - 若不存在,执行
INSERT操作
示例代码:
// 构造需要更新的实体 TutorStudentEntity entity = new TutorStudentEntity(); entity.setTutorId(1L); entity.setStudentId("S001"); entity.setStatus("INACTIVE"); // 调用save,自动执行更新 tutorStudentRepository.save(entity).subscribe();
关键注意事项
- 复合主键类必须实现
Serializable,这是Spring Data的强制要求 - 必须正确重写
equals和hashCode,否则Spring无法准确识别重复记录 - 两种方式都能实现需求,推荐使用自定义主键类的方式,代码可读性和扩展性更好
内容的提问来源于stack exchange,提问作者Andrii Torzhkov
相关产品推荐
相关产品推荐

