背景描述
我們經常會在專案中用到一些資料字典,在儲存和傳輸時使用Code,在前端展示時使用Name,這樣做的好處是便於系統維護,比如專案中用到了"醫院"這個名稱,如果後期需求發生變化不叫"醫院"了,改成"醫療機構",假如不使用資料字典,那麼我們程式碼中、資料庫中所有用到"醫院"的地方都要修改,麻煩不說,漏掉一個就是一個小Bug。在處理這個Code/Name的轉化的時候,我思考了幾種處理方式,第一種,使用@ManyToOne註解關聯字典查詢,這樣是最容易想到的方式,但是這種方式得到的結果是字典物件整體包含在查詢到的實體中,我們所需要的只是字典裡的name,所以我嘗試尋找一種直接將字典表裡的name對映到實體物件上的方式。第二種,使用HQL關聯查詢對映到自定義物件,這種方式可以達到我的預期,但是HQL寫起來很麻煩,尤其是當需要關聯查詢的字典特別多的時候。並且我的專案中動態用的是JPA的Specification,如果使用這種方式,那麼專案中的動態查詢需要改寫,也是不小的工作量。第三種,使用@Formula註解的方式,下面重點說說這種方式。
使用介紹
@Formula的作用是計算出一個臨時的屬性值,我們可以利用它,去關聯查詢其他表中的某個欄位為物件的屬性賦值,而這個屬性是不持久化到資料庫中的。
用程式碼描述一下會比較直觀:
建立兩個實體類
班級字典
@Data
@Entity
public class DictClass {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false)
private String classCode;//班級編號
private String className;//班級名稱
}
學生類
@Data
@Entity
public class Student {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false)
private String name; //名字
@Column(name = "CLASS_CODE")
private String classCode;//班級編號
@Formula("(select d.class_name from dict_class as d where d.class_code = class_code)")
private String className;//班級名稱
}
@Formula("(select d.class_name from dict_class as d where d.class_code = class_code)")意思就是從資料庫的dict_class
表中查詢到class_name
欄位的資料,賦值到className
屬性上。"="後面的class_code對應的時@Column裡的class_code
持久層
public interface StudentRepo extends JpaRepository<Student, Long> {
//很持久
}
控制層
@RestController
@RequestMapping("/formula")
@Api(tags = "formula測試介面")
public class FormulaController {
@Autowired
StudentRepo studentRepo;
@GetMapping
public List<Student> findAll(){
return studentRepo.findAll();
}
}
測試一下,獲取成功
注意事項
雖然看起來很簡單,不過還是有好些個需要注意的地方,一言不合就失效。
1.網上好多人說@Formula必須用在屬性上,其實不是的,@Formula 要與@Id註解同時用在屬性上,或者同時用在在get方法上,否則@Formula失效。
2.如果查詢中用到了where,那麼需要給表起一個別名,否則@Formula失效。
3.@Formula與@Transient不能同時使用,否則@Formula失效。
4.使用@Formula註解的屬性不需要在資料庫表中建立與之對應的欄位,並且即使建立了也沒有作用,加上@Column註解也不行。
使用過程中遇到的坑
當持久層使用原生sql查詢時,會造成NPE異常。
在持久層新增
@Query(nativeQuery = true,value = "select * from student")
List<Student> findByNative();
測試一下
這似乎是因為@Formula遮蔽了className欄位,框架獲取@Column對應的name時拿到null導致的,我未能找到具體原因,所以用起來還是很不隨心,不知道大家是否有好的處理方案,歡迎指教!