資料庫表之間關係: 一對一 (可以看做一對多的特例) 一對多 多對多 下圖將涵蓋了所有關係。 根據restful介面風格,我們最終會落到一個實體上,示例按照b表。其他表同理。 GET https://ip:port/xx/xx/1/0/b 查詢的場景: 只需要b表的某些元素 需要b某些元素,及相關聯 ...
資料庫表之間關係:
- 一對一 (可以看做一對多的特例)
- 一對多
- 多對多
下圖將涵蓋了所有關係。
根據restful介面風格,我們最終會落到一個實體上,示例按照b表。其他表同理。
GET https://ip:port/xx/xx/1/0/b
查詢的場景:
- 只需要b表的某些元素
- 需要b某些元素,及相關聯的a表某些元素。
- 需要b某些元素,及a,c,d某些元素。
思路: 通過資料庫的外鍵,自動生成代碼。在新建實體類時,自動查詢關聯的實體。關聯實體查詢一層一層傳導下去。
通過在返回對象實體上加註解,標識需要返回的欄位,及需要關聯查詢的對象,防止洪泛式關聯對象查詢。
class B{ 自身屬性; 關聯的外鍵對象a; 被關聯的對象c集合; 構造方法(){ 查詢自身屬性; 查詢關聯的外鍵對象a; 查詢被關聯的對象c集合; } }
生成代碼示例如下:
public class BInfo implements Serializable { private static final long serialVersionUID = 1L; public BInfo(){} // 主鍵 @JsonProperty("id") private Integer id; // 名稱 @JsonProperty("name") private String name; // 詳情 @JsonProperty("detail") private String detail; // 狀態:0-無效,1-有效,2-編輯 @JsonProperty("status") private Integer status; // 外鍵關聯實體類(欄位:[ a_id ] 註釋:[ ]) // 業務實體(關聯表名:tbl_a ) @JsonProperty("tblAInfo") private AInfo aInfo; // 業務實體 集合 [tbl_c][b_id] @JsonProperty("tblCInfoList") private List<CInfo> cInfoList; // 構造方法 public BInfo(SapoDao dao ,B b) throws Exception { super(); //System.err.println("-----new BInfo--------------"); Class<? extends BInfo> thisClass = this.getClass(); JsonIgnoreProperties annotation = thisClass.getAnnotation(JsonIgnoreProperties.class); // 外鍵關聯對象:業務實體(tbl_a) // 如果註解標註不需要該欄位,則不用查詢該欄位 if(annotation != null && (! java.util.Arrays.asList(annotation.value()).contains("tblAInfo"))) { // 不管狀態,只用外鍵查詢 // A a = dao.getAById(b.getAId()); A aResult = null; // 外鍵欄位不為空才有去查的意義 if(b.getAId() != null){ // 組建查詢條件 A aForQuery = new A(); aForQuery.setId(b.getAId()); // aForQuery.setStatus(A.STATUS_INVALID); // 查詢,不可為空 aResult = dao.getA(aForQuery) ; } // 如果能查該外鍵對應的值,則進行賦值操作 if(aResult !=null){ // 判斷該欄位是否被註解標識,使用哪個子類 FkPojo fkano = thisClass.getAnnotation(FkPojo.class); // 如果沒有被註解標註,需要報錯 if (fkano == null || fkano.value().length == 0) { throw new Exception(thisClass.getName() + " -> must have @PkPojo annotation and lenght !=0 "); } // pojo不符合規範也要報錯,註解用冒號分開[類名:全類名] Map<String, String> map = new HashMap<String, String>(); for (String s : fkano.value()) { s = s.trim(); String[] split = s.split(":"); if (split == null || split.length != 2) { throw new Exception( thisClass.getName() + " -> @PkPojo annotation format error [pojoName:pojoAllPath] "); } map.put(split[0], split[1]); } //如果沒有標識子類使用哪一個,則報錯 if(map.get("AInfo")==null){ throw new Exception(thisClass.getName() + " -> @PkPojo annotation has no class: AInfo"); } // 反射出子類,將子類賦值給該對象。 Class<?> c1 = Class.forName(map.get("AInfo")); Constructor<?> declaredConstructor = c1.getDeclaredConstructor(SapoDao.class, A.class); this.aInfo = (AInfo)declaredConstructor.newInstance(dao, aResult); } } this.detail=b.getDetail(); this.id=b.getId(); this.name=b.getName(); this.status=b.getStatus(); // 註解如果將該欄位忽略了,就不需要查了。 if (annotation != null && (! java.util.Arrays.asList(annotation.value()).contains("tblCInfoList"))) { C cForQuery = new C(); cForQuery.setBId(b.getId()); List<C> cList = dao.getCListWithNull(cForQuery); // 如果查詢的集合是空,也不用繼續往下查了。 if (cList != null && cList.size() != 0) { cInfoList = new ArrayList<CInfo>(cList.size()); // 判斷該欄位是否被註解標識,使用哪個子類 FkPojo fkano = thisClass.getAnnotation(FkPojo.class); // 如果沒有被註解標註,需要報錯 if (fkano == null || fkano.value().length == 0) { throw new Exception(thisClass.getName() + " -> must have @PkPojo annotation and lenght !=0 "); } // pojo不符合規範也要報錯,註解用冒號分開[類名:全類名] Map<String, String> map = new HashMap<String, String>(); for (String s : fkano.value()) { s = s.trim(); String[] split = s.split(":"); if (split == null || split.length != 2) { throw new Exception( thisClass.getName() + " -> @PkPojo annotation format error [pojoName:pojoAllPath] "); } map.put(split[0], split[1]); } // 如果沒有標識子類使用哪一個,則報錯 if (map.get("CInfo") == null) { throw new Exception( thisClass.getName() + " -> @PkPojo annotation has no class: CInfo"); } // 反射出子類,將子類賦值給該對象。 Class<?> c1 = Class.forName(map.get("CInfo")); Constructor<?> declaredConstructor = c1.getDeclaredConstructor(SapoDao.class, C.class); // 迴圈新建對象,將對象加入到集合中。 for (C item : cList) { cInfoList.add((CInfo) declaredConstructor.newInstance(dao, item)); } } } } /** * 設置 * 主鍵 * 的方法 * * @param id 主鍵 */ public void setId(Integer id){ this.id = id; } /** * 獲取 * 主鍵 * 的方法 * * @return 主鍵 */ public Integer getId(){ return id; } /** * 設置 * 名稱 * 的方法 * * @param name 名稱 */ public void setName(String name){ this.name = name; } /** * 獲取 * 名稱 * 的方法 * * @return 名稱 */ public String getName(){ return name; } /** * 設置 * 詳情 * 的方法 * * @param detail 詳情 */ public void setDetail(String detail){ this.detail = detail; } /** * 獲取 * 詳情 * 的方法 * * @return 詳情 */ public String getDetail(){ return detail; } /** * 設置 * 狀態:0-無效,1-有效,2-編輯 * 的方法 * * @param status 狀態:0-無效,1-有效,2-編輯 */ public void setStatus(Integer status){ this.status = status; } /** * 獲取 * 狀態:0-無效,1-有效,2-編輯 * 的方法 * * @return 狀態:0-無效,1-有效,2-編輯 */ public Integer getStatus(){ return status; } /** * 設置 * 業務實體(tbl_a ) * 的方法 * * @param 業務實體(tbl_a ) */ public AInfo setAInfo(AInfo aInfo){ this.aInfo = aInfo; return this; } /** * 獲取 * 業務實體(tbl_a ) * 的方法 * * @return */ public AInfo getAInfo(){ return aInfo; } public void setCInfoList(List<CInfo> cInfoList){ this.cInfoList = cInfoList; } public List<CInfo> getCInfoList(){ return cInfoList; } }B實體對象
@JsonIgnoreProperties({ "id", "name", "detail", "status", "tblAInfo", "tblCInfoList" }) @FkPojo({ "CInfo:classQualifiedName", "AInfo:classQualifiedName" }) public class BInfoxx extends BInfo{ private static final long serialVersionUID = 1L; public BInfoxx(){} public BInfoxx(SapoDao dao,B b) throws Exception{ super(dao,b); } }B實體返回對象
生成代碼工具如下:
1 DROP PROCEDURE IF EXISTS `print_pojo`; 2 DELIMITER $ 3 CREATE PROCEDURE `print_pojo`() 4 BEGIN 5 6 SET group_concat_max_len = 4294967295; 7 8 -- 表名去除那些首碼 9 -- SET @noStrInTbl='tbl_ams'; 10 SET @noStrInTbl='tbl'; 11 -- 通用dao層類名, 12 SET @common_dao_name='SapoDao'; 13 -- domain類名尾碼 14 SET @domain_suffix='Info'; 15 16 SET @domain_prefix='tbl_sapo'; 17 18 19 20 -- ######################begin:基礎信息表維護################### 21 22 -- 保存所有表及表的所有欄位 23 DROP TABLE if EXISTS all_col_table; 24 CREATE table if not exists all_col_table( 25 `id` int(10) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '主鍵', 26 tbl_name VARCHAR(256) COMMENT '表名:tbl_sapo_admin_account', 27 tbl_name_comment VARCHAR(256) COMMENT '表註釋', 28 29 tbl_name_upper_camel VARCHAR(1024) COMMENT '表名大寫駝峰:SapoAdminAccount', 30 tbl_name_lower_camel VARCHAR(1024) COMMENT '表名小寫駝峰:sapoAdminAccount', 31 32 domain_tbl_name_upper_camel VARCHAR(1024) COMMENT 'doamin層表名駝峰:SapoAdminAccountInfo', 33 domain_tbl_name_lower_camel VARCHAR(1024) COMMENT 'domain層表名引用駝峰:sapoAdminAccountInfo', 34 35 col VARCHAR(256) COMMENT '欄位名:create_time', 36 if_fk VARCHAR(512) NOT NULL DEFAULT 'no' COMMENT '外鍵標誌,yes=外鍵', 37 col_comment VARCHAR(512) COMMENT '欄位註釋', 38 col_lower_camel VARCHAR(256) COMMENT '欄位駝峰形式:createTime', 39 col_upper_camel VARCHAR(256) COMMENT '欄位駝峰首字母大寫:CreateTime', 40 41 col_type VARCHAR(256) COMMENT '欄位類型,datetime,int', 42 java_type VARCHAR(256) COMMENT 'java類型,LocalDateTime,Integer', 43 44 col_setter VARCHAR(256) COMMENT 'setter模式:setCreateTime', 45 col_getter VARCHAR(256) COMMENT 'getter模式:getCreateTime', 46 47 json_property VARCHAR(256) COMMENT 'json_property欄位', 48 49 PRIMARY KEY (`id`) , 50 index (`tbl_name`,col) , 51 INDEX idx_1(col) 52 ) ENGINE=InnoDB DEFAULT CHARSET=UTF8; 53 54 55 -- 外鍵臨時表 56 DROP TABLE if exists fk_def; 57 CREATE TABLE if not exists fk_def as 58 SELECT 59 t.TABLE_NAME AS tbl_name, 60 k.column_name AS col, 61 k.REFERENCED_TABLE_NAME AS rf_tbl_name, 62 k.REFERENCED_COLUMN_NAME AS rf_col 63 FROM 64 information_schema.TABLE_CONSTRAINTS t 65 JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE k 66 ON t.CONSTRAINT_NAME = k.CONSTRAINT_NAME 67 AND t.TABLE_NAME = k.TABLE_NAME 68 AND t.CONSTRAINT_SCHEMA = k.CONSTRAINT_SCHEMA 69 WHERE 70 t.CONSTRAINT_TYPE = 'FOREIGN KEY' 71 AND t.table_schema = DATABASE(); 72 73 ALTER TABLE `fk_def` 74 ADD INDEX `idx1` (tbl_name,col); 75 76 ALTER TABLE `fk_def` 77 ADD INDEX `idx2` (rf_tbl_name,rf_col); 78 79 80 -- select * from fk_def ; 81 -- ######################end:基礎信息表維護################### 82 83 84 -- 將本庫中所有表及所有欄位插入表中: tbl_name,tbl_name_comment,col,col_comment,col_type 85 INSERT INTO all_col_table(tbl_name,tbl_name_comment,col,col_comment,col_type) 86 SELECT 87 t1.table_name, t2.TABLE_COMMENT,t1.column_name ,t1.COLUMN_COMMENT,t1.DATA_TYPE 88 FROM 89 information_schema.COLUMNS t1 JOIN information_schema.tables t2 ON t1.TABLE_NAME=t2.TABLE_NAME 90 WHERE 91 t1.table_schema= DATABASE() AND t1.TABLE_NAME LIKE 'tbl_%' ORDER BY t1.TABLE_NAME,t1.ORDINAL_POSITION; 92 93 -- java類型轉換 94 UPDATE all_col_table SET java_type= 95 case col_type 96 when 'datetime' then 'LocalDateTime' 97 when 'tinyint' then 'Byte' 98 when 'bigint' then 'Long' 99 when 'int' then 'Integer' 100 when 'varchar' then 'String' 101 END; 102 103 -- 欄位轉駝峰 104 UPDATE all_col_table SET col_lower_camel =CONCAT_WS('',REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE( REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(col, '_z', 'Z'), '_y', 'Y'), '_x', 'X'), '_w', 'W'), '_v', 'V'), '_u', 'U'), '_t', 'T'), '_s', 'S'), '_r', 'R'), '_q', 'Q'), '_p', 'P'), '_o', 'O'), '_n', 'N'), '_m', 'M'), '_l', 'L'), '_k', 'K'), '_j', 'J'), '_i', 'I'), '_h', 'H'), '_g', 'G'), '_f', 'F'), '_e', 'E'), '_d', 'D'), '_c', 'C'), '_b', 'B'), '_a', 'A'),'_','') 105 ,''); 106 UPDATE all_col_table SET col_upper_camel =CONCAT_WS('',REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE( REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(CONCAT_WS('','_',col), '_z', 'Z'), '_y', 'Y'), '_x', 'X'), '_w', 'W'), '_v', 'V'), '_u', 'U'), '_t', 'T'), '_s', 'S