JPA(六)多表配置

1、一对多实体类的配置

我们采用的示例为客户和联系人。
客户:指的是一家公司,我们记为 A。
联系人:指的是 A 公司中的员工。
在不考虑兼职的情况下,公司和员工的关系即为一对多。

/**
* 客户的实体类
* 明确使用的注解都是 JPA 规范的
* 所以导包都要导入 javax.persistence 包下的
*/
@Entity // 声明实体类
@Table(name = "cst_customer") // 建立实体类和表的映射关系
public class Customer {

    @Id // 声明当前私有属性为主键
    @GeneratedValue(strategy = GenerationType.IDENTITY) // 配置主键的生成策略
    @Column(name = "cust_id") // 指定和表中 cust_id 字段的映射关系
    private Long custId;

    @Column(name = "cust_name") // 指定和表中 cust_name 字段的映射关系
    private String custName;
    @Column(name = "cust_source") // 指定和表中 cust_source 字段的映射关系
    private String custSource;
    @Column(name = "cust_industry") // 指定和表中 cust_industry 字段的映射关系
    private String custIndustry;
    @Column(name = "cust_level") // 指定和表中 cust_level 字段的映射关系
    private String custLevel;
    @Column(name = "cust_address") // 指定和表中 cust_address 字段的映射关系
    private String custAddress;
    @Column(name = "cust_phone") // 指定和表中 cust_phone 字段的映射关系
    private String custPhone;

    //配置客户和联系人的一对多关系
    @OneToMany(targetEntity = LinkMan.class, mappedBy = "customer", cascade = CascadeType.PERSIST)
    private Set linkMans = new HashSet<>(0);
}

由于联系人是多的一方,在实体类中要体现出,每个联系人只能对应一个客户,代码如下:

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

    @Id
    @Column(name="lkm_id")
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    private Long lkmId;

    @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;

    //多对一关系映射
    @ManyToOne(targetEntity=Customer.class,cascade=CascadeType.PERSIST)
    @JoinColumn(name="cust_id",referencedColumnName="cust_id")
    private Customer customer;
}

2、映射的注解说明

- @OneToMany:

作用:
  建立一对多的关系映射
属性:
  targetEntityClass:指定多的多方的类的字节码
  cascade:指定要使用的级联操作
  fetch:指定是否采用延迟加载
  
  mappedBy:指定从表实体类中引用主表对象的名称。
  orphanRemoval:是否使用孤儿删除

- @ManyToOne

作用:
  建立多对一的关系
属性:
  targetEntityClass:指定一的一方实体类字节码
  cascade:指定要使用的级联操作
  fetch:指定是否采用延迟加载

- @JoinColumn
作用:
  用于定义主键字段和外键字段的对应关系。
属性:
  name:指定外键字段的名称
  referencedColumnName:指定引用主表的主键字段名称
  unique:是否唯一。默认值不唯一
  nullable:是否允许为空。默认值允许。
  insertable:是否允许插入。默认值允许。
  updatable:是否允许更新。默认值允许。
  columnDefinition:列的定义信息。

3、 一对多的操作

/**
* 1、保存操作
* 需求:
* 保存一个客户和一个联系人
* 要求:
* 创建一个客户对象和一个联系人对象
* 建立客户和联系人之间关联关系(双向一对多的关联关系)
* 先保存客户,再保存联系人
*
*/
@Test
    public void testSave() {
        // 创建客户和联系人对象
        Customer c = new Customer();// 瞬时态
        c.setCustName("百度云");
        c.setCustLevel("VIP 客户");
        c.setCustSource("网络");
        c.setCustIndustry("商业办公");
        c.setCustAddress("北京");
        c.setCustPhone("010-84389340");
        LinkMan l = new LinkMan();// 瞬时态
        l.setLkmName("百度云 联系人");
        l.setLkmGender("male");
        l.setLkmMobile("13811111111");
        l.setLkmPhone("010-34785348");
        l.setLkmEmail("[email protected]");
        l.setLkmPosition("老师");
        l.setLkmMemo("还行吧");
        // 建立他们的双向一对多关联关系
        l.setCustomer(c);
        c.getLinkMans().add(l);
        EntityManager em = JpaUtil.getEntityManager();
        EntityTransaction tx = em.getTransaction();
        tx.begin();
        // 按照要求:先保存客户,再保存联系人(此时符合保存原则:先保存主表,再保存从表)
        em.persist(c);// 如果在把客户对象转成持久态时,不考虑联系人的信息。就不会有联系人的快照产生
        em.persist(l);
        tx.commit();// 默认此时会执行快照机制,当发现一级缓存和快照不一致了,使用一级缓存更新数据库。
    }

  通过保存的案例,我们可以发现在设置了双向关系之后,会发送两条 insert 语句,一条多余的 update 语句,那我们的解决是思路很简单,就是一的一方放弃维护权
  

/**
* 放弃外键维护权的配置将如下配置改为 :mappedBy="customer"
*/
@OneToMany(mappedBy="customer",targetEntity=LinkMan.class
/**
     * 3、删除操作
     * 
     * 删除从表实体:直接删
     * 删除主表实体:
     *      先看看从表实体是否有引用
     *          有:
     *              第一:解除引用关系,把从表实体的外键字段置为null。要求外键字段允许为null。
     *                  如果数据之间有父子关系,那么如果子数据没有引用的父数据,子数据就应该删除,所以在解除关系时,子数据就该被删除
     *                  例如:用户和订单
     *                  解除关系调用的是update方法,把外键字段给置为null,实现的是删除功能。
     *                  孤儿删除:是用update方法更新外键字段为null,干的是delete的事。
     * 
     *                  如果数据没有父子关系,那么就可以把子数据的外键字段置为null。
     *                  例如:部门和员工
     *              第二:使用级联删除
     *                  删除客户的同时,把所有引用的联系人一起删除。
     *                  调用的删除方法,实现删除客户同时删除联系人
     *          没有: 单表操作,直接删
     * 在实际开发中,级联删除请慎用!(在一对多的情况下)
     */
    @Test
    public void testRemove() {
        // 定义对象
        EntityManager em = null;
        EntityTransaction tx = null;
        try {
            // 获取实体管理对象
            em = JpaUtil.getEntityManager();
            // 获取事务对象
            tx = em.getTransaction();
            // 开启事务
            tx.begin();
            // 执行操作
            Customer c1 = em.find(Customer.class, 26L);

            em.remove(c1);

            // 提交事务
            tx.commit();

        } catch (Exception e) {
            // 回滚事务
            tx.rollback();
            e.printStackTrace();
        } finally {
            // 释放资源
            em.close();
        }
    }
级联操作:
指操作一个对象同时操作它的关联对象
使用方法:只需要在操作主体的注解上配置 cascade
/**
* cascade:配置级联操作
* CascadeType.MERGE  级联更新
* CascadeType.PERSIST 级联保存:
* CascadeType.REFRESH 级联刷新:
* CascadeType.REMOVE 级联删除:
* CascadeType.ALL 包含所有
*/
@OneToMany(mappedBy="customer",cascade=CascadeType.ALL,targetEntity=LinkMan.clas
s)
@JoinColumn(name="lkm_cust_id",referencedColumnName="cust_id")
private Set linkmans = new HashSet(0)

你可能感兴趣的:(JPA)