SpringData JPA多表操作(增,刪)


一對多:

  示例:客戶和聯系人關系

  在實體類中,由於客戶是少的一方,它應該包含多個聯系人,所以實體類要體現出客戶中有多個聯系人的信息

/**
 * 客戶的實體類
 */
@Entity
@Table(name = "cst_customer")
public class Customer implements Serializable {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "cust_id")
    private Long custId;
    @Column(name = "cust_name")
    private String custName;
    @Column(name = "cust_source")
    private String custSource;
    @Column(name = "cust_industry")
    private String custIndustry;
    @Column(name = "cust_level")
    private String custLevel;
    @Column(name = "cust_address")
    private String custAddress;
    @Column(name = "cust_phone ")
    private String custPhone;

    // @OneToMany(targetEntity = LinkMan.class, fetch = FetchType.LAZY)
    // @JoinColumn(name = "lkm_cust_id", referencedColumnName = "cust_id")
    /**
     * 放棄外鍵維護權
     *      mappedBy: 對方配置關系的屬性名稱
     *      cascade: 配置級聯(可以配置到設置多表的映射關系的注解上)
     *          CascadeType.all         : 所有
     *                      MERGE       :更新
     *                      PERSIST     :保存
     *                      REMOVE      :刪除
     * fetch : 配置關聯對象的加載方式
     *          EAGER   :立即加載
     *          LAZY    :延遲加載
     */
    @OneToMany(mappedBy = "customer")
    private Set<LinkMan> linkMans = new HashSet<>(0);

    /************************ get/set方法 ************************/
}

  由於聯系人是多的一方,在實體類中要體現出,每個聯系人只能對應一個客戶

/**
 * 聯系人實體類
 */
@Entity
@Table(name = "cst_linkman")
public class LinkMan {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "lkm_id")
    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;

    /**
     * 配置聯系人到客戶的多對一關系
     *     使用注解的形式配置多對一關系
     *      1.配置表關系
     *          @ManyToOne : 配置多對一關系
     *              targetEntity:對方的實體類字節碼
     *      2.配置外鍵(多對多配置中間表)
     *  配置外鍵的過程,配置到了多的一方,就會在多的一方維護外鍵
     */
    @ManyToOne(targetEntity = Customer.class)
    @JoinColumn(name = "lkm_cust_id",referencedColumnName = "cust_id")
    private Customer customer;

    /************************ get/set方法 ************************/
}

  持久層接口

/**
 * 客戶持久層接口
 * JpaRepository<實體類類型,主鍵類型>:用來完成基本CRUD操作
 * JpaSpecificationExecutor<實體類類型>:用於復雜查詢(分頁等查詢操作)
 */
public interface CustomerDao extends JpaRepository<Customer, Long>, JpaSpecificationExecutor<Customer> {
}
/**
 * 聯系人持久層接口
 */
public interface LinkManDao extends JpaRepository<LinkMan, Long>, JpaSpecificationExecutor<LinkMan> {
}

  測試新增記錄

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:applicationContext.xml")
public class OneToManyTest {

    @Autowired
    private CustomerDao customerDao;
    @Autowired
    private LinkManDao linkManDao;

    /**
     * 保存一個客戶,保存一個聯系人
     */
    @Test
    @Transactional
    @Rollback(false)
    public void testAdd() {
        Customer customer = new Customer();
        customer.setCustName("夫子");
        LinkMan linkMan = new LinkMan();
        linkMan.setLkmName("寧缺");

        /**
         * 配置了客戶到聯系人的關系
         *      從客戶的角度上:發送兩條insert語句,發送一條更新語句更新數據庫(更新外鍵)
         * 由於我們配置了客戶到聯系人的關系:客戶可以對外鍵進行維護
         */
        customer.getLinkMans().add(linkMan);

        /**
         * 配置聯系人到客戶的關系(多對一)
         *    只發送了兩條insert語句
         * 由於配置了聯系人到客戶的映射關系(多對一):聯系人也可以對外鍵進行維護
         */
        // linkMan.setCustomer(customer);

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

    /**  *** 最終建議使用方式 ***
     * 會有一條多余的update語句
     *      * 由於一的一方可以維護外鍵:會發送update語句
     *      * 解決此問題:只需要在一的一方放棄維護權即可
     *
     */
    @Test
    @Transactional
    @Rollback(false)
    public void testAdd2() {
        //創建一個客戶,創建一個聯系人
        Customer customer = new Customer();
        customer.setCustName("陳某");

        LinkMan linkMan = new LinkMan();
        linkMan.setLkmName("隆慶");

        linkMan.setCustomer(customer);//由於配置了多的一方到一的一方的關聯關系(當保存的時候,就已經對外鍵賦值)
        customer.getLinkMans().add(linkMan);//由於配置了一的一方到多的一方的關聯關系(發送一條update語句)

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

  測試刪除記錄

@Test
@Transactional
@Rollback(false)
public void testDelete() {
    customerDao.delete(1L);
}

  刪除操作的說明如下:
    刪除從表數據:可以隨時任意刪除。
    刪除主表數據:
      有從表數據
        1、在默認情況下,它會把外鍵字段置為null,然后刪除主表數據。如果在數據庫的表結構上,外鍵字段有非空約束,默認情況就會報錯。
        2、如果配置了放棄維護關聯關系的權利,則不能刪除(與外鍵字段是否允許為null, 沒有關系)因為在刪除時,它根本不會去更新從表的外鍵字段了。
        3、如果還想刪除,使用級聯刪除引用(慎用)

      沒有從表數據引用:隨便刪

級聯操作:指操作一個對象同時操作它的關聯對象

  使用方法:只需要在操作主體的注解上配置cascade

  @OneToMany(mappedBy = "customer", cascade = CascadeType.ALL) private Set<LinkMan> linkMans = new HashSet<>(0);
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:applicationContext.xml")
public class CascadeTest {
    @Autowired
    private CustomerDao customerDao;
    @Autowired
    private LinkManDao linkManDao;

    /**
     * 級聯添加:保存一個客戶的同時,保存客戶的所有聯系人
     *      需要在操作主體的實體類上,配置casacde屬性
     */
    @Test
    @Transactional
    @Rollback(false)
    public void testCascadeAdd() {
        Customer customer = new Customer();
        customer.setCustName("百度");

        LinkMan linkMan = new LinkMan();
        linkMan.setLkmName("小李");

        linkMan.setCustomer(customer);
        customer.getLinkMans().add(linkMan);

        customerDao.save(customer);
    }
    
    /**
     * 級聯刪除:
     *      刪除1號客戶的同時,刪除1號客戶的所有聯系人
     */
    @Test
    @Transactional
    @Rollback(false)
    public void testCascadeRemove() {
        //1.查詢1號客戶
        Customer customer = customerDao.findOne(1L);
        //2.刪除1號客戶
        customerDao.delete(customer);
    }
}

多對多:

  示例:用戶和角色關系

  多對多的表關系建立靠的是中間表,其中用戶表和中間表的關系是一對多,角色表和中間表的關系也是一對多

  實體類關系建立以及映射配置:

    一個用戶可以具有多個角色,所以在用戶實體類中應該包含多個角色的信息

/**
 * 用戶實體類
 */
@Entity
@Table(name = "sys_user")
public class SysUser implements Serializable {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "user_id")
    private Long userId;
    @Column(name = "user_code")
    private String userCode;
    @Column(name = "user_name")
    private String userName;
    @Column(name = "user_password")
    private String userPassword;
    @Column(name = "user_state")
    private String userState;
  
/**   * 配置用戶到角色的多對多關系   * 配置多對多的映射關系   * 1.聲明表關系的配置   * @ManyToMany(targetEntity = SysRole.class) //多對多   * targetEntity:代表對方的實體類字節碼   * 2.配置中間表(包含兩個外鍵)   * @JoinTable   * name : 中間表的名稱   * joinColumns:配置當前對象在中間表的外鍵   * @JoinColumn的數組   * name:外鍵名   * referencedColumnName:參照的主表的主鍵名   * inverseJoinColumns:配置對方對象在中間表的外鍵   */   @ManyToMany(targetEntity = SysRole.class)   @JoinTable(name = "sys_user_role", //joinColumns,當前對象在中間表中的外鍵 joinColumns = {@JoinColumn(name = "sys_user_id",referencedColumnName = "user_id")}, //inverseJoinColumns,對方對象在中間表的外鍵 inverseJoinColumns = {@JoinColumn(name = "sys_role_id",referencedColumnName = "role_id")}   )   private Set<SysRole> roles = new HashSet<SysRole>(0);
  
/************************ get/set方法 ************************/
}

    一個角色可以賦予多個用戶,所以在角色實體類中應該包含多個用戶的信息

/**
 * 角色實體類
 */
@Entity
@Table(name = "sys_role")
public class SysRole implements Serializable {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "role_id")
    private Long roleId;
    @Column(name = "role_name")
    private String roleName;
    @Column(name = "role_memo")
    private String roleMemo;
  
//多對多關系映射   @ManyToMany(mappedBy = "roles") // 對方配置關系的屬性名稱,表示由對方來維護中間表關系   private Set<SysUser> users = new HashSet<SysUser>(0);
  
/************************ get/set方法 ************************/ }

  持久層接口:

/**
 * 用戶持久層接口
 */
public interface SysUserDao extends JpaRepository<SysUser, Long>, JpaSpecificationExecutor<SysUser> {
}
/**
 * 角色持久層接口
 */
public interface SysRoleDao extends JpaRepository<SysRole, Long>, JpaSpecificationExecutor<SysRole> {
}

  測試:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:applicationContext.xml")
public class ManyToManyTest {

    @Autowired
    private SysRoleDao sysRoleDao;
    @Autowired
    private SysUserDao sysUserDao;

    /**
     * 保存一個用戶,保存一個角色
     * 多對多放棄維護權:被動的一方放棄
     */
    @Test
    @Transactional
    @Rollback(false)
    public void testAdd() {
        SysUser sysUser = new SysUser();
        sysUser.setUserName("小強");

        SysRole sysRole = new SysRole();
        sysRole.setRoleName("java程序員");

        // 配置用戶到角色關系,可以對中間表中的數據進行維護
        sysUser.getRoles().add(sysRole);

        // 配置角色到用戶的關系,可以對中間表的數據進行維護(放棄了維護)
        sysRole.getUsers().add(sysUser);

        sysUserDao.save(sysUser);
        sysRoleDao.save(sysRole);
    }
}

 

    在多對多(保存)中,如果雙向都設置關系,意味着雙方都維護中間表,都會往中間表插入數據,中間表的2個字段又作為聯合主鍵,主鍵重復,所以報錯

    解決保存失敗的問題:只需要在任意一方放棄對中間表的維護權即可,推薦在被動的一方放棄,配置如下:

      // 放棄對中間表的維護權,解決保存中主鍵沖突的問題
      @ManyToMany(mappedBy = "roles")
      private Set<SysUser> users = new HashSet<SysUser>(0);

多對多級聯操作:

  和一對多一樣,只需要在操作主體的注解上配置cascade

@ManyToMany(targetEntity = SysRole.class, cascade = CascadeType.ALL)
  @JoinTable(name = "sys_user_role",
        //joinColumns,當前對象在中間表中的外鍵
        joinColumns = {@JoinColumn(name = "sys_user_id",referencedColumnName = "user_id")},
        //inverseJoinColumns,對方對象在中間表的外鍵
        inverseJoinColumns = {@JoinColumn(name = "sys_role_id",referencedColumnName = "role_id")}
  )
  private Set<SysRole> roles = new HashSet<SysRole>(0);

  測試:

/**
     * 測試級聯添加(保存一個用戶的同時保存用戶的關聯角色)
     */
    @Test
    @Transactional
    @Rollback(false)
    public void testCasCadeAdd() {
        SysUser sysUser = new SysUser();
        sysUser.setUserName("小李");

        SysRole sysRole = new SysRole();
        sysRole.setRoleName("java程序員");

        sysUser.getRoles().add(sysRole);
        sysRole.getUsers().add(sysUser);

        sysUserDao.save(sysUser);
    }

    /**
     * 刪除操作
     *     在多對多的刪除時,雙向級聯刪除根本不能配置
     * 禁用
     *    如果配了的話,如果數據之間有相互引用關系,可能會清空所有數據
     *
     * 案例:刪除id為1的用戶,同時刪除他的關聯對象
     */
    @Test
    @Transactional
    @Rollback(false)
    public void testCasCadeRemove() {
        //查詢1號用戶
        SysUser user = sysUserDao.findOne(1L);
        //刪除1號用戶
        sysUserDao.delete(user);
    }

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM