SpringDataJpa(五) 多表操作-一对多

SpringDataJpa(五) 多表操作-一对多

1.多表设计

1)表之间关系划分

数据库中多表之间存在着三种关系:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IKook7kZ-1582188278641)(./1582184919123.png)]
     一对多关系可以看作两种: 一对多,多对一

2)JPA 表关系分析步骤

1.确定两张表之间的关系
2.在数据库中实现两张表的关系
3.在实体类中描述两张表的关系(包含|继承)
4.配置实体类与数据库表的关系映射



2.一对多操作

1) 实例分析:

在CRM 客户关系管理系统中: 客户与联系人 是一对多关系
客户:客户是指顾客方,可以理解为公司,如公司A
联系人:公司中负责对接(洽谈合同…)的联系人,如公司A中的员工
一个客户可能有多个联系人(存在一对多关系)
    eg:商务联系人、财务联系人、技术联系人



2) 表关系建立

如图:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-aNIGMHqg-1582188278645)(./1582185905981.png)]

3)实体类与数据库表关系映射
Customer.java

客户实体 一的一方

  /**
 *  客户实体类
 */
@Entity
@Table(name = "cst_customer")
public class Customer {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "cust_id")
    private Long custId;
    @Column(name = "cust_name")
    private String custName;
    @Column(name = "cust_industry")
    private String custIndustry;
    @Column(name = "cust_source")
    private String custSource;
    @Column(name = "cust_level")
    private String custLevel;
    @Column(name = "cust_address")
    private String custAddress;
    @Column(name = "cust_phone")
    private String custPhone;

    /**
     *  外键关联  一对多
     *     客户:联系人:1:n
     *         @OneToMany(targetEntity = 关联实体.class)
     *         @JoinColumn(name = "从表外键名称(在多的一方表中)",
     *                      referencedColumnName = "主表主键id")
     *         最维护外键
     */
     @OneToMany(targetEntity = LinkMan.class)
     @JoinColumn(name = "lkm_cust_id", referencedColumnName = "cust_id")

    /**
     *  放弃外键维护权,外键维护参照 mappedBy= "多的一方维护外键的属性"
     *    cascade: 级联设置
     *      CascadeType.ALL: 增删改查全部级联操作
     *
      */
    //@OneToMany(mappedBy = "customer", cascade = CascadeType.ALL)
    private Set<LinkMan> linkMans = new HashSet<LinkMan>();
    
     //getter setter 方法
}
LinkMan.java

联系人实体类,多的一方

   /**
 *  联系人实体类
 */
@Entity
@Table(name = "cst_linkman")
public class LinkMan {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "lkm_id")
    private Long lkmId;  //联系人id
    @Column(name = "lkm_name")
    private String lkmName;  //联系人姓名
    @Column(name = "lkm_gender")
    private String lkmGender; //联系人性别
    @Column(name = "lkm_phone")
    private String lkmPhone; //联系人办公电话
    @Column(name = "lkm_mobile")
    private String lkmMobile; //联系人移动电话
    @Column(name = "lkm_email")
    private String lkmEmail;  //联系人邮箱
    @Column(name = "lkm_position")
    private String lkmPosition;//联系人职位
    @Column(name = "lkm_memo")
    private String lkmMemo;//联系人备注

    /**
     * 外键关联
     *    多对一
     *     @JoinColumn: 维护外键
     */
    @ManyToOne(targetEntity = Customer.class)
    @JoinColumn(name = "lkm_cust_id", referencedColumnName = "cust_id")
    private Customer customer;
    
    //getter setter 方法
}
4) 映射注解说明

@OneToMany:

  • 作用: 建立一对多关系映射
  • 属性:
    • targetEntityCLass: 指定多的一方类的字节码

      • eg:@OneToMany(targetEntity = LinkMan.class)
    • mapperBy: 放弃外键维护权力,由从表维护外键,指定多的一方引用一的一方对象名称

      • eg: LinkMan: private Customer customer;
             Customer: @OneToMany(mappedBy = “customer”)
    • cascade: 指定要使用的级联操作

    • fetch:指定是否使用延迟加载

    • orphanRemoval:是否使用孤儿删除

@ManyToOne

  • 作用:建立多对一的关系
  • 属性:
  • targetEntityClass:指定一的一方实体类字节码
  • cascade:指定要使用的级联操作
  • fetch:指定是否采用延迟加载
    • fetch = FetchType.EAGER 立即加载
    • fetch = FetchType.LAZY 延迟加载
  • optional:关联是否可选。如果设置为false,则必须始终存在非空关系。

@JoinColumn

  • 作用:用于定义主键和外键的对应关系
  • 属性:
    • name:指定外键字段的名称(以数据库表为准)
    • referencedColumnName:指定引用主表的主键字段名称
    • unique:是否唯一,默认值不唯一
    • nullable:是否允许空值,默认允许
    • insertable:是否允许插入,默认允许
    • updatable:是否允许更新,默认允许
    • columnDefinition:列定义信息



5) 双向关系添加操作

Customer.java

@OneToMany(targetEntity = LinkMan.class)
@JoinColumn(name = "lkm_cust_id", referencedColumnName = "cust_id")
private Set<LinkMan> linkMans = new HashSet<LinkMan>();

LinkMan.java

@ManyToOne(targetEntity = Customer.class)
@JoinColumn(name = "lkm_cust_id", referencedColumnName = "cust_id")
private Customer customer;

双方都有外键维护权,都能找到对方,故为双向关系

测试

    @Autowired
    private CustomerDao customerDao;

    @Autowired
    private LinkManDao linkManDao;
    
    /**
     * 双方同时维护外键测试
     *    多出一条update 语句
     *      原因: 多的一方在插入数据时自动维护外键
     *              而后一的一方再次更新外键(多余操作)
     */
    @Test
    @Transactional
    @Rollback(false)
    public void test_save03(){
        Customer customer = new Customer();
        customer.setCustName("李四");

        LinkMan linkMan = new LinkMan();
        linkMan.setLkmName("王二");

        /**
         *  双方同时维护外键
         */
        linkMan.setCustomer(customer);
        customer.getLinkMans().add(linkMan);

        customerDao.save(customer);
        linkManDao.save(linkMan);

    }

问题:多的一方在插入时就已经设置了外键,一的一方又updte重新修改了外键(虽然外键没变化,该操作是多余的)

  • 两条inser语句,一条update语句(多余的)

解决:一的一方放弃外键维护权
Customer.java

   @OneToMany(mappedBy = "customer")
    private Set<LinkMan> linkMans = new HashSet<LinkMan>();


6)级联删除

删除从表数据:可任意删除
删除主表数据:

  • 无从表关联:随便删除,无影响
  • 有从表关联:
    • 默认情况下,会将从表关联的外键字段设为null,然后删除主表数据(如果数据库表结构上设置外键不能为控,则会报错)
    • 如果主表配置放弃外键维护权,则不能删除(有外键与之关联)
    • 设置级联删除,删除主表记录的同时,从表与之有关联的记录也会被删除,从表删除不影响主表
cascade:配置级联操作
  • CascadeType.MERGE 级联更新
  • CascadeType.PERSIST 级联保存
  • CascadeType.REFRESH 级联刷新
  • CascadeType.REMOVE 级联删除
  • CascadeType.ALL 级联所有操作
    Customer.java
@OneToMany(mappedBy = "customer", cascade = CascadeType.ALL)
private Set<LinkMan> linkMans = new HashSet<LinkMan>();
测试:
    /**
     *  级联删除
     *     删除客户的同时,删除与客户关联的所有联系人
     */
    @Test
    @Transactional
    @Rollback(false)
    public void test_cascadeDelete(){
        Customer customer = customerDao.findOne(1l);
        customerDao.delete(customer);
    }

发布了47 篇原创文章 · 获赞 7 · 访问量 2331

猜你喜欢

转载自blog.csdn.net/qq_43616898/article/details/104413394