经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » Java相关 » Spring » 查看文章
Spring?Boot?整合JPA?数据模型关联使用操作(一对一、一对多、多对多)
来源:jb51  时间:2022/7/19 15:03:49  对本文有异议

表关联

上一篇介绍了JPA的简单使用,这一篇介绍JPA在表关联上的使用

一对一

配置参数
JPA对于数据实体一对一映射使用的是@OneToOne注解。

代码

User用户表

  1. /**
  2. * 用户信息
  3. * @author daify
  4. **/
  5. @Data
  6. @Entity
  7. @Table(name = "cascade_user")
  8. public class User {
  9.  
  10. @Id
  11. @GeneratedValue
  12. private Long id;
  13. private String name;
  14. private Integer age;
  15. private Double point;
  16. /**
  17. * CascadeType包含的类别 级联:给当前设置的实体操作另一个实体的权限
  18. * CascadeType.ALL 级联所有操作
  19. * CascadeType.PERSIST 级联持久化(保存)操作
  20. * CascadeType.MERGE 级联更新(合并)操作
  21. * CascadeType.REMOVE 级联删除操作
  22. * CascadeType.REFRESH 级联刷新操作
  23. * CascadeType.DETACH 级联分离操作,如果你要删除一个实体,但是它有外键无法删除,这个级联权限会撤销所有相关的外键关联。
  24. */
  25. @OneToOne(targetEntity = UserCart.class,
  26. cascade = CascadeType.ALL,
  27. mappedBy = "user")
  28. private UserCart userCart;
  29.  
  30. @OneToOne(targetEntity = UserInfo.class,
  31. cascade = CascadeType.ALL)
  32. @JoinTable(name = "user_info_table",
  33. joinColumns = @JoinColumn(name="user_id"),
  34. inverseJoinColumns = @JoinColumn(name = "info_id"))
  35. private UserInfo userInfo;
  36.  
  37. }
  38.  

UserCart用户购物车表

  1. @Data
  2. @Entity
  3. @Table(name = "cascade_user_cart")
  4. public class UserCart {
  5.  
  6. @Id
  7. @GeneratedValue
  8. private Long id;
  9. private Integer count;
  10. private Double totalMoney;
  11. private Date updateTime;
  12.  
  13. /**
  14. * CascadeType包含的类别 级联:给当前设置的实体操作另一个实体的权限
  15. * CascadeType.ALL 级联所有操作
  16. * CascadeType.PERSIST 级联持久化(保存)操作
  17. * CascadeType.MERGE 级联更新(合并)操作
  18. * CascadeType.REMOVE 级联删除操作
  19. * CascadeType.REFRESH 级联刷新操作
  20. * CascadeType.DETACH 级联分离操作,如果你要删除一个实体,但是它有外键无法删除,这个级联权限会撤销所有相关的外键关联。
  21. */
  22. @OneToOne(targetEntity = User.class,
  23. cascade = {},
  24. fetch = FetchType.LAZY)
  25. private User user;
  26. }

用户信息

  1. @Data
  2. @Entity
  3. @Table(name = "cascade_user_info")
  4. public class UserInfo {
  5.  
  6. @Id
  7. @GeneratedValue
  8. private Long id;
  9. private String userInfo;
  10. private String config;
  11. }

上面例子中尝试使用了两种方式来维护一对一的关系,首先在User实体中同样标注了@OneToOne但是配置了mappedBy,
这样生成的表数据中,User和UserCart的关系将由UserCart负责维护。User表中并不会维护UserCart的信息。
而在User和UserInfo的关系中使用了中间表user_info_table来维护双方的关系,UserInfo实体中并没有保存任何User的信息。

权限

在User、UserCart、UserInfo三者中User为数据存在的主体,其他两个实体都是依托于User数据的存在而存在。
所以在权限中给User实体提供了全部全部权限。

注解

@OneToOne主要提供了下面的参数内容

注解参数描述
@OneToOne 描述一个 一对一的关联
 targetEntity目标类的实体
 cascade关联到目标的操作
 fetch是否使用延迟加载
 mappedBy反向关联

测试
因为上面一对一的例子中权限被赋予给User表中,UserCart并没有赋予任何权限,所以保存用户的时候可以关联保存用户购物车,删除购物车的时候并不会删除用户,但是删除用户的时候会删除购物车

通过保存用户关联保存购物车

  1. @Test
  2. public void testUserSave() {
  3. User defUser1 = UserMock.getDefUser1();
  4. UserCart defUserCart1 = UserCartMock.getDefUserCart1();
  5. defUser1.setUserCart(defUserCart1);
  6. defUserCart1.setUser(defUser1);
  7. // 此时保存用户,用户购物车应该也被保存
  8. userRepository.save(defUser1);
  9. List <UserCart> all = userCartRepository.findAll();
  10. Assert.assertNotNull(all);
  11. Assert.assertTrue(all.size() == 1);
  12. }

删除用户购物车的时候,用户不会被删除

  1. @Test
  2. public void testUserCartDelete() {
  3. User defUser1 = UserMock.getDefUser1();
  4. UserCart defUserCart1 = UserCartMock.getDefUserCart1();
  5. defUser1.setUserCart(defUserCart1);
  6. // 此时保存用户,用户购物车应该也被保存
  7. userRepository.save(defUser1);
  8. // 此时删除用户购物车并不会删除用户
  9. userCartRepository.deleteAll();
  10. List <User> all1 = userRepository.findAll();
  11. Assert.assertNotNull(all1);
  12. Assert.assertTrue(all1.size() == 1);
  13. }

删除用户的时候,购物车会被删除

  1. @Test
  2. public void testUserDelete() {
  3. User defUser1 = UserMock.getDefUser1();
  4. UserCart defUserCart1 = UserCartMock.getDefUserCart1();
  5. defUser1.setUserCart(defUserCart1);
  6. // 此时保存用户,用户购物车应该也被保存
  7. userRepository.save(defUser1);
  8. // 此时删除用户购物车并不会删除用户
  9. userRepository.delete(defUser1);
  10. List <UserCart> all = userCartRepository.findAll();
  11. Assert.assertTrue(all.size() == 0);
  12. }

一对多和多对一

通过@OneToMany和@ManyToOne的组合我们可以实现双向关联。根据JPA规范,我们使用多方来维护关系。
通过在多方维护@JoinColumn来注释属性。

代码

订单表

  1. @Data
  2. @Entity
  3. @Table(name = "cascade_order")
  4. public class Order {
  5.  
  6. @Id
  7. @GeneratedValue
  8. private Long id;
  9. private String orderNo;
  10. private Integer count;
  11. private Double money;
  12.  
  13. @OneToMany(mappedBy = "order",
  14. cascade = CascadeType.ALL,
  15. fetch = FetchType.EAGER)
  16. private List<OrderItem> orderItems;
  17. /**
  18. * 重写toString防止死循环
  19. * @return
  20. */
  21. @Override
  22. public String toString() {
  23. return "OrderItem{" + "id=" + id + ", productId='" + productId + '\'' + ", money=" + money
  24. + ", count=" + count + ", productName='" + productName + '\'' + ", order=" + order
  25. + '}';
  26. }
  27. }

订单子项

  1. @Data
  2. @Entity
  3. @Table(name = "cascade_order_item")
  4. public class OrderItem {
  5.  
  6. @Id
  7. @GeneratedValue
  8. private Long id;
  9.  
  10. private String productId;
  11.  
  12. private Double money;
  13. private Integer count;
  14. private String productName;
  15. @ManyToOne(targetEntity = Order.class,
  16. cascade = {},
  17. fetch = FetchType.EAGER)
  18. @JoinColumn(
  19. // 设置在OrderItem表中的关联字段(外键)
  20. name="order_id"
  21. )
  22. // 防止json序列化死循环问题解决
  23. @JsonBackReference
  24. private Order order;
  25. }

上门的例子中,订单方为一端,订单子项为多端,在多端除了使用了@ManyToOne
还使用了@JoinColumn注解来标识Order主键创建到OrderItem表的列的名称,
当然没有此注解的时候JPA会根据默认规则生成一个列名称。

权限
根据JPA规范一对多时候,关系的维护交给了多方来进行,但是很多时候多方的存在是依靠一方的。
比如(订单、订单子项)所以更新删除的权限需要授权给一方(Order)。

@ManyToOne,@OneToMany

注解参数描述
@ManyToOne 一个多对一的映射,该注解标注的属性通常是数据库表的外键.
 targetEntity目标类的实体
 cascade关联到目标的操作
 fetch是否使用延迟加载
@OneToMany 一个 一对多的关联,该属性应该为集体类型,在数据库中并没有实际字段
 targetEntity目标类的实体
 cascade关联到目标的操作
 fetch是否使用延迟加载
 mappedBy反向关联
 orphanRemoval讲移除操作级联到关联的实体中

测试

一对多的时候虽然多方维持了两者关系,但是我们把权限赋予了一方所以,删除多方并不会级联操作,删除一方可以移除多方数据。所以下的测试可以通过

  1. @Test
  2. public void testOrder() {
  3. Order defOrder1 = OrderMock.getDefOrder1();
  4. List <OrderItem> allDefOrder = OrderItemMock.getAllDefOrder();
  5. // order 维持orderItem关系
  6. defOrder1.setOrderItems(allDefOrder);
  7. // orderItem维持order关系
  8. allDefOrder.forEach(o -> o.setOrder(defOrder1));
  9. Order save = orderRepository.save(defOrder1);
  10. List <OrderItem> all = orderItemRepository.findAll();
  11. Assert.assertTrue(all.size() == allDefOrder.size());
  12.  
  13. orderItemRepository.delete(all.get(0));
  14. Order order = orderRepository.findById(save.getId()).get();
  15. Assert.assertNotNull(order);
  16.  
  17. orderRepository.deleteById(order.getId());
  18. List <OrderItem> all1 = orderItemRepository.findAll();
  19. Assert.assertTrue(all1.size() == 0);
  20.  
  21. }

多对多

配置方法
和一对多、一对一不同,多对多没法只使用两个数据实体完成相互之间的关系维护,这个时候需要一个关联的中间表来维护他们之间的关系。
对于中间表的配置,你大可不去进行额外的配置让JPA自动生成,当然你也可以使用之前介绍的@JoinTable注解进行配置。

权限

和一对多、一对一不同,多对多一般是没有办法设置级联操作的。因为双方对应的都是集合对象,
而双方某一条数据都可能被对方多条数据关联。

代码

用户表

  1. @Data
  2. @Entity
  3. @Table(name = "cascade_user")
  4. public class User {
  5.  
  6. @Id
  7. @GeneratedValue
  8. private Long id;
  9. private String name;
  10. private Integer age;
  11. private Double point;
  12.  
  13. @ManyToMany(targetEntity = UserTag.class,
  14. cascade = {},
  15. fetch = FetchType.LAZY)
  16. // 假如不定义,jpa会生成“表名1”+“_”+“表名2”的中间表
  17. @JoinTable(
  18. // 定义中间表的名称
  19. name = "cascade_user_tag_table",
  20. // 定义中间表中关联User表的外键名
  21. joinColumns = {
  22. @JoinColumn(name = "user_id",
  23. referencedColumnName = "id")
  24. },
  25. // 定义中间表中关联UserTag表的外键名
  26. inverseJoinColumns = {
  27. @JoinColumn(name = "tags_id",
  28. referencedColumnName = "id")
  29. }
  30. )
  31. private Set<UserTag> tags;
  32.  
  33. }

用户标签表

  1. @Data
  2. @Entity
  3. @Table(name = "cascade_user_tag")
  4. public class UserTag {
  5.  
  6. @Id
  7. @GeneratedValue
  8. private Long id;
  9. private String tagName;
  10.  
  11. @ManyToMany(mappedBy = "tags",
  12. cascade = {},
  13. fetch = FetchType.LAZY)
  14. private List<User> userList;
  15. }

@ManyToMany

下面是@OneToOne以及与其配合的@JoinTable提供的注解参数

注解参数描述
@ManyToMany 描述一个多对多的关联.多对多关联上是两个一对多关联,但是在ManyToMany描述中,中间表是由ORM框架自动处理
 targetEntity目标类的实体
 cascade关联到目标的操作
 fetch是否使用延迟加载
 mappedBy反向关联
@JoinTable JoinTable在many-to-many关系的所有者一边定义。如果没有定义JoinTable,使用JoinTable的默认值。
 catalog表的目录
 foreignKey外键约束,创建表的时候使用
 indexes表的索引,在生成表的时候使用
@JoinColumns 关系存在多个JoinColumn,用JoinColumns定义多个JoinColumn的属性。
 foreignKey此列的外键约束
 valueJoinColumn的集合
 inverseJoinColumns联接表的外键列,该列引用不拥有关联的实体的主表
 joinColumns联接表的外键列,该列引用拥有关联的实体的主表
 name进行连接的表名称
 schema表的命名空间

测试

多对多的情况下,我们使用虽然User和UserTag的关系由中间表维护,但是我们在User中配置了中间表的关系维护,所以此时删除用户的时候可以成功删除,且可以成功移除中间表数据,但是这个时候移除UserTag数据的时候,却会抛出DataIntegrityViolationException异常。只能通过User移除中间关系。

  1. @Test
  2. public void testUserTag() {
  3. User defUser1 = UserMock.getDefUser1();
  4. UserTag defTag1 = UserTagMock.getDefTag1();
  5. UserTag defTag2 = UserTagMock.getDefTag2();
  6. defUser1 = userRepository.save(defUser1);
  7. defTag1 = userTagRepository.save(defTag1);
  8. defTag2 = userTagRepository.save(defTag2);
  9.  
  10. defUser1.getTags().add(defTag1);
  11. defUser1.getTags().add(defTag2);
  12.  
  13. defUser1 = userRepository.save(defUser1);
  14. // 此时会报错,因为中间表关系为User维护
  15. try {
  16. userTagRepository.delete(defTag2);
  17. } catch (Exception e) {
  18. log.info(e.getCause().getMessage());
  19. Assert.assertTrue(e instanceof DataIntegrityViolationException);
  20. }
  21. // 修改User表中关系
  22. defUser1.setTags(new HashSet <>());
  23. defUser1.getTags().add(defTag1);
  24. defUser1 = userRepository.save(defUser1);
  25. // 此时可以删除
  26. userTagRepository.delete(defTag2);
  27. // 直接删除用户没问题
  28. userRepository.delete(defUser1);
  29. }

本篇文章涉及的源码下载地址:https://gitee.com/daifyutils/springboot-samples

到此这篇关于Spring Boot 整合JPA 数据模型关联操作(一对一、一对多、多对多)的文章就介绍到这了,更多相关Spring Boot 整合JPA 数据模型关联内容请搜索w3xue以前的文章或继续浏览下面的相关文章希望大家以后多多支持w3xue!

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号