• JPA中的一对多双向关联与级联操作


    学习Spring有两周时间了 , 个人觉得服务端主要实现的是数据关系的维护和数据结构的制定 , 以及由业务需求产生的CRUD , 只要保证对前端提供的接口稳定高效响应 , 具体的前端实现完全不关心.

    这个是接触后端的一个感受 , Spring boot使用了特定的方式来进行配置 ,大大简化了后端开发人员的开发工作量 ,比如JPA:用来操作实体对象,执行CRUD操作,框架在后台替我们完成所有的事情,开发者从繁琐的JDBC和SQL代码中解脱出来 ,此时数据之间的关系维护和数据结构的制定就显得尤为关键 , 下面记录数据表对应关系中的常见关系:一对多关系.

    数据表关系

    下面用实际场景简单理解数据表之间的关系

    一对一:我有一个身份证号 ;
    一对多:我有多张银行卡;
    多对多:我是招行、交行、建行的客户,但是这些银行用户均过亿。

    一对多双向关联

    下面以订单order和订单项orderitem来展示

    创建订单实体类,代码如下:

    package learn.jpa.entity;
    
    import java.util.HashSet;
    import java.util.Set;
    
    import javax.persistence.CascadeType;
    import javax.persistence.Column;
    import javax.persistence.Entity;
    import javax.persistence.Id;
    import javax.persistence.OneToMany;
    
    /**
     * 订单
     */
    @Entity // 定义类为实体类
    public class Order {
    
        private String orderid;
        private float amount = 0f;
        private Set<OrderItem> item = new HashSet<OrderItem>();
        
        @Id // 实体标识符,因为是字符串类型,所有不能用 @GeneratedValue,只能人为的赋值
        @Column(length=20)
        public String getOrderid() {
            return orderid;
        }
        public void setOrderid(String orderid) {
            this.orderid = orderid;
        }
        @Column(nullable = false)
        public float getAmount() {
            return amount;
        }
        public void setAmount(float amount) {
            this.amount = amount;
        }
        @OneToMany(cascade={CascadeType.REFRESH,CascadeType.PERSIST,CascadeType.MERGE})
        public Set<OrderItem> getItem() {
            return item;
        }
        public void setItem(Set<OrderItem> item) {
            this.item = item;
        }
        
    }
    /**
     * 1 - N
     * 多的一端为关系维护端,关系维护端负责外键记录的更新
     *
     */

    创建订单项实体类,代码如下

    package learn.jpa.entity;
    
    import javax.persistence.Column;
    import javax.persistence.Entity;
    import javax.persistence.GeneratedValue;
    import javax.persistence.Id;
    
    /**
     * 订单项
     */
    @Entity // 定义类为实体类
    public class OrderItem {
    
        private int id;
        private String productName;
        private float sellPrice = 0f;
        private Order order;
        
        @Id // 实体标识符
        @GeneratedValue // 主键自动增长
        public int getId() {
            return id;
        }
        public void setId(int id) {
            this.id = id;
        }
        
        @Column(length=40,nullable=false)
        public String getProductName() {
            return productName;
        }
        public void setProductName(String productName) {
            this.productName = productName;
        }
        
        @Column(nullable=false)
        public float getSellPrice() {
            return sellPrice;
        }
        public void setSellPrice(float sellPrice) {
            this.sellPrice = sellPrice;
        }
        public Order getOrder() {
            return order;
        }
        public void setOrder(Order order) {
            this.order = order;
        }
    }

    注解:

    1、@OneToMany(fetch=FetchType,cascade=CascadeType)

    @OneToMany描述一个一对多的关联,该属性应该为集体类型,在数据库中并没有实际字段.

    fetch:表示该属性的读取策略,有EAGER和LAZY两种,分别表示主支抓取和延迟加载,默认为EAGER. 

    cascade:表示级联操作策略,对于OneToMany类型的关联非常重要,通常该实体更新或删除时,其关联的实体也应当被更新或删除

    (1)、CascadeType.MERGE级联更新:若items属性修改了那么order对象保存时同时修改items里的对象。对应EntityManager的merge方法

    (2)、CascadeType.PERSIST级联刷新:获取order对象里也同时也重新获取最新的items时的对象。对应EntityManager的refresh(object)方法有效。即会重新查询数据库里的最新数据 

    (3)、CascadeType.REFRESH级联保存:对order对象保存时也对items里的对象也会保存。对应EntityManager的presist方法

    (4)、CascadeType.REMOVE级联删除:对order对象删除也对items里的对象也会删除。对应EntityManager的remove方法

    CascadeType.PERSIST只有A类新增时,会级联B对象新增。若B对象在数据库存(跟新)在则抛异常(让B变为持久态)

    CascadeType.MERGE指A类新增或者变化,会级联B对象(新增或者变化)

    CascadeType.REMOVE只有A类删除时,会级联删除B类;

    CascadeType.ALL包含所有;

    综上:大多数情况用CascadeType.MERGE就能达到级联跟新又不报错,用CascadeType.ALL时要斟酌下CascadeType.REMOVE

    optional:是否允许该字段为null,该属性应该根据数据库表的外键约束来确定,默认为true

    一对多延迟加载与关系维护

    订单实体类,代码:

    package learn.jpa.entity;
    
    import java.util.HashSet;
    import java.util.Set;
    
    import javax.persistence.CascadeType;
    import javax.persistence.Column;
    import javax.persistence.Entity;
    import javax.persistence.FetchType;
    import javax.persistence.Id;
    import javax.persistence.OneToMany;
    import javax.persistence.Table;
    
    /**
     * 订单
     */
    @Entity // 定义类为实体类
    @Table(name="orders")
    public class Order {
    
        private String orderid;
        private float amount = 0f;
        private Set<OrderItem> item = new HashSet<OrderItem>();
        
        @Id // 实体标识符,因为是字符串类型,所有不能用 @GeneratedValue,只能人为的赋值
        @Column(length=20)
        public String getOrderid() {
            return orderid;
        }
        public void setOrderid(String orderid) {
            this.orderid = orderid;
        }
        @Column(nullable = false)
        public float getAmount() {
            return amount;
        }
        public void setAmount(float amount) {
            this.amount = amount;
        }
        /**
         * 如果是一对多或多对多 fetch 默认是延迟加载,反之是立即加载
         * mappedBy="order" 表示由实体 OrderItem 中的 order 属性维护
         * @return
         */
        @OneToMany(cascade={CascadeType.REFRESH,CascadeType.PERSIST,CascadeType.MERGE,CascadeType.REMOVE},
                fetch=FetchType.LAZY,mappedBy="order")
        public Set<OrderItem> getItem() {
            return item;
        }
        public void setItem(Set<OrderItem> item) {
            this.item = item;
        }
    
        public void addOrderItem(OrderItem orderItem){
            orderItem.setOrder(this);
            this.item.add(orderItem);
        }
        
    }
    /**
     * 1 - N
     * 多的一端为关系维护端,关系维护端负责外键记录的更新
     *
     */

    mappedBy只有在双向关联时,才会使用这个属性

    mappedBy=”另一方的关系引用属性”

    订单项实体类,代码:

    package learn.jpa.entity;
    
    import javax.persistence.CascadeType;
    import javax.persistence.Column;
    import javax.persistence.Entity;
    import javax.persistence.FetchType;
    import javax.persistence.GeneratedValue;
    import javax.persistence.Id;
    import javax.persistence.JoinColumn;
    import javax.persistence.ManyToOne;
    import javax.persistence.Table;
    
    /**
     * 订单项
     */
    @Entity // 定义类为实体类
    public class OrderItem {
    
        private int id;
        private String productName;
        private float sellPrice = 0f;
        private Order order;
        
        @Id // 实体标识符
        @GeneratedValue // 主键自动增长
        public int getId() {
            return id;
        }
        public void setId(int id) {
            this.id = id;
        }
        
        @Column(length=40,nullable=false)
        public String getProductName() {
            return productName;
        }
        public void setProductName(String productName) {
            this.productName = productName;
        }
        
        @Column(nullable=false)
        public float getSellPrice() {
            return sellPrice;
        }
        public void setSellPrice(float sellPrice) {
            this.sellPrice = sellPrice;
        }
        @ManyToOne(cascade={CascadeType.MERGE,CascadeType.REFRESH},fetch=FetchType.EAGER,optional=false)
        @JoinColumn(name="order_id")
        public Order getOrder() {
            return order;
        }
        public void setOrder(Order order) {
            this.order = order;
        }
    }

    joinColumns属性表示,在保存关系中的表中,所保存关联关系的外键的字段。并配合@JoinColumn标记使用。

    小结

    理解关联表中存储的关系对含义, 记下使用的套路基本上就没什么问题了 ,下篇文章将记录多对多双向关联与级联操作 .

  • 相关阅读:
    存储过程、触发器、数据完整性
    结构化查询语句
    HDU 1205 鸽巢原理
    ZOJ 2386 容斥原理
    poj 2356 抽屉原理
    HDU 2048 错排
    数据库原理及应用——关系数据库
    数据库原理及应用——绪论
    UVA
    汇编试验十一:编写子程序
  • 原文地址:https://www.cnblogs.com/fuyaozhishang/p/10166890.html
Copyright © 2020-2023  润新知