Spring 4 +Hibernate. P2 Config relationship for entity

Trong kì trước chúng ta đã tìm hiểu về cách sử dụng entity cho việc cấu hình thiết lập database. (https://viblo.asia/sondv/posts/lA7GKnn5MKZQ) Trong bài viết này tôi sẽ trình bày về cách tạo ràng buộc giưa các entity.

  1. Quan hệ 1 - 1 . Để cấu hình ràng buộc quan hệ 1:1 trong entity , bạn có thể sử dụng tag annotation @OneToOne

Ex: làm việc với 2 entity: stock và stock_detail.

Mỗi stock có duy nhất 1 stock_detail.

Đây là các fields của 2 entity:

@Entity
@Table(name = "stock", uniqueConstraints = {
		@UniqueConstraint(columnNames = "STOCK_NAME"),
		@UniqueConstraint(columnNames = "STOCK_CODE") })
public class Stock implements java.io.Serializable {

	private Integer stockId;
	private String stockCode;
	private String stockName;
	private StockDetail stockDetail;

	public Stock() {
	}

	public Stock(String stockCode, String stockName) {
		this.stockCode = stockCode;
		this.stockName = stockName;
	}

	public Stock(String stockCode, String stockName, StockDetail stockDetail) {
		this.stockCode = stockCode;
		this.stockName = stockName;
		this.stockDetail = stockDetail;
	}

	@Id
	@GeneratedValue(strategy = IDENTITY)
	@Column(name = "STOCK_ID", unique = true, nullable = false)
	public Integer getStockId() {
		return this.stockId;
	}

	public void setStockId(Integer stockId) {
		this.stockId = stockId;
	}

	@Column(name = "STOCK_CODE", unique = true, nullable = false, length = 10)
	public String getStockCode() {
		return this.stockCode;
	}

	public void setStockCode(String stockCode) {
		this.stockCode = stockCode;
	}

	@Column(name = "STOCK_NAME", unique = true, nullable = false, length = 20)
	public String getStockName() {
		return this.stockName;
	}

	public void setStockName(String stockName) {
		this.stockName = stockName;
	}

	@OneToOne(fetch = FetchType.LAZY, mappedBy = "stock", cascade = CascadeType.ALL)
	public StockDetail getStockDetail() {
		return this.stockDetail;
	}

	public void setStockDetail(StockDetail stockDetail) {
		this.stockDetail = stockDetail;
	}

}

@Entity
@Table(name = "stock_detail")
public class StockDetail implements java.io.Serializable {

	private Integer stockId;
	private Stock stock;
	private String compName;
	private String compDesc;
	private String remark;
	private Date listedDate;

	public StockDetail() {
	}

	public StockDetail(Stock stock, String compName, String compDesc,
			String remark, Date listedDate) {
		this.stock = stock;
		this.compName = compName;
		this.compDesc = compDesc;
		this.remark = remark;
		this.listedDate = listedDate;
	}

	@GenericGenerator(name = "generator", strategy = "foreign",
	parameters = @Parameter(name = "property", value = "stock"))
	@Id
	@GeneratedValue(generator = "generator")
	@Column(name = "STOCK_ID", unique = true, nullable = false)
	public Integer getStockId() {
		return this.stockId;
	}

	public void setStockId(Integer stockId) {
		this.stockId = stockId;
	}

	@OneToOne(fetch = FetchType.LAZY)
	@PrimaryKeyJoinColumn
	public Stock getStock() {
		return this.stock;
	}

	public void setStock(Stock stock) {
		this.stock = stock;
	}

	@Column(name = "COMP_NAME", nullable = false, length = 100)
	public String getCompName() {
		return this.compName;
	}

	public void setCompName(String compName) {
		this.compName = compName;
	}

	@Column(name = "COMP_DESC", nullable = false)
	public String getCompDesc() {
		return this.compDesc;
	}

	public void setCompDesc(String compDesc) {
		this.compDesc = compDesc;
	}

	@Column(name = "REMARK", nullable = false)
	public String getRemark() {
		return this.remark;
	}

	public void setRemark(String remark) {
		this.remark = remark;
	}

	@Temporal(TemporalType.DATE)
	@Column(name = "LISTED_DATE", nullable = false, length = 10)
	public Date getListedDate() {
		return this.listedDate;
	}

	public void setListedDate(Date listedDate) {
		this.listedDate = listedDate;
	}

}

Chúng ta sẽ tập chung vào 2 phần chính

Trong stock entity:

@OneToOne(fetch = FetchType.LAZY, mappedBy = "stock", cascade = CascadeType.ALL)
	public StockDetail getStockDetail() {
		return this.stockDetail;
	}

và trong stock_detail entity

@OneToOne(fetch = FetchType.LAZY)
	@PrimaryKeyJoinColumn
	public Stock getStock() {
		return this.stock;
	}

Khi sử dụng @onetoOne annon sẽ thông báo cho Hibernate biết về mỗi quan hệ ràng buộc của 2 entity, và 1 relationship sẽ được tạo ra khi bạn khởi động application ở mode :create or update.

Node:

-Thuộc tính fetch để cấu hình 1 đối tượng sẽ được load khi entity được gọi hay đối tượng được gọi khi chính xác thuộc tính đấy được gọi.

Sample : khi bạn gọi đến object Stock , nếu feth cấu hình là lazy, đối tượng stock_detail sẽ không được load lên. Chỉ khi bạn "thực sự" gọi đến nó , JPA sẽ thực hiện giúp bạn thao tác này.

(nếu chưa rõ cách cấu hình hibernate entity bạn có thể tham khảo bài viết của tồi phần 1.)

  1. Ràng buộc 1 -n Để setup mỗi quan hệ 1 - nhiều bạn có thể sử dụng annation : -@OneToMany

-@ManyToOne

Với việc sử dụng 2 annotions trên , bạn sẽ nói cho hibernate mỗi quan hệ giữa 2 đối tượng entity. Giả sử bạn có 1 đối tượng stock và bạn có nhiều StockDailyRecord .

Để giải quyết vân đế này bạn có thể tạo ra trong tabel StockDailyRecord 1 field lưu id của stock.

Bạn hoàn toàn có thể làm điều đó với cách viết sau:

@OneToMany(fetch = FetchType.LAZY, mappedBy = "stock")
	public Set<StockDailyRecord> getStockDailyRecords() {
		return this.stockDailyRecords;
	}
@ManyToOne(fetch = FetchType.LAZY)
	@JoinColumn(name = "STOCK_ID", nullable = false)
	public Stock getStock() {
		return this.stock;
	}

Điều cần chú ý nhất trong việc cấu hình chính là nới chúng ta sử dụng biến mappedBy . Điều này tương ưng sẽ có 1 property tên là stock trong StockDailyRecord class.

  1. Và annation cuối cùng chúng ta sẽ tìm hiểu là :

Tạo mối quan hệ nhiều nhiều giữa 2 entity. Để tạo mối quan hệ nhiều nhiều giữa 2 entity, bạn hoàn toàn không phải tạo ra 1 bảng để map giữa 2 đối tượng này. Hibernate sẽ đảm nhiệm việc tạo giúp bảng table này .

Giả sử bạn có 2 đối tượng stock và category có quan hệ n-n. Để config điều này bạn có thể config như sau:

@ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
	@JoinTable(name = "stock_category", catalog = "mkyongdb", joinColumns = {
			@JoinColumn(name = "STOCK_ID", nullable = false, updatable = false) },
			inverseJoinColumns = { @JoinColumn(name = "CATEGORY_ID",
					nullable = false, updatable = false) })
	public Set<Category> getCategories() {
		return this.categories;
	}
@ManyToMany(fetch = FetchType.LAZY, mappedBy = "categories")
	public Set<Stock> getStocks() {
		return this.stocks;
	}

Bằng việc cấu hình như trên, hibernate sẽ tạo cho bạn 1 table với tên là stock_category với 2 khóa chính của 2 bảng tương ứng: stock_id và category_id .

Với việc cậu hình ràng buộc bằng các annotions, bạn hoàn toàn kiểm soát được bằng các thẻ, ngoài ra nó sẽ mở đường cho bạn sử dụng các lib được viêt bới hibernate (or ...) cho các thao tác truy xuất vào database .

Thanks for read.