分享JPA的幾個小技巧
關系型數據庫其實很討人厭,尤其是在你使用數據庫驅動的開發模式時。需要首先把表給創建好瞭,然後再使用代碼生成器反向生成一堆幾乎無法可讀的代碼。當字段有變更的時候,又是一番折騰。
這其中的典型,就是MyBatis,所以催生瞭更加簡潔的MyBatis Plus。
瞭解到一些大廠(阿裡、騰訊、抖音等),JPA的使用也越來越廣泛瞭,包括我們公司,這是把合適的工具放到瞭合適的地方。如果想要快速開發,JPA無疑是一個比較好的選擇。你無需關註數據庫表的結構,使用代碼驅動即可完成工作,管它後面是MySQL還是Oracle。JPA把數據庫相關的知識給弱化瞭,讓你專註於業務開發。
我個人曾是非常排斥JPA這種弱化SQL的工具的,這源於對早起Hibernate版本的錯誤認識。但嘗試過mybatis、spring-data-jdbc、jooq後,發現這個東西是真的香!一個遲到的贊,送給JPA。
這對一些管理系統來說,非常合適。因為性能並不是這些系統主要的痛點,業務復雜性才是。
本文將介紹一個簡單的實體類,需要準備哪些基本字段。這些字段,又是如何在代碼中被使用的。
1. 基本字段介紹
首先看一下我們的基礎定義類。
代碼不多,信息卻不少。
下面來一行行解析。
@Data
Data註解是屬於lombok類的,lombok是地球人都知道的代碼簡化工具,提供瞭非常多的註解。如果你不想記憶太多的註解,直接加上一個Data,是最偷懶的選擇。
@MappedSuperclass
這個註解是JPA的,用來標識父類。標註為@MappedSuperclass
的類將不是一個完整的實體類,不會映射到數據庫表,但是它的屬性都將映射到子類的數據庫字段中。放在這裡再合適不過瞭。
@EntityListeners(AuditingEntityListener.class)
開啟自動審計功能,這個和下面的兩個日期字段是相互配合的,我們稍後介紹。
@JsonIgnoreProperties(value = {"hibernateLazyInitializer", "handler"}) //直接使用bean時,避免json序列號失敗
有時候,我們想要再controller層直接使用JPA的實體。但JPA內部其實是有很多附加變量的,比如hibernateLazyInitializer
。
為瞭讓實體在json序列化的時候能夠正常進行,需要忽略這兩個字段。所以這個註解,是屬於jackson json
的。
2. 自定義ID生成器
JPA其實提供瞭非常多的ID生成策略。不過,在互聯網應用下,應用較多的還是雪花算法,因為它有著良好的擴展性,在數據遷移的時候也不會有很多沖突。
為瞭指定雪花算法,我們需要下面幾行代碼。
static final String ID_GEN = "cn.xjjdog.bcmall.utils.db.DistributedId"; @Id @GenericGenerator(name = "IdGen", strategy = ID_GEN) @GeneratedValue(generator = "IdGen")
其中的一個關鍵,就是使用我們名稱叫做IdGen
的ID生成器。這裡的代碼,是有一點小遺憾的。由於JVM類加載的緣故,我們無法在註解中直接使用類的名稱(*.class.getName()
) 來獲取它的包路徑,隻能作為字符串寫死在這裡。
下面我們就來看一下這個ID生成器的處理。
public class DistributedId implements IdentifierGenerator { @Override public Serializable generate(SharedSessionContractImplementor sharedSessionContractImplementor, Object obj) throws HibernateException { if (obj == null) throw new HibernateException(new NullPointerException()) ; if ((((AbstractEntity) obj).getId()) == null) { return String.valueOf(Snowflake.createId()); } else { return ((AbstractEntity) obj).getId(); } } }
代碼如上。在直接使用之前,我們還做瞭一點小處理。當我們判斷實體的ID為空的時候,才使用雪花算法構造一個新的ID;否則使用實體原來設置好的ID,保持不變。
為什麼這樣做?因為這是有需求的。像訂單這種業務,你需要先生成一個訂單號,然後再更新一些數據庫信息,發佈一些消息等;而不是在保存動作出發的時候才生成一個。
如果你不做上面代碼的處理。JPA將每次保存的時候都自動生成一個,覆蓋瞭你原有的。我就在這裡吃過虧,通過debug代碼才進行的修復。
3. 自動填充字段
上面說到createdDate
和lastModifiedDate
兩個字段,其實在使用的時候,是不需要手動去設值的。這兩個值,將通過審計功能自動完成。
@EntityListeners(AuditingEntityListener.class)
當然,我們還要用特有的註解,來標識這兩個字段。
/** * 創建時間 */ @CreatedDate private Date createdDate; /** * 更新時間 */ @LastModifiedDate private Date lastModifiedDate;
最後,不要忘瞭在全局配置中通過Config開啟這個功能。
@Configuration @EnableJpaAuditing public class JpaConfig { }
當然,審計是不能沒有用戶的。所以這個系列還有@CreatedBy
註解,用來標註是誰創建的。你需要在代碼中組裝它們,比如下面的代碼,就是從Spring Sercurity
中獲取用戶信息。
@Configuration @Slf4j public class UserAuditor implements AuditorAware<String> { @Override public Optional<String> getCurrentAuditor() { UserDetails user; try { user = (UserDetails) SecurityContextHolder.getContext().getAuthentication().getPrincipal(); return Optional.ofNullable(user.getUsername()); }catch (Exception e){ return Optional.empty(); } } }
4. End
JPA寫管理系統,真的是神器。當你不需要考慮極限的代碼效率時,是一個非常好的選擇。再看看最近的MyBatis版本,包括MyBatis Plus設計,很多東西已經和JPA越來越像瞭。因為在設計上來說,JPA是最接近面向對象編程的思想的。
B端復雜業務的技術棧,並不需要和C端的技術棧相雷同。JPA顯然通過極少的代碼和約定,就能把事情搞定,讓開發者真正的把重點關註到業務開發上來。後面的文章,我們還會用到MyBatis和MyBatis Plus,到時候,我們再詳細分析它們使用的場景。
以上就是分享JPA的幾個小技巧的詳細內容,更多關於JPA 技巧的資料請關註WalkonNet其它相關文章!
推薦閱讀:
- 利用SpringDataJPA開啟審計功能,自動保存操作人操作時間
- Spring Data JPA的Audit功能審計數據庫的變更
- Jpa 實現自動更新表中的創建日期和修改時間
- 解決@RequestBody搭配@Data的大坑
- Java8的Optional如何幹掉空指針(示例詳解)