表关联

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

一对一

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

代码

User用户表

/**
 * 用户信息
 * @author daify
 **/
@Data
@Entity
@Table(name = "cascade_user")
public class User {

    @Id
    @GeneratedValue
    private Long id;
    
    private String name;
    
    private Integer age;
    
    private Double point;
    /**
     * CascadeType包含的类别  级联:给当前设置的实体操作另一个实体的权限
     *      CascadeType.ALL 级联所有操作
     *      CascadeType.PERSIST 级联持久化(保存)操作
     *      CascadeType.MERGE   级联更新(合并)操作
     *      CascadeType.REMOVE  级联删除操作
     *      CascadeType.REFRESH 级联刷新操作
     *      CascadeType.DETACH 级联分离操作,如果你要删除一个实体,但是它有外键无法删除,这个级联权限会撤销所有相关的外键关联。
     */
    @OneToOne(targetEntity = UserCart.class,
            cascade = CascadeType.ALL,
            mappedBy = "user")
    private UserCart userCart;

    @OneToOne(targetEntity = UserInfo.class,
            cascade = CascadeType.ALL)
    @JoinTable(name = "user_info_table",
            joinColumns = @JoinColumn(name="user_id"),
            inverseJoinColumns = @JoinColumn(name = "info_id"))
    private UserInfo userInfo;

}

UserCart用户购物车表

@Data
@Entity
@Table(name = "cascade_user_cart")
public class UserCart {

    @Id
    @GeneratedValue
    private Long id;
    
    private Integer count;
    
    private Double totalMoney;
    
    private Date updateTime;

    /**
     * CascadeType包含的类别  级联:给当前设置的实体操作另一个实体的权限
     *      CascadeType.ALL 级联所有操作
     *      CascadeType.PERSIST 级联持久化(保存)操作
     *      CascadeType.MERGE   级联更新(合并)操作
     *      CascadeType.REMOVE  级联删除操作
     *      CascadeType.REFRESH 级联刷新操作
     *      CascadeType.DETACH 级联分离操作,如果你要删除一个实体,但是它有外键无法删除,这个级联权限会撤销所有相关的外键关联。
     */
    @OneToOne(targetEntity = User.class,
            cascade = {},
            fetch = FetchType.LAZY)
    private User user;
}

用户信息

@Data
@Entity
@Table(name = "cascade_user_info")
public class UserInfo {

    @Id
    @GeneratedValue
    private Long id;
    
    private String userInfo;
    
    private String config;
}

上面例子中尝试使用了两种方式来维护一对一的关系,首先在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并没有赋予任何权限,所以保存用户的时候可以关联保存用户购物车,删除购物车的时候并不会删除用户,但是删除用户的时候会删除购物车

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

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

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

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

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

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

一对多和多对一

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

代码

订单表

@Data
@Entity
@Table(name = "cascade_order")
public class Order {

    @Id
    @GeneratedValue
    private Long id;
    
    private String orderNo;
    
    private Integer count;
    
    private Double money;

    @OneToMany(mappedBy = "order",
            cascade = CascadeType.ALL,
            fetch = FetchType.EAGER)
    private List<OrderItem> orderItems;
    
    /**
    * 重写toString防止死循环
    * @return 
    */
    @Override 
    public String toString() {
        return "OrderItem{"   "id="   id   ", productId='"   productId   '\''   ", money="   money
                 ", count="   count   ", productName='"   productName   '\''   ", order="   order
                 '}';
    }
    
}

订单子项

@Data
@Entity
@Table(name = "cascade_order_item")
public class OrderItem {

    @Id
    @GeneratedValue
    private Long id;

    private String productId;

    private Double money;
    
    private Integer count;
    
    private String productName;
    
    @ManyToOne(targetEntity = Order.class,
            cascade = {},
            fetch = FetchType.EAGER)
    @JoinColumn(
            // 设置在OrderItem表中的关联字段(外键)
            name="order_id"
    )
    // 防止json序列化死循环问题解决
    @JsonBackReference
    private Order order;
}

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

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

@ManyToOne,@OneToMany

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

测试

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

    @Test
    public void testOrder() {
        Order defOrder1 = OrderMock.getDefOrder1();
        List <OrderItem> allDefOrder = OrderItemMock.getAllDefOrder();
        // order 维持orderItem关系
        defOrder1.setOrderItems(allDefOrder);
        // orderItem维持order关系
        allDefOrder.forEach(o -> o.setOrder(defOrder1));
        Order save = orderRepository.save(defOrder1);
        List <OrderItem> all = orderItemRepository.findAll();
        Assert.assertTrue(all.size() == allDefOrder.size());

        orderItemRepository.delete(all.get(0));
        Order order = orderRepository.findById(save.getId()).get();
        Assert.assertNotNull(order);

        orderRepository.deleteById(order.getId());
        List <OrderItem> all1 = orderItemRepository.findAll();
        Assert.assertTrue(all1.size() == 0);

    }

多对多

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

权限

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

代码

用户表

@Data
@Entity
@Table(name = "cascade_user")
public class User {

    @Id
    @GeneratedValue
    private Long id;
    
    private String name;
    
    private Integer age;
    
    private Double point;

    @ManyToMany(targetEntity = UserTag.class,
            cascade = {},
            fetch = FetchType.LAZY)
    // 假如不定义,jpa会生成“表名1” “_” “表名2”的中间表
    @JoinTable(
            // 定义中间表的名称
            name = "cascade_user_tag_table",
            // 定义中间表中关联User表的外键名
            joinColumns = {
                    @JoinColumn(name = "user_id",
                            referencedColumnName = "id")
            },
            // 定义中间表中关联UserTag表的外键名
            inverseJoinColumns = {
                    @JoinColumn(name = "tags_id",
                            referencedColumnName = "id")
            }
    )
    private Set<UserTag> tags;

}

用户标签表

@Data
@Entity
@Table(name = "cascade_user_tag")
public class UserTag {

    @Id
    @GeneratedValue
    private Long id;
    
    private String tagName;

    @ManyToMany(mappedBy = "tags",
            cascade = {},
            fetch = FetchType.LAZY)
    private List<User> userList;
}

@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 此列的外键约束
  value JoinColumn的集合
  inverseJoinColumns 联接表的外键列,该列引用不拥有关联的实体的主表
  joinColumns 联接表的外键列,该列引用拥有关联的实体的主表
  name 进行连接的表名称
  schema 表的命名空间

测试

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

    @Test
    public void testUserTag() {
        User defUser1 = UserMock.getDefUser1();
        UserTag defTag1 = UserTagMock.getDefTag1();
        UserTag defTag2 = UserTagMock.getDefTag2();
        defUser1 = userRepository.save(defUser1);
        defTag1 = userTagRepository.save(defTag1);
        defTag2 = userTagRepository.save(defTag2);

        defUser1.getTags().add(defTag1);
        defUser1.getTags().add(defTag2);

        defUser1 = userRepository.save(defUser1);
        // 此时会报错,因为中间表关系为User维护
        try {
            userTagRepository.delete(defTag2);
        } catch (Exception e) {
            log.info(e.getCause().getMessage());
            Assert.assertTrue(e instanceof DataIntegrityViolationException);
        }
        // 修改User表中关系
        defUser1.setTags(new HashSet <>());
        defUser1.getTags().add(defTag1);
        defUser1 = userRepository.save(defUser1);
        // 此时可以删除
        userTagRepository.delete(defTag2);
        // 直接删除用户没问题
        userRepository.delete(defUser1);
    }

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

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

Spring Boot 整合JPA 数据模型关联使用操作(一对一、一对多、多对多)的更多相关文章

  1. Spring JdbcTemplate执行数据库操作详解

    JdbcTemplate是Spring框架自带的对JDBC操作的封装,目的是提供统一的模板方法使对数据库的操作更加方便、友好,效率也不错,这篇文章主要介绍了Spring JdbcTemplate执行数据库操作,需要的朋友可以参考下

  2. Spring Batch批处理框架操作指南

    Spring Batch 是 Spring 提供的一个数据处理框架。企业域中的许多应用程序需要批量处理才能在关键任务环境中执行业务操作,这篇文章主要介绍了Spring Batch批处理框架操作指南,需要的朋友可以参考下

  3. Spring详细讲解@Autowired注解

    @Autowired注解可以用在类属性,构造函数,setter方法和函数参数上,该注解可以准确地控制bean在何处如何自动装配的过程。在默认情况下,该注解是类型驱动的注入

  4. 使用Spring AOP实现用户操作日志功能

    这篇文章主要介绍了使用Spring AOP实现了用户操作日志功能,功能实现需要一张记录日志的log表,结合示例代码给大家讲解的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下

  5. Spring Security认证器实现过程详解

    一些权限框架一般都包含认证器和决策器,前者处理登陆验证,后者处理访问资源的控制,这篇文章主要介绍了Spring Security认证器实现过程,需要的朋友可以参考下

  6. spring学习JdbcTemplate数据库事务管理

    这篇文章主要为大家介绍了spring学习JdbcTemplate数据库事务管理,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪

  7. Spring Boot 集成Redisson实现分布式锁详细案例

    这篇文章主要介绍了Spring Boot 集成Redisson实现分布式锁详细案例,文章围绕主题展开详细的内容介绍,具有一定的参考价值,需要的朋友可以参考一下

  8. Spring Security实现接口放通的方法详解

    在用Spring Security项目开发中,有时候需要放通某一个接口时,我们需要在配置中把接口地址配置上,这样做有时候显得麻烦。本文将通过一个注解的方式快速实现接口放通,感兴趣的可以了解一下

  9. 如何利用Spring把元素解析成BeanDefinition对象

    这篇文章主要介绍了如何利用Spring把元素解析成BeanDefinition对象,文章围绕主题展开详细的内容介绍,具有一定的参考价值,需要的小伙伴可以参考一下

  10. Spring解决循环依赖问题及三级缓存的作用

    这篇文章主要介绍了Spring解决循环依赖问题及三级缓存的作用,所谓的三级缓存只是三个可以当作是全局变量的Map,Spring的源码中大量使用了这种先将数据放入容器中等使用结束再销毁的代码风格

随机推荐

  1. 基于EJB技术的商务预订系统的开发

    用EJB结构开发的应用程序是可伸缩的、事务型的、多用户安全的。总的来说,EJB是一个组件事务监控的标准服务器端的组件模型。基于EJB技术的系统结构模型EJB结构是一个服务端组件结构,是一个层次性结构,其结构模型如图1所示。图2:商务预订系统的构架EntityBean是为了现实世界的对象建造的模型,这些对象通常是数据库的一些持久记录。

  2. Java利用POI实现导入导出Excel表格

    这篇文章主要为大家详细介绍了Java利用POI实现导入导出Excel表格,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

  3. Mybatis分页插件PageHelper手写实现示例

    这篇文章主要为大家介绍了Mybatis分页插件PageHelper手写实现示例,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪

  4. (jsp/html)网页上嵌入播放器(常用播放器代码整理)

    网页上嵌入播放器,只要在HTML上添加以上代码就OK了,下面整理了一些常用的播放器代码,总有一款适合你,感兴趣的朋友可以参考下哈,希望对你有所帮助

  5. Java 阻塞队列BlockingQueue详解

    本文详细介绍了BlockingQueue家庭中的所有成员,包括他们各自的功能以及常见使用场景,通过实例代码介绍了Java 阻塞队列BlockingQueue的相关知识,需要的朋友可以参考下

  6. Java异常Exception详细讲解

    异常就是不正常,比如当我们身体出现了异常我们会根据身体情况选择喝开水、吃药、看病、等 异常处理方法。 java异常处理机制是我们java语言使用异常处理机制为程序提供了错误处理的能力,程序出现的错误,程序可以安全的退出,以保证程序正常的运行等

  7. Java Bean 作用域及它的几种类型介绍

    这篇文章主要介绍了Java Bean作用域及它的几种类型介绍,Spring框架作为一个管理Bean的IoC容器,那么Bean自然是Spring中的重要资源了,那Bean的作用域又是什么,接下来我们一起进入文章详细学习吧

  8. 面试突击之跨域问题的解决方案详解

    跨域问题本质是浏览器的一种保护机制,它的初衷是为了保证用户的安全,防止恶意网站窃取数据。那怎么解决这个问题呢?接下来我们一起来看

  9. Mybatis-Plus接口BaseMapper与Services使用详解

    这篇文章主要为大家介绍了Mybatis-Plus接口BaseMapper与Services使用详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪

  10. mybatis-plus雪花算法增强idworker的实现

    今天聊聊在mybatis-plus中引入分布式ID生成框架idworker,进一步增强实现生成分布式唯一ID,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

返回
顶部