南山制作網(wǎng)站公司b站推廣網(wǎng)站2024
1.前言
一般來說 對于不同表格之間的屬性約束 我們通常直接使用數(shù)據(jù)庫已經(jīng)實(shí)現(xiàn)好的外鍵來完成 但是數(shù)據(jù)庫底層實(shí)現(xiàn)的外鍵他的性能很差 這是因?yàn)樵趫?zhí)行數(shù)據(jù)庫修改操作時(shí) 他需要遍歷其他所有的表來找出其中可能相關(guān)聯(lián)的屬性 一并進(jìn)行數(shù)據(jù)庫修改(應(yīng)用層的維護(hù)則只需要遍歷所有關(guān)聯(lián)外鍵所在的表) 當(dāng)然 性能差還有其他很多的原因 這里就提供一個(gè)思路 所以要求我們可以通過應(yīng)用層(業(yè)務(wù)層)來自行維護(hù)一個(gè)外鍵約束
2.實(shí)現(xiàn)思路
aop+注解
我們會通過aop去攔截所有形式的數(shù)據(jù)庫修改業(yè)務(wù) 因?yàn)椴煌问降臄?shù)據(jù)庫修改操作(比如removeById和removeByIds)都有可能影響到其他的約束屬性 所以不能夠遺漏任何情況 對于任何形式的數(shù)據(jù)庫修改操作而言 都需要對可能存在的約束屬性進(jìn)行檢查
同時(shí) 也會根據(jù)注解來標(biāo)識外鍵/主鍵 同時(shí)儲存一些相關(guān)信息(約束主鍵所在的表以及屬性等等) 注解內(nèi)部的屬性也會提供一些默認(rèn)值
3.注解實(shí)現(xiàn)
1.級聯(lián)枚舉類
所謂級聯(lián) 其實(shí)就是在對某個(gè)表格的某條記錄執(zhí)行數(shù)據(jù)庫修改操作時(shí) 他的影響會傳遞給其他所有相關(guān)聯(lián)的記錄
有兩種策略 一種就是只提醒用戶 讓用戶去做具體決定 另一種就是繞過用戶 直接進(jìn)行當(dāng)前記錄以及相關(guān)聯(lián)的所有記錄的數(shù)據(jù)庫修改操作
我們可以設(shè)計(jì)一個(gè)枚舉類型來儲存有限個(gè)的級聯(lián)策略
/*** 級聯(lián)策略 所謂級聯(lián) 其實(shí)就是對某條記錄的影響會傳遞給其他相關(guān)聯(lián)的記錄* DEFAULT表示默認(rèn)策略 即提示用戶 讓用戶做具體決定* DELETE表示激進(jìn)策略 不問用戶 直接刪除/更新當(dāng)前記錄以及有關(guān)聯(lián)的所有記錄*/
public enum ForeignCascade {DEFAULT, DELETE
}
2.ForeignField注解
利用該可選注解我們可以用于標(biāo)識外鍵屬性/主鍵屬性
在真正應(yīng)用該注解時(shí) 如果主表屬性為id 就可以直接全部采取默認(rèn)值 則不需要為主表屬性作用該注解 這就是可選注解的理解
/*** Documented注解在于會將該注解加入到j(luò)avadoc文檔中* Target注解在于設(shè)置該注解的作用類型 比如類、方法、屬性等等* Retention注解在于設(shè)置該注解的作用周期 取值為RUNTIME表示可以作用于編譯運(yùn)行完整的階段* Repeatable注解作用是該注解可以在同一個(gè)地方同時(shí)設(shè)置多次 并且需要依賴于一個(gè)容器儲存 本質(zhì)上 多次使用就相當(dāng)于在外部封裝一層容器*/
@Documented
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Repeatable(ForeignField.ForeignFields.class)
public @interface ForeignField {// 主表類Class<?> value() default Object.class;// 主表類Class<?> mainTable() default Object.class;// 主表屬性名String mainField() default "id";// 對應(yīng)字段String column() default "";// 級聯(lián)策略ForeignCascade cascade() default ForeignCascade.DEFAULT;// 容器@Documented@Target(ElementType.FIELD)@Retention(RetentionPolicy.RUNTIME)@interface ForeignFields {ForeignField[] value() default {};}
}
3.ForeignTable注解
利用該可選注解我們可以用于標(biāo)識外鍵/主鍵所在表格
/*** Documented注解的作用在于會將該注解加入到j(luò)avadoc文檔中* Target注解的作用在于該注解只能作用于類上*/
@Documented
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface ForeignTable {// 表名String value() default "";// 表名String name() default "";
}
3.屬性信息類/表格信息類實(shí)現(xiàn)
1.屬性信息類
/*** 該類是屬性信息類*/
@Getter
@Setter
public class ForeignFieldInfo {// 當(dāng)前屬性private Field field;// 當(dāng)前屬性對應(yīng)字段private String column;// 當(dāng)前屬性所在的表格信息類private ForeignTableInfo table;// 引用的屬性集合private List<ForeignFieldInfo> mainFields;// 被引用的屬性集合private List<ForeignFieldInfo> subFields;// 級聯(lián)策略private ForeignCascade cascade;// 設(shè)置屬性 如果屬性是私有屬性的話 那么就不可以直接由外界訪問 這時(shí)候 我們應(yīng)該開放權(quán)限public void setField(Field field) {// 表明任意權(quán)限的變量都可以訪問 即使是private也行field.setAccessible(true);this.field = field;}// 追加引用屬性public void addMainField(ForeignFieldInfo mainField) {// 如果集合不存在的話 那么就先創(chuàng)建集合 如果參數(shù)已經(jīng)存在的話 那么就不追加 否則的話 就直接追加if (mainFields == null) {mainFields = new ArrayList<>();} else if (mainFields.contains(mainField)) {return;}mainFields.add(mainField);}// 追加被引用屬性public void addSubField(ForeignFieldInfo subField) {// 如果集合不存在的話 先創(chuàng)建集合 如果已經(jīng)存在 考慮參數(shù)是否存在于集合 如果存在 那就不添加 反之則需要添加if (subFields == null) {subFields = new ArrayList<>();} else if (subFields.contains(subField)) {return;}subFields.add(subField);}
}