Mybatis-Plus增強包

方糖先生發表於2021-09-08

簡介

本框架(Gitee地址 )結合公司日常業務場景,對Mybatis-Plus 做了進一步的擴充封裝,即保留MP原功能,又新增更多有用便捷的功能。具體擴充體現在資料自動填充(類似JPA中的審計)關聯查詢(類似sql中的join)自動建表(僅支援mysql)冗餘資料自動更新動態條件等功能做了補充完善。其中自動建表,是在A.CTable 框架上的基礎上改進適配本框架的,只保留了其表建立功能,因此改動較大不與原框架相容。

專案地址

https://gitee.com/tangzc/mybatis-plus-ext

快速開始

引入jar包

starter內自帶了MybatisPlus3.4.3.3版本及spring-boot2.3.12的依賴管理,如果要更改springboot的版本,可以排除掉,但是如果要變更MybatisPlus的版本,請注意了,框架中重寫了TableInfoHelper,不同版本的MP該類有所變動,同時框架內也採用了MP的部分工具類,例如LambdaUtils、ReflectionKit等在不同的版本也有所變動,需要小心,哈哈哈哈,可以聯絡我幫你改~~

<dependency>
    <groupId>com.tangzc</groupId>
    <artifactId>mybatis-plus-ext-boot-starter</artifactId>
    <version>1.2.9</version>
</dependency>

自動建表

根據實體上的註解及欄位註解自動建立、更新資料庫表。

官方的設計思路是預設Bean下的所有欄位均不是表欄位,需要手動通過@Column宣告,我在引用過來之後,改為了預設所有欄位均為表欄位,只有被MP的@TableField(exist=false)修飾的才會被排除,具備@TableField(exist=false)功能的註解有:@Exclude、@Bind**系列,他們整合了@TableField,且內建exist屬性為false了。

另外A.CTable框架內部整合了類似MP的功能,不如MP完善,所以我也剔除掉了,順帶解決了不相容和bug。同時像DefaultValue註解重名了,也給它改名為ColumnDefault了,另外整理了一遍內部的註解利用spring的AliasFor做了關聯,更方便管理。

其中還有一點,@Table裡面加了一個primary屬性,表示是否為主表,為了支援多個Entity對應一個資料庫表(正常用不到請忽略_

@Data
// @Table標記的可被識別為需要自動建立表的Entity
@Table(comment = "使用者")
public class User {
	
    // 自動識別id屬性名為主鍵
    // @IsAutoIncrement宣告為自增主鍵,什麼都不宣告的話,預設為雪花演算法的唯一主鍵(MP的自帶功能),推薦預設便於後期的資料分散式儲存等處理。
    @IsAutoIncrement
    // 欄位註釋
    @ColumnComment("主鍵")
    // 欄位長度
    @ColumnLength(32)
    private String id;

    // 索引
    @Index
    // 非空
    @IsNotNull
    @ColumnComment("名字")
    private String name;
    
    // 唯一索引
    @Unique
    // 非空
    @IsNotNull
    @ColumnComment("手機號")
    private String phone;
    
    // 省略其他屬性
    ......
}
// 啟用自動生成資料庫表功能,此處簡化了A.CTable的複雜配置,均採用預設配置
@EnableAutoTable
@SpringBootApplication
public class DemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }
}
# actable的配置資訊保留了如下幾項,均做了預設配置,正常無需配置
actable.table.auto=update
actable.model.pack=[Spring啟動類所在包]
actable.database.type=mysql
actable.index.prefix=自己定義的索引字首#該配置項不設定預設使用actable_idx_
actable.unique.prefix=自己定義的唯一約束字首#該配置項不設定預設使用actable_uni_

資料填充

可以在資料插入或更新的時候,自動賦值資料操作人、操作時間、預設值等屬性。

以文章釋出為例,講解一下資料填充的基本用法。通過如下例子可發現,在建立Artice的時候,我們無需再去關心過多的與業務無關的欄位值,只需要關心titlecontent兩個核心資料即可,其他的資料均會被框架處理。

@Data
@Table(comment = "文章")
public class Article {
	
    // 字串型別的ID,預設也是雪花演算法的一串數字(MP的預設功能)
    @ColumnComment("主鍵")
    private String id;

    @ColumnComment("標題")
    private String title;
    
    @ColumnComment("內容")
    private String content;
    
    // 文章預設啟用狀態
    @DefaultValue("ACTIVE")
    @ColumnComment("內容")
    // ActicleStatusEnum(ACTIVE, INACTIVE)
    private ActicleStatusEnum status;

    @ColumnComment("釋出時間")
    // 插入資料時候會自動獲取系統當前時間賦值,支援多種資料型別,具體可參考@OptionDate註解詳細介紹
    @InsertOptionDate
    private Date publishedTime;

    @ColumnComment("釋出人")
    // 插入的時候,根據UserIdAutoFillHandler自動填充使用者id
    @InsertOptionUser(UserIdAutoFillHandler.class)
    private String publishedUserId;

    @ColumnComment("釋出人名字")
    // 插入的時候,根據UserIdAutoFillHandler自動填充使用者名稱字
    @InsertOptionUser(UsernameAutoFillHandler.class)
    private String publishedUsername;

    @ColumnComment("最後更新時間")
    // 插入和更新資料時候會自動獲取系統當前時間賦值,支援多種資料型別,具體可參考@OptionDate註解詳細介紹
    @InsertUpdateOptionDate
    private Date publishedTime;

    @ColumnComment("最後更新人")
    // 插入和更新的時候,根據UserIdAutoFillHandler自動填充使用者id
    @InsertUpdateOptionUser(UserIdAutoFillHandler.class)
    private String publishedUserId;

    @ColumnComment("最後更新人名字")
    // 插入和更新的時候,根據UserIdAutoFillHandler自動填充使用者名稱字
    @InsertUpdateOptionUser(UsernameAutoFillHandler.class)
    private String publishedUsername;
}
/**
 * 全域性獲取使用者ID
 * 此處實現IOptionByAutoFillHandler介面和AutoFillHandler介面均可,建議實現IOptionByAutoFillHandler介面,
 * 因為框架內的BaseEntity預設需要IOptionByAutoFillHandler的實現。後面會講到BaseEntity的使用。
 */
@Component
public class UserIdAutoFillHandler implements IOptionByAutoFillHandler<String> {

    /**
     * @param object 當前操作的資料物件
     * @param clazz  當前操作的資料物件的class
     * @param field  當前操作的資料物件上的欄位
     * @return 當前登入使用者id
     */
    @Override
    public String getVal(Object object, Class<?> clazz, Field field) {
      	RequestAttributes requestAttributes = RequestContextHolder.currentRequestAttributes();
        HttpServletRequest request = ((ServletRequestAttributes)requestAttributes).getRequest();
        // 配合閘道器或者過濾器,token校驗成功後就把使用者資訊塞到header中
        return request.getHeader("user-id");
    }
}
/**
 * 全域性獲取使用者名稱
 */
@Component
public class UsernameAutoFillHandler implements AutoFillHandler<String> {

    /**
     * @param object 當前操作的資料物件
     * @param clazz  當前操作的資料物件的class
     * @param field  當前操作的資料物件上的欄位
     * @return 當前登入使用者id
     */
    @Override
    public String getVal(Object object, Class<?> clazz, Field field) {
        RequestAttributes requestAttributes = RequestContextHolder.currentRequestAttributes();
        HttpServletRequest request = ((ServletRequestAttributes)requestAttributes).getRequest();
        // 配合閘道器或者過濾器,token校驗成功後就把使用者資訊塞到header中
        return request.getHeader("user-name");
    }
}

關聯查詢

資料關聯查詢的解決方案,替代sql中的join方式,通過註解關聯多表之間的關係,查詢某實體的時候,自動帶出其關聯性的資料實體。

本示例以比較複雜的通過中間表關聯資料的案例來講解下,使用者和角色之間多對多,通過中間表進行資料級聯,@BindEntity*系列是關聯Entity的資料,@BindField*系列是關聯Entity下的某個欄位。當@Bind*系列註解用在物件上即表達一對一,當註解在List上時便表達一對多的意思,當外部物件本身就是查詢集合的情況下便是多對多的場景了。

@Data
@Table(comment = "角色資訊")
public class Role {

    @ColumnComment("主鍵")
    private String id;

    @ColumnComment("角色名")
    private String name;
}
@Data
@Table(comment = "使用者資訊")
public class User {

    @ColumnComment("主鍵")
    private String id;

    @ColumnComment("使用者名稱")
    private String username;

    @ColumnComment("密碼")
    private String password;

    // 關鍵配置,宣告瞭User想關聯對應的Rule集合,中間表是UserRule
    @BindEntityByMid(conditions = @MidCondition(
            midEntity = UserRole.class, selfMidField = "userId", joinMidField = "roleId"
    ))
    private List<Role> roles;
}
@Data
@Table(comment = "使用者-角色關聯關係")
public class UserRole {

    @ColumnComment("主鍵")
    private String id;

    @ColumnComment("使用者id")
    private String userId;

    @ColumnComment("角色id")
    private String roleId;
}
/**
 * 使用者服務
 */
@Slf4j
@Service
public class UserService {

    // UserRepository繼承了BaseRepository<UserMapper, User>,後面會講BaseRepository
    @Resource
    private UserRepository userRepository;

    /**
     * 根據使用者的名字模糊查詢所有使用者的詳細資訊
     */
    @Transactional(readOnly = true)
    public List<UserDetailWithRoleDto> searchUserByNameWithRule(String name) {

        // MP的lambda查詢方式
        List<User> userList = userRepository.lambdaQuery()
               .eq(name != null, User::getUsername, name)
               .list();
        // 關鍵步驟,指定關聯角色資料。如果你開啟sql列印,會看到3條sql語句,第一條根據id去User表查詢user資訊,第二條根據userId去UserRule中間表查詢所有的ruleId,第三條sql根據ruleId集合去Rule表查詢全部的許可權
        Binder.bindOn(userList, User::getRoles);
        // Binder.bind(userList); 此種用法預設關聯user下所有宣告需要繫結的元素
        
        return UserMapping.MAPPER.toDto5(userList);
    }

    /**
     * 根據使用者的名字模糊查詢所有使用者的詳細資訊,等價於上一個查詢方式
     */
    @Transactional(readOnly = true)
    public List<UserDetailWithRoleDto> searchUserByNameWithRule2(String name) {

        // 本框架擴充的lambda查詢器lambdaQueryPlus,增加了bindOne、bindList、bindPage
        // 顯然這是一種更加簡便的查詢方式,但是如果存在多級深度的關聯關係,此種方法就不適用了,還需要藉助Binder
        List<User> userList = userRepository.lambdaQueryPlus()
               .eq(name != null, User::getUsername, name)
               .bindList(User::getRoles);
        
        return UserMapping.MAPPER.toDto5(userList);
    }
}

提示: 假如存在此種場景:UserRoleMenu三個實體,他們之間的關係是:User 多對多 RoleRole 多對多Menu,當我查詢出User的集合後,如何獲取Role和Menu的資料呢?

// 資料庫查詢出了使用者列表 【1】
List<User> userList = userRepository.list();
// 為所有使用者關聯角色資訊 【2】
Binder.bindOn(userList, User::getRoles);
// 為所有角色資訊關聯選單資訊 【3】
// Deeper為一個深度遍歷工具,可以深入到物件的多層屬性內部,從而獲取全域性上該層級的所有物件同一屬性
Binder.bindOn(Deeper.with(userList).inList(User::getRoles), User::getMenus);
注意?:【2】和【3】存在順序依賴,必須先執行【2】才能執行【3】

資料冗餘

當其他表的資料需要作為當前表的查詢條件的時候,多數情況下會使用sql的join語法,另一種方案是做資料冗餘,講其他表的欄位作為當前表的欄位,但是牽扯一個資料修改後同步的問題,本框架可以解決。

假設使用者評論的場景,評論上需要冗餘使用者名稱和頭像,如果使用者的名字和頭像有改動,則需要同步新的改動,程式碼如下:

@Data
@Table(comment = "使用者資訊")
public class User {

    @ColumnComment("主鍵")
    private String id;

    @ColumnComment("使用者名稱")
    private String username;

    @ColumnComment("頭像")
    private String icon;
    
    // 省略其他屬性
    ......
}
@Data
@Table(comment = "評論")
public class Comment {

    @ColumnComment("主鍵")
    private String id;

    @ColumnComment("評論內容")
    private String content;

    @ColumnComment("評論人id")
    private String userId;

    // 基於該註解,框架會自動註冊監聽EntityUpdateEvent事件,User的updateById和updateBatchById兩個方法會自動釋出EntityUpdateEvent事件
    @DataSource(source = User.class, field = "username", conditions = @Condition(selfField = "userId"))
    @ColumnComment("評論人名稱")
    private String userName;

    @DataSource(source = User.class, field = "icon", condition = @Condition(selfField = "userId"))
    @ColumnComment("評論人頭像")
    private String userIcon;
}

動態條件

適用場景:資料篩選,比如根據不同許可權獲取不同資料,使用者只能看到自己的資料,管理員能看到所有人的資料。

此種場景,我們通常需要在每一個查詢、更新、刪除的sql操作上都追加上某個條件,很容易忘記,但是可以抽象成註解直接配置到Entity上,就省去了每個資料操作關心這個特殊條件了。

@Data
@Table(comment = "文章")
public class Article {

    @ColumnComment("主鍵")
    private String id;

    @ColumnComment("標題")
    private String title;
    
    @ColumnComment("內容")
    private String content;

    @ColumnComment("釋出人")
    @InsertOptionUser(UserIdAutoFillHandler.class)
    // 新增了該註解後,針對文章的查詢、修改、刪除操作,均會被自動帶上 published_user_id=或者in的新增
    @DynamicCondition(CurrentUserDynamicConditionHandler.class)
    private String publishedUserId;
    
    // 省略其他欄位
    ......
}
@Component
public class CurrentUserDynamicConditionHandler implements IDynamicConditionHandler {

    @Resource
    private HttpServletRequest request;

    @Override
    public List<Object> values() {
		// 只有當enable()返回true的時候 本動態條件才 生效
        // 返回空集合或者null的時候,sql上體現的是 [column] is null,只返回一個值的時候sql上體現的是 [column]=***,返回集合的時候,sql上體現的是 [column] in (***)
        String userId = request.getHeader("USER_ID");
        return Collections.singletonList(userId);
    }

    @Override
    public boolean enable() {
        // 簡單例子:header中取使用者許可權,如果是非管理員則執行該過濾條件,如果是管理員預設查全部,返回false,本動態條件失效
        String userRule = request.getHeader("USER_ROLE");
        return !"ADMIN".equals(userRule);
    }
}

BaseEntity使用

通常的表設計中,都會要求新增一些審計資料,比如建立人、建立時間、最後修改人、最後修改時間,但是這些屬性又不應該屬於業務的,更多的是為了資料管理使用的。如果業務需要使用的話,建議起一個有意義的業務名稱與上述的建立時間區分開,比如使用者的註冊時間(registrationTime)。為了簡化資料審計欄位的工作量,框架內部整合了BaseEntity

@Getter
@Setter
public class BaseEntity<ID_TYPE extends Serializable, TIME_TYPE> {

    // 這裡就是資料填充樣例那裡提到的IOptionByAutoFillHandler介面
    // 此處單獨指定一個標記性的介面是為了區別使用者其他資料的自動填充,例如使用者名稱、使用者電話等都會實現AutoFillHandler介面,框架上根據該介面無法拿到唯一的實現,因此同樣IOptionByAutoFillHandler在整個系統中也只能有一個實現,不然會報錯。
    @InsertOptionUser(IOptionByAutoFillHandler.class)
    @ColumnComment("建立人")
    protected ID_TYPE createBy;
    @InsertUpdateOptionUser(IOptionByAutoFillHandler.class)
    @ColumnComment("最後更新人")
    protected ID_TYPE updateBy;
    @InsertOptionDate
    @ColumnComment("建立時間")
    protected TIME_TYPE createTime;
    @InsertUpdateOptionDate
    @ColumnComment("最後更新時間")
    protected TIME_TYPE updateTime;
}

還存在某些情況下資料表要求設計成邏輯刪除(邏輯刪除存在很多弊端,不建議無腦所有表都設計為邏輯刪除),所以框架同時提供了一個BaseLogicEntity,該實現方式利用的是MP本身自帶的邏輯刪除策略。

@Getter
@Setter
public class BaseLogicEntity<ID_TYPE extends Serializable, TIME_TYPE> extends BaseEntity<ID_TYPE, TIME_TYPE> {

    // 使用了MP支援的邏輯刪除註解
    @TableLogic
    @DefaultValue("0")
    @ColumnComment("邏輯刪除標誌")
    protected Integer deleted;
}

BaseRepository使用

建議開發中以此為資料基本操作類,而不是以*Mapper為基礎操作類,如果需要使用*Mapper中的方法,可以直接通過getMapper()取得Entity對應的*Mapper類,此類與*Mapper類相比做了很多的增強功能,尤其是其lambda語法,非常高效便捷。

// 整合了MP的ServiceImpl,實現了IBaseRepository介面(內部擴充了lambda查詢操作)
public abstract class BaseRepository<M extends BaseMapper<E>, E> extends ServiceImpl<M, E> implements IBaseRepository<E> {

    @Override
    public boolean updateById(E entity) {
        boolean result = super.updateById(entity);
        if(result) {
            // 資料自動更新@DataSource註解的配合邏輯
            SpringContextUtil.getApplicationContext()
                .publishEvent(EntityUpdateEvent.create(entity));
        }
        return result;
    }

    @Override
    public boolean updateBatchById(Collection<E> entityList, int batchSize) {
        boolean result = super.updateBatchById(entityList, batchSize);
        if(result) {
            // 資料自動更新@DataSource註解的配合邏輯
            for (E entity : entityList) {
                SpringContextUtil.getApplicationContext().publishEvent(EntityUpdateEvent.create(entity));
            }
        }
        return result;
    }

    @Override
    protected Class<M> currentMapperClass() {
        return (Class<M>) ReflectionKit.getSuperClassGenericType(this.getClass(), BaseRepository.class, 0);
    }

    @Override
    protected Class<E> currentModelClass() {
        return (Class<E>) ReflectionKit.getSuperClassGenericType(this.getClass(), BaseRepository.class, 1);
    }
}

註解詳細介紹

自動建表註解

只有小部分註解,進行了輕微改動,基本所有註解均是通用的,詳細教程可以直接參考A.CTable官方。

@Table

新增一個primary屬性,isNull屬性為了一致性改為了isNotNull屬性預設false

@TableCharset

@TableComment

@TableEngine

@TablePrimary

新增註解,同步@Table中的primary屬性,在多個Entity對映一張表的情況下,確定主Entity是哪個,資料表生成的時候根據主表來生成。

@IgnoreTable

@EnableTimeSuffix

@Column

@ColumnComment

@ColumnDefault

原@DefaultValue,跟本框架中的資料插入的時候指定預設值的註解重名了,因此把這裡改名字了

@ColumnType

@IsAutoIncrement

@IsKey

@IsNotNull

@IsNativeDefValue

@Unique

@Index

@IgnoreUpdate


資料填充類註解

@OptionDate

描述:

自動賦值資料操作時間。需結合mybatis-plus原框架註解@TableField (該註解的使用請檢視官方文件,懶得看的話,請往下讀,有驚喜)一併使用才有效。

被標註的欄位,在可允許的型別範圍(StringLonglongDateLocalDateLocalDateTime)內,資料被操作的情況下,會自動被賦值上當前時間。

如果使用String的話需要同時指明format參,用以確認格式化後的樣式。

欄位:

屬性 型別 必需 預設值 描述
format String 非必需 yyyy-MM-dd HH:mm:ss 如果欄位型別為String,需要制定字串格式
override boolean 非必需 true 若物件上存在值,是否覆蓋

擴充套件註解:

註解 描述
@InsertOptionDate 基於@OptionDate的擴充,無需結合@TableField ,資料插入的時候,自動賦值資料操作時間。
@UpdateOptionDate 基於@OptionDate的擴充,無需結合@TableField ,資料更新注意:update(Wrapper updateWrapper)方法除外)的時候,自動賦值資料操作時間。
@InsertUpdateOptionDate 基於@OptionDate的擴充,無需結合@TableField ,資料插入更新注意:update(Wrapper updateWrapper)方法除外)的時候,自動賦值資料操作時間。

@OptionUser

描述:

指定實現方式,自動賦值資料操作人員資訊。需結合mybatis-plus原框架註解@TableField (該註解的使用請檢視官方文件,懶得看的話,請往下讀,有驚喜)一併使用才有效。

被標註的欄位,會根據@OptionUserAuditHandler的實現來返回對應的值。

通常的實現方案都是使用者資訊(id、name等)放入header中,全域性定義函式來獲取。

欄位:

屬性 型別 必需 預設值 描述
value Class> 必需 自定義使用者資訊生成方式
override boolean 非必需 true 若物件上存在值,是否覆蓋

擴充套件註解:

註解 描述
@InsertOptionUser 基於@OptionUser的擴充,無需結合@TableField ,資料插入的時候,自動賦值操作人資訊。
@UpdateOptionUser 基於@OptionUser的擴充,無需結合@TableField ,資料更新注意:update(Wrapper updateWrapper)方法除外)的時候,自動賦值操作人資訊。
@InsertUpdateOptionUser 基於@OptionUser的擴充,無需結合@TableField ,資料插入更新注意:update(Wrapper updateWrapper)方法除外)的時候,自動賦值操作人資訊。

@DefaultValue

描述:

資料插入的時候欄位的預設值,支援型別:String, Integer, int, Long, long, Boolean, boolean, Double, double, Float, float, BigDecimal, Date, LocalDate, LocalDateTime,列舉(僅支援列舉的名字作為預設值)

欄位:

屬性 型別 必需 預設值 描述
value String 必需 預設值
format boolean 非必需 yyyy-MM-dd HH:mm:ss 如果欄位型別為時間型別(Date,LocalDateTime等),需要制定字串格式

關聯查詢類註解

@BindField

描述:

繫結其他Entity的某個欄位,可實現一對一、一對多的繫結查詢。

注意:所有Bind註解底層均依賴相關Entity的Mapper,且Mapper必須繼承MybatisPlus的BaseMapper<Entity, ID>

欄位:

屬性 型別 必需 預設值 描述
entity Class<?> 被關聯的Entity
field String 被關聯的Entity的具體欄位
conditions @JoinCondition[] 關聯Entity所需要的條件
customCondition String 被關聯的Entity所需要的額外條件,通常指被關聯的Entity自身的特殊條件,例如:enable=1 and is_deleted=0
orderBy @JoinOrderBy[] 排序條件,被關聯的Entity或者欄位為結果集的時候生效

@BindEntity

描述:

繫結其他Entity,可實現一對一、一對多的繫結查詢。

注意:所有Bind註解底層均依賴相關Entity的Mapper,且Mapper必須繼承MybatisPlus的BaseMapper<Entity, ID>

欄位:

屬性 型別 必需 預設值 描述
entity Class<?> 欄位宣告型別 被關聯的Entity,不再需要顯示的指明,預設取欄位上的宣告型別
conditions @JoinCondition[] 關聯Entity所需要的條件
customCondition String 被關聯的Entity所需要的額外條件,通常指被關聯的Entity自身的特殊條件,例如:enable=1 and is_deleted=0
orderBy @JoinOrderBy[] 排序條件,被關聯的Entity或者欄位為結果集的時候生效
deepBind boolean false 深度繫結,列表資料的情況下會產生效能問題。(不熟悉的,不建議使用)

@JoinCondition

描述:

繫結條件

欄位:

屬性 型別 必需 預設值 描述
selfField String 關聯Entity所需的自身欄位
joinField String "id" 被關聯Entity的關聯欄位,預設為關聯Entity的id

@JoinOrderBy

描述:

繫結結果的排序

欄位:

屬性 型別 必需 預設值 描述
field String 被關聯的Entity中結果集排序欄位
isAsc boolean false 排序,true:正序,false:倒序

@BindFieldByMid

描述:

通過中間關係Entity的形式繫結其他Entity的某個欄位,可實現一對一、一對多、多對多的繫結查詢。

注意:所有Bind註解底層均依賴相關Entity的Mapper,且Mapper必須繼承MybatisPlus的BaseMapper<Entity, ID>

欄位:

屬性 型別 必需 預設值 描述
entity Class<?> 被關聯的Entity
field String 被關聯的Entity的具體欄位
conditions @MidCondition 中間表關聯條件
customCondition String 被關聯的Entity所需要的額外條件,通常指被關聯的Entity自身的特殊條件,例如:enable=1 and is_deleted=0
orderBy @JoinOrderBy[] 排序條件,被關聯的Entity或者欄位為結果集的時候生效

@BindEntityByMid

描述:

通過中間關係Entity的形式繫結其他Entity,可實現一對一、一對多、多對多的繫結查詢。

注意:所有Bind註解底層均依賴相關Entity的Mapper,且Mapper必須繼承MybatisPlus的BaseMapper<Entity, ID>

欄位:

屬性 型別 必需 預設值 描述
entity Class<?> 被關聯的Entity
conditions @MidCondition 中間表關聯條件
customCondition String 被關聯的Entity所需要的額外條件,通常指被關聯的Entity自身的特殊條件,例如:enable=1 and is_deleted=0
orderBy @JoinOrderBy[] 排序條件,被關聯的Entity或者欄位為結果集的時候生效
deepBind boolean false 深度繫結,列表資料的情況下會產生效能問題。(不熟悉的,不建議使用)

@MidCondition

描述:

中間表條件描述

欄位:

屬性 型別 必需 預設值 描述
midEntity Class<?> 中間表Entity,需要對應建立其Mapper
selfField String "Id" 關聯Entity所需的自身欄位
selfMidField String 關聯Entity所需的自身欄位,中間表欄位名
joinField String "id" 被關聯Entity的關聯欄位
joinMidField String 被關聯Entity的關聯欄位,中間表欄位名

資料同步註解

@DataSource

描述:

通過註解指定資料來源,底層框架自動通過Spring中的事件機制監聽EntityUpdateEvent事件,完成資料自動更新。在BaseRepository<Mapper, Entity>的基類中,預設實現了updateById、updateBatchById兩個方法自動釋出EntityUpdateEvent事件,所以只要對應Entity的Repository繼承了BaseRepository<Mapper, Entity>便具備了通過ID更新資料的自動同步資料的功能。

擴充:分散式情況下如何同步其他服務的資料_?不妨先想一想。其實sourceName屬性就是為此情況預留的,引入外部MQ,監聽Spring下的EntityUpdateEvent事件,然後推送至MQ,另一邊消費MQ中的事件,再還原出EntityUpdateEvent事件廣播到各個系統即可,這其中還需要考慮和解決時序和事務的問題。

欄位:

屬性 型別 必需 預設值 描述
source Class<?> 否,與sourceName二選一 Void.class 資料來源的Entity class
sourceName String 否,與source二選一 "" 資料來源的Entity class 的全路徑名稱(包名.類名)
field String 資料來源的Entity對應的屬性
conditions Condition[] 被關聯的Entity所需要的條件

@Condition

描述:

資料來源的關聯條件

欄位:

屬性 型別 必需 預設值 描述
selfField String 關聯資料來源Entity所需的自身欄位
sourceField String "id" 資料來源的Entity的欄位,預設為id

動態條件註解

@DynamicCondition

描述:

適用場景:資料篩選,比如根據不同許可權獲取不同資料,使用者只能看到自己的資料,管理員能看到所有人的資料。

具體demo移步快速開始的例子。

欄位:

屬性 型別 必需 預設值 描述
value Class<? extends IDynamicConditionHandler> IDynamicConditionHandler介面有兩個方法,enable()決定了該條件是否生效,values()是條件匹配的值。

相關文章