背景&問題描述
很多專案選擇jpa/hibernate,更多是為了程式碼的可移植性,不限制資料庫的選擇。特別是toB的業務系統,不同的客戶,要求用不同的資料庫。特別近幾年,大力倡導軟體國產化,國產資料庫也在崛起,很多政府、國企、電力、銀行在資料庫的選擇上,紛紛轉向國產資料庫。也許mybatis+mysql很香,但是程式碼可移植性卻成了toB\toG軟體類平臺的關鍵競爭力。
hibernate,在欄位定義,很多人都喜歡用columnDefinition註解去定義表的DDL,可能是曾經留下來的習慣吧。以前的hibernate對資料庫欄位及資料庫移植性支援的比較差,像json、time、blob等一些特殊欄位型別,表欄位註釋等支援的不好,為了方便,大家基本都會用columnDefinition註解直接去定義表,columnDefinition註解屬性定義的ddl是不可移植性的,導致了整個專案不能跨資料庫自動建立表。
下文講解columnDefinition欄位的替代方案。
解決方案
以mysql為例
@Colum(columnDefinition="decimal(15,2) comment '產品單價' default 1.5 ")
- 欄位註釋
需要將hibernate升級到5.6版本以上
欄位註釋使用註解org.hibernate.annotations.Comment
@Comment("產品單價")
- 欄位長度、精度
欄位長度使用javax.persistence.Column註解中的length屬性,精度使用scale
@Column(length=15,scale=2)
- 欄位預設值
欄位預設值使用org.hibernate.annotations.ColumnDefault註解
注意:字元型,需要加單引號
@ColumnDefault("1.5")
- 大欄位型別
大欄位處理用javax.persistence.Lob,同時加註解@javax.persistence.Basic(fetch = FetchType.LAZY)懶載入,避免載入大資料,導致效能很差
對應資料庫text\blob欄位等
@Column
@Lob
@Basic(fetch = FetchType.LAZY)
- 時間類
時間型別欄位使用註解javax.persistence.Temporal
透過傳入註解引數TemporalType型別,可以指定時間型別
@Temporal(TemporalType.TIMESTAMP) // 時間戳
@Temporal(TemporalType.DATE) // 日期
@Temporal(TemporalType.TIME) // 時間
- json型別
json資料,如果資料庫不支援json型別,hibernate會預設轉為varchar型別,並且長度預設為255,對於json資料,通常都是大欄位,下期將改造方案。
@TypeDef(name = "json", typeClass = JsonStringType.class)
public class Test {
@Type(type = "json")
private TestModel testModel;
}
@Type(type = "json") 其含義是將當前表格列對映成“Hibernate Types”中的 JsonStringType型別。JsonStringType進一步將對應的資料實體對映成VARCHAR型別。
hibernate支援json欄位型別,可參考
•https://github.com/vladmihalcea/hibernate-types
注意事項
- 漢字編碼長度不同
不同資料庫編碼對字元長度的定義不一致,比如,mysql,varchar(20)可以存放20個漢字,而sqlserver相同的欄位定義只能存放10個漢字。建議在定義表欄位時,取大不取小,考慮漢字佔位符; - 批次操作限制
SqlServer批次入庫不能超過2100條,而mysql沒有此限制。在有批次入庫操作,需要分批入庫處理; - 引數限制
SqlServer傳參不能超過1000個,對於一些表引數傳遞,儘可能用關聯查詢,推薦使用querydsl做關聯查詢,避免大量引數傳遞 - 唯一鍵名稱重複限制
mysql中,唯一鍵就是索引,在同一個表,索引名稱不能重複,而SqlServer是有唯一鍵,是整庫內,所有唯一鍵名稱不能重複。建議在建唯一鍵時,不填寫索引name,hibernate會自動生產不重複name
@Table(uniqueConstraints = {@UniqueConstraint(columnNames = {"name", "age"})})