insert 或 update 都是通过调用 JpaRepository 的 save 方法实现的。
本文以 User,Role,Group (用户,角色,组)为例进行阐述。
用户和角色是多对多关系,用户和组是多对一关系。
对于实体类 User ,调用 save(user) 方法,分为以下情况讨论:
假如 user 的主键字段(@Id 修饰的字段)为空,是新增方法,执行一条 insert 语句。
假如主键字段不为空则会先执行 select 语句,查看当前主键对应的 user 是否存在。 - 假如不存在则执行 insert 语句,需要注意的是如果主键有自动生成规则,保存的主键则会根据数据库的规则生成而不是设置的值。 - 假如存在则比较字段值有无变化,有变化则执行 update 语句。
总结如下:
执行 insert 语句的条件:待保存的实体类主键为空,或者主键是一个在数据库中不存在的值。执行 update 语句的条件:待保存的实体类主键不为空,并且主键是一个在数据库中存在的值,并且相比数据库存在的值有变化。对于实体类 User 和 Group,User 与 Group 的关系是多对一关系,Group 与 User 的关系是一对多关系。 多对一关系中,一般不会新建关系表,User 实体类中有一个字段是 Group 类型
@ManyToOne @JoinColumn(name = "group_id") private Group group;相应的 User 表中有一个 group_id 字段关联 Group 表。
对于实体类 User ,调用 save(user) 方法,假如 user 内部的 group 不为空并且 group 没有设置主键字段,则会报错。假如 user 内部的 group 不为空并且 group 设置了主键并且主键在表中不存在,则会报错。报错都是因为数据库硬性指定了外键。
只有 group 为空,或者 group 不为空并且主键字段对应的数据在 Group 表中存在的时候才不会报错。
假设 User 和 Role 两个实体是多对多关系。通过 @ManyToMany 注释,JPA 会自动生成并维护 user 和 role 的关系表。
user 里面有个集合是 roles,标注了多对多关系。
@ManyToMany @JoinTable(name = "user_role", joinColumns = {@JoinColumn(name = "user_id")}, inverseJoinColumns = {@JoinColumn(name = "role_id")}) private List<Role> roles;调用 save(user) 方法时,roles 字段的要求如下:
roles 集合可以为空,若为空调用 save(user) 方法:假如 user 执行的是 update 语句,则会删除所有当前 user 主键字段的关系。假如 user 执行的是 insert 语句,则对关系表没有操作。
roles 若不为空调用 save(user) 方法:假如 user 执行的是 update 语句,则会删除所有当前 user 主键字段的关系,然后重新插入(例外情况就是从数据库中查询出 user 后,对此 user 的 roles 元素没有增删或者更改主键字段,更改元素的其它字段没关系,此时对关系表不会有任何操作)。假如 user 执行的是 insert 语句,则对关系表直接插入。无论 insert 还是 update,集合中所有元素的主键字段不能为空或者不存在的值,否则会报错。
需要注意的是,上面对 role 的主键必须存在的情形,只有在 @ManyToMany 的 cascade 字段没有开启级联关联的时候才符合,假如开启插入关联,则会自动在 role 表中插入数据,需另外讨论。
