JPA many to many 表设计与问题解决

在使用JPA时遇到多对多关系(A, B, C)保存到数据库的问题,导致更新A的父对象X时出错。错误提示为:A different object with the same identifier value was already associated with the session。解决方案包括检查Entity的ID生成策略和ManyToMany的cascade设置。通过调整cascade=CascadeType.ALL解决了问题。需要注意的是,不当的cascade设置可能导致意外的数据删除。" 104241430,6776484,阿里云APush集成实战指南,"['WebSocket集成', '消息推送', '阿里云服务', '前端开发', '后端开发']

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

今天写业务代码,将many 2 many的对象(A,B,C)存入db,在entity本身save过程没有问题,但是在下一句update它的另一个关系父对象过程出错如下:

...A different object with the same identifier value was already associated with the session...

entities关系,A和C是多对多关系,通过B关系表关联。

A --------> B <----------C

A 和X是多对一关系,这里把X看成A的父亲,X和Y是多对一关系,把Y看成A的爷爷。

     多对一             多对一

A--------------->X-------------------->Y. 

业务代码上,通过put API创建X,在建X过程中构建A,B,C。代码执行到save(X)都没有问题,接着后面执行update(Y)出问题了。之前代码A,X,Y都已经存在,构建流程也是一样,这次是新加了C和关系表B,对A添加了新关系。报错对象指向了B这个关系表。

查了一下网上资料,两种说法:

1. entity上的id没有设计对,我理解是因为没有正确让id unique导致多条记录没有unique id,通过加入@GeneratedValue(strategy = GenerationType.IDENTITY) 将问题解决。

2. ManyToMany 时cascade没有写对,通过加入cascade = CascadeType.ALL解决

针对第一种方法,我查看了entity id生成策略是用的uuid,所以不可能是第一种原因。第二种仔细检查了几个entity的写法,特别是新增加的B和C。

A(OutMapping) 和 C(Condition )是多对多关系,通过B(MappingAssign)表关联

A: OutMapping

@Entity
public class OutMapping {
    @Id
    @Column(name = "id")
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long                               id;

   

    @OneToMany(mappedBy = "outMapping", cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.LAZY)
    @JsonManagedReference("mappingAssigns")
    private final Set<MappingAssign>   mappingAssigns    = new HashSet<>();

   
}

B:MappingAssign

@Entity
public class MappingAssign implements Serializable {

    @Id
    @Column(name = "uuid")
    private String uuid = UUID.randomUUID().toString();

    @ManyToOne(fetch = FetchType.EAGER)
    @JoinColumn(name = "condition_id")
    @JsonBackReference("mappingAssigns")
    private ConditionExp ConditionExp;

    @ManyToOne(fetch = FetchType.EAGER)
    @JoinColumn(name = "out_id")
    @JsonBackReference("mappingAssigns")
    private OutMapping outMapping;
    
}

C:ConditionExp

@Entity
public class ConditionExp implements Identifiable {
    @Id
    @Column(name = "condition_id")
    private String uuid = UUID.randomUUID().toString();

    @OneToMany(mappedBy = "conditionExp",  cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.LAZY)
    private final Set<MappingAssign> mappingAssigns    = new HashSet<>();
}

清理三者业务处理关系:

A: OutMapping 在业务上只有当它父亲X创建时它才创建,只有它存在,B关系表里的记录才有意义,如果OutMapping删除,关系记录一并删除。所以cascade=ALL是正确的。

C:ConditionExp 独立于A和B存在,不受A,B的影响,有自己的CRUD API。所以检查C里对B的引用CASCADE=ALL并不适用,删除。

最后再测试,问题解决 :)

后话: 在解决问题之前试着在assign表里增加了cascade=ALL的方式,测试发现第一次put X没有问题,第二次试着put X的时候发现,由于assign里的cascade=ALL,第二次的put里构建新A会删除老的A,然后会把第一次的assign 删除,级联着删除了本来不应该删除的C!这个是低级错误,应该注意。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值