Spring Data JPA的一對多關聯映射

1. Spring Data JPA一對多的關聯映射案例

1.1 創建表結構

客戶表的建表語句:

CREATE TABLE `cst_customer` (
	`cust_id` bigint(32) NOT NULL AUTO_INCREMENT COMMENT '客戶編號(主鍵)',
	`cust_name` varchar(32) NOT NULL COMMENT '客戶名稱(公司名稱)',
	`cust_user_id` bigint(32) DEFAULT NULL COMMENT '負責人id',
	`cust_create_id` bigint(32) DEFAULT NULL COMMENT '創建人id',
	`cust_source` varchar(32) DEFAULT NULL COMMENT '客戶信息來源',
	`cust_industry` varchar(32) DEFAULT NULL COMMENT '客戶所屬行業',
	`cust_level` varchar(32) DEFAULT NULL COMMENT '客戶級別',
	`cust_linkman` varchar(64) DEFAULT NULL COMMENT '聯繫人',
	`cust_phone` varchar(64) DEFAULT NULL COMMENT '固定電話',
	`cust_mobile` varchar(16) DEFAULT NULL COMMENT '移動電話',
	PRIMARY KEY (`cust_id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;

聯繫人表的建表語句:

CREATE TABLE `cst_linkman` (
	`lkm_id` bigint(32) NOT NULL AUTO_INCREMENT COMMENT '聯繫人編號(主鍵)',
	`lkm_name` varchar(16) DEFAULT NULL COMMENT '聯繫人姓名',
	`lkm_cust_id` bigint(32) NOT NULL COMMENT '客戶id',
	`lkm_gender` char(1) DEFAULT NULL COMMENT '聯繫人性別',
	`lkm_phone` varchar(16) DEFAULT NULL COMMENT '聯繫人辦公電話',
	`lkm_mobile` varchar(16) DEFAULT NULL COMMENT '聯繫人手機',
	`lkm_email` varchar(64) DEFAULT NULL COMMENT '聯繫人郵箱',
	`lkm_qq` varchar(16) DEFAULT NULL COMMENT '聯繫人qq',
	`lkm_position` varchar(16) DEFAULT NULL COMMENT '聯繫人職位',
	`lkm_memo` varchar(512) DEFAULT NULL COMMENT '聯繫人備註',
	PRIMARY KEY (`lkm_id`),
	KEY `FK_cst_linkman_lkm_cust_id` (`lkm_cust_id`),
	CONSTRAINT `FK_cst_linkman_lkm_cust_id` FOREIGN KEY (`lkm_cust_id`) REFERENCES `cst_customer` (`cust_id`) ON DELETE NO ACTION ON UPDATE NO ACTION
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;

1.2 編寫實體類

客戶的實體類如下:

@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:建立一對多的關係映射
     * 		targetEntityClass:指定多的多方的類的字節碼
     * 		mappedBy:指定從表實體類中引用主表對象的名稱
     * 		cascade:指定要使用的級聯操作
     * 		fetch:指定是否採用延遲加載
     * 		orphanRemoval:是否使用孤兒刪除
     * @JoinColumn:用於定義主鍵字段和外鍵字段的對應關係
     * 		name:指定外鍵字段的名稱
     * 		referencedColumnName:指定引用主表的主鍵字段名稱
     * 		unique:是否唯一。默認值不唯一
     * 		nullable:是否允許爲空。默認值允許
     * 		insertable:是否允許插入。默認值允許
     * 		updatable:是否允許更新。默認值允許
     * 		columnDefinition:列的定義信息
     */
  	@OneToMany(targetEntity=LinkMan.class)
	@JoinColumn(name="lkm_cust_id", referencedColumnName="cust_id")
	private Set<LinkMan> linkmans = new HashSet<LinkMan>(0);

	//省略 get和 set方法
}

聯繫人的實體類如下:

@Entity
@Table(name="cst_linkman")
public class LinkMan implements Serializable {

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

	/**
     * 配置聯繫人和客戶的多對一關係映射
     * @ManyToOne:建立多對一的關係
     * 		targetEntityClass:指定多的多方的類的字節碼
     * 		cascade:指定要使用的級聯操作
     * 		fetch:指定是否採用延遲加載
     * 		optional:關聯是否可選。如果設置爲 false,則必須始終存在非空關係
     */
	@ManyToOne(targetEntity=Customer.class)
	@JoinColumn(name="lkm_cust_id", referencedColumnName="cust_id")
	private Customer customer;
	
	//省略 get和 set方法
}

2.3 編寫Dao層接口

客戶的Dao層接口如下:

public interface CustomerDao extends JpaRepository<Customer, Long>, JpaSpecificationExecutor<Customer> {
}

聯繫人的Dao層接口如下:

public interface LinkManDao extends JpaRepository<LinkMan, Long>, JpaSpecificationExecutor<LinkMan> {
}

2.4 編寫測試代碼

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

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

	@Test
	public void test1(){
		Customer customer = new Customer();
		customer.setCust_name("張總");
		LinkMan linkMan = new LinkMan();
		linkMan.setLkm_name("秦助理");
		
		customer.getLinkMans().add(linkMan);
		linkMan.setCustomer(customer);
		
		customerDao.save(customer);
		linkManDao.save(linkMan);
	}
}

2. Spring Data JPA一對多的相關操作

2.1 保存

@Test
public void test1(){
	Customer customer = new Customer();
	customer.setCust_name("張總");
	LinkMan linkMan = new LinkMan();
	linkMan.setLkm_name("秦助理");
	
	customer.getLinkMans().add(linkMan);
	linkMan.setCustomer(customer);
	
	customerDao.save(customer);
	linkManDao.save(linkMan);
}

通過上面的代碼,我們可以發現當我們建立了雙向的關聯關係之後,先保存主表,再保存從表時,會產生2條insert和1條update,而實際開發中我們只需要2條insert。

原因是在客戶實體類上(一的一方)添加了外鍵了配置,所以對於客戶而言,也具備了維護外鍵的作用。那我們的解決是思路很簡單,就是一的一方放棄維護權。放棄外鍵維護權需要將客戶實體類的一對多配置改爲:

@Entity
@Table(name="cst_customer")
public class Customer implements Serializable {
	
	//mappedBy:放棄外鍵維護權,值爲對方配置關係的屬性名稱
	@OneToMany(mappedBy="customer")
	private Set<LinkMan> linkmans = new HashSet<LinkMan>(0);
}

2.2 刪除

@Test
public void test2(){
	customerDao.delete(1l);
}

刪除操作的說明如下:

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

2.3 級聯操作和延遲加載

級聯操作:指操作一個對象同時操作它的關聯對象。使用方法:只需要在操作主體的註解上配置cascade。

延遲加載:就是當在真正需要數據的時候,才真正執行數據加載操作。使用方法:只需要在操作主體的註解上配置fetch。

/**
 * cascade:配置級聯操作
 * 		CascadeType.MERGE:級聯更新
 * 		CascadeType.PERSIST:級聯保存
 * 		CascadeType.REFRESH:級聯刷新
 * 		CascadeType.REMOVE:級聯刪除
 * 		CascadeType.ALL:包含所有
 * fetch:配置關聯對象的加載方式
 *      FetchType.EAGER:立即加載
 *      FetchType.LAZY:延遲加載 
 */
@OneToMany(mappedBy="customer", cascade=CascadeType.ALL, fetch=FetchType.LAZY)
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章