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

目录
  • 表关联
    • 一对一
    • 一对多和多对一
    • 多对多

表关联

上一篇介绍了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 数据模型关联内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Spring Boot 整合JPA 数据模型关联使用操作(一对一、一对多、多对多)

    目录 表关联 一对一 一对多和多对一 多对多 表关联 上一篇介绍了JPA的简单使用,这一篇介绍JPA在表关联上的使用 一对一 配置参数JPA对于数据实体一对一映射使用的是@OneToOne注解. 代码 User用户表 /** * 用户信息 * @author daify **/ @Data @Entity @Table(name = "cascade_user") public class User { @Id @GeneratedValue private Long id; priv

  • springboot 中整合mybatis多数据源不使用JPA

    目录 前言: 1 创建一个springboot项目 项目创建成功了,那么开始说下整合mybatis 上面的配置可以直接使用 如果上面的配置都完成的话那么我们来自动生成一下 在下图两个数据源完全生成好后的目录结构 前言: 小伙伴说能不能用springboot整合一下mybatis多数据源不使用JPA进行数据库连接操作.那么说干就干 1 创建一个springboot项目 流程就不介绍了,按图操作即可 创建项目后可能会发现,这个项目展示的有些不对劲啊,java类前面还有个小J号,这是由于maven包还

  • SpringBoot+Spring Data JPA整合H2数据库的示例代码

    目录 前言 Maven依赖 Conroller 实体类 Repository 数据库脚本文件 配置文件 启动项目 访问H2数据库 查看全部数据 H2数据库文件 运行方式 前言 H2数据库是一个开源的关系型数据库.H2采用java语言编写,不受平台的限制,同时支持网络版和嵌入式版本,有比较好的兼容性,支持相当标准的sql标准 提供JDBC.ODBC访问接口,提供了非常友好的基于web的数据库管理界面 官网:http://www.h2database.com/ Maven依赖 <!--jpa-->

  • springboot整合JPA过程解析

    这篇文章主要介绍了springboot整合JPA过程解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 接下来具体看看是怎么弄的. 1.新建一个springboot项目,选择web.data jdbc.data jpa.mysql driver. 2.建立以下目录及结构: pom.xml <?xml version="1.0" encoding="UTF-8"?> <project xmlns=&

  • Spring Boot整合JPA使用多个数据源的方法步骤

    介绍 JPA(Java Persistence API)Java 持久化 API,是 Java 持久化的标准规范,Hibernate 是持久化规范的技术实现,而 Spring Data JPA 是在 Hibernate 基础上封装的一款框架. 第一次使用 Spring JPA 的时候,感觉这东西简直就是神器,几乎不需要写什么关于数据库访问的代码一个基本的 CURD 的功能就出来了.在这篇文章中,我们将介绍 Spring Boot 整合 JPA 使用多个数据源的方法. 开发环境: Spring B

  • Spring Boot整合Mybatis并完成CRUD操作的实现示例

    MyBatis 是一款优秀的持久层框架,被各大互联网公司使用,本文使用Spring Boot整合Mybatis,并完成CRUD操作. 为什么要使用Mybatis?我们需要掌握Mybatis吗? 说的官方一点: MyBatis 是一款优秀的持久层框架,它支持定制化 SQL.存储过程以及高级映射.MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集.MyBatis 可以使用简单的 XML 或注解来配置和映射原生信息,将接口和 Java 的 POJOs(Plain Ordina

  • Spring boot整合Mybatis实现级联一对多CRUD操作的完整步骤

    前言 在关系型数据库中,随处可见表之间的连接,对级联的表进行增删改查也是程序员必备的基础技能.关于Spring Boot整合Mybatis在之前已经详细写过,不熟悉的可以回顾Spring Boot整合Mybatis并完成CRUD操作,这是本文操作的基础.本文先准备一个测试的数据库,然后使用MyBatis Generator进行部分代码自动生成,再以一个例子来展示稍微高级点的操作:使用Mybatis完成级联一对多的CRUD操作. 数据库准备 数据库用到三张表:user表,role表,user_ro

  • Spring Boot整合Spring Data JPA过程解析

    Spring Boot整合Spring Data JPA 1)加入依赖 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <dependency> <groupId>mysql</groupId> &l

  • Spring Boot 整合持久层之Spring Data JPA

    目录 整合Spring Data JPA 1. 创建数据库 2. 创建项目 3. 数据库配置 4. 创建实体类 5. 创建 BookDao 接口 6. 创建 BookService 7. 创建 BookController 8. 测试 整合Spring Data JPA JPA (Java Persistence API)和 Spring Data 是两个范畴的概念. Hibernate 是一个 ORM 框架,JPA 则是一种ORM,JPA 和 Hibernate 的关系就像 JDBC 与 JD

  • Spring Boot整合mybatis使用注解实现动态Sql、参数传递等常用操作(实现方法)

    前面介绍了Spring Boot 整合mybatis 使用注解的方式实现数据库操作,介绍了如何自动生成注解版的mapper 和pojo类. 接下来介绍使用mybatis 常用注解以及如何传参数等数据库操作中的常用操作. 其实,mybatis 注解方式 和 XML配置方式两者的使用基本上相同,只有在构建 SQL 脚本有所区别,所以这里重点介绍两者之间的差异,以及增删改查,参数传递等注解的常用操作. 详解SpringBoot 快速整合Mybatis(去XML化+注解进阶)已经介绍过了,不清楚的朋友可

  • Spring Boot整合持久层之JPA多数据源

    目录 JPA 多数据源 准备工作 创建 JPA 配置 创建 Repository 创建 Controller JPA 多数据源 JPA 和 MyBatis 配置多数据源类似,不同的是,JPA 配置时主要提供不同的 LocalContainerEntityManagerFactoryBean 以及事务管理器. 项目目录结构如下: 准备工作 创建 Spring Boot Web 项目,添加如下依赖: <dependency> <groupId>org.springframework.b

  • Spring Boot 整合 Reactor实例详解

    目录 引言 1 创建项目 2 集成 H2 数据库 3 创建测试类 3.1 user 实体 3.2 UserRepository 3.3 UserService 3.4 UserController 3.5 SpringReactorApplication 添加注解支持 测试 总结 引言 Reactor 是一个完全非阻塞的 JVM 响应式编程基础,有着高效的需求管理(背压的形式).它直接整合 Java8 的函数式 API,尤其是 CompletableFuture, Stream,还有 Durat

  • 微服务Spring Boot 整合 Redis 实现好友关注功能

    目录 引言 一.Redis 实现好友关注 – 关注与取消关注 二.Redis 实现好友关注 – 共同关注功能 小结 引言 本博文参考 黑马 程序员B站 Redis课程系列 在点评项目中,有这样的需求,如何实现笔记的好友关注.以及发布笔记后推送消息功能? 使用Redis 的 好友关注.以及发布笔记后推送消息功能 一.Redis 实现好友关注 – 关注与取消关注 需求:针对用户的操作,可以对用户进行关注和取消关注功能. 在探店图文的详情页面中,可以关注发布笔记的作者 具体实现思路:基于该表数据结构,

随机推荐