MybatisPlus 多數(shù)據(jù)源自動建表套媚、級聯(lián)查詢、自動填充.......

簡介

本框架結(jié)合公司日常業(yè)務場景真朗,對[Mybatis-Plus] 做了進一步的拓展封裝介却,即保留MP原功能臼闻,又添加更多有用便捷的功能有巧。具體拓展體現(xiàn)在數(shù)據(jù)自動填充(類似JPA中的審計)鹉胖、關聯(lián)查詢(類似sql中的join)自動建表(僅支持mysql)审编、冗余數(shù)據(jù)自動更新撼班、動態(tài)條件等功能做了補充完善歧匈。其中自動建表垒酬,是在[A.CTable]框架上的基礎上改進適配本框架的,只保留了其表創(chuàng)建功能件炉,因此改動較大不與原框架兼容勘究。

詳細設計及源碼請移步碼云搜索mybatis-plus-ext

原理介紹

基于注解的形式,將日常工作中重復的模板式代碼進行了封裝斟冕,底層實現(xiàn)完全調(diào)用的Mybatis-Plus的框架口糕,全都是走的單表查詢的方式,所以不用擔心數(shù)據(jù)庫兼容問題(自動建表功能除外磕蛇,只支持mysql)景描,同樣也不需要擔心性能問題(前提是正確使用[捂臉])十办,因為框架內(nèi)部會自動做查詢整合。

快速開始

引入jar包

starter內(nèi)自帶了MybatisPlus及spring-boot的依賴管理超棺,如果要更改springboot的版本向族,可以排除掉,但是如果要變更MybatisPlus的版本棠绘,請注意了件相,框架中重寫了MP中的TableInfoHelper類,不同版本的MP該類有所變動氧苍,同時框架內(nèi)也采用了MP的部分工具類夜矗,例如LambdaUtils、ReflectionKit等在不同的版本也有所變動让虐,需要小心紊撕,哈哈哈哈,可以聯(lián)系我?guī)湍愀膥~

框架在設計上赡突,盡量以拓展的功能為單位做了模塊拆分逛揩,所有功能均能獨立引入也可以合并引入,大家視情況選用吧麸俘。

<!-- 1缁!从媚!重點說明逞泄,框架內(nèi)部已經(jīng)引入了MybatisPlus的包,自己項目中的需要去掉0菪АE缰凇! -->
<!-- 全功能整體引入 -->
<dependency>
    <groupId>com.tangzc</groupId>
    <artifactId>mybatis-plus-ext-boot-starter</artifactId>
    <version>{maven倉庫搜索最新版}</version>
</dependency>
<!-- 如果想只引入自動建表 -->
<dependency>
    <groupId>com.tangzc</groupId>
    <artifactId>mybatis-plus-ext-actable-core</artifactId>
    <version>{maven倉庫搜索最新版}</version>
</dependency>
<!-- 如果想只引入關聯(lián)查詢 -->
<dependency>
    <groupId>com.tangzc</groupId>
    <artifactId>mybatis-plus-ext-bind</artifactId>
    <version>{maven倉庫搜索最新版}</version>
</dependency>
<!-- 如果想只引入數(shù)據(jù)冗余(關聯(lián)更新) -->
<dependency>
    <groupId>com.tangzc</groupId>
    <artifactId>mybatis-plus-ext-datasource</artifactId>
    <version>{maven倉庫搜索最新版}</version>
</dependency>
<!-- 如果想只引入動態(tài)條件 -->
<dependency>
    <groupId>com.tangzc</groupId>
    <artifactId>mybatis-plus-ext-condition</artifactId>
    <version>{maven倉庫搜索最新版}</version>
</dependency>

自動建表

根據(jù)實體上的注解及字段注解自動創(chuàng)建紧憾、更新數(shù)據(jù)庫表到千。

本模塊核心代碼采用了A.CTable框架,因該框架與本框架的需求不太符合赴穗,因此就對其進行了一版魔改憔四,具體改動如下:

  1. 官方的設計思路是默認Bean下的所有字段均不是表字段,需要手動通過@Column聲明般眉,我在引用過來之后了赵,改為了默認所有字段均為表字段,只有被MP的@TableField(exist=false)修飾的才會被排除甸赃,具備@TableField(exist=false)功能的注解有:@Exclude柿汛、@Bind**系列,他們集成了@TableField埠对,且內(nèi)置exist屬性為false了络断。
  2. A.CTable框架內(nèi)部集成了類似MP的功能裁替,不如MP完善,所以我也剔除掉了貌笨,順帶解決了不兼容和bug胯究。
  3. 像DefaultValue注解與本框架內(nèi)部注解重名了,因此改名為ColumnDefault躁绸。
  4. 整理了一遍內(nèi)部的注解裕循,利用spring的AliasFor做了關聯(lián),更方便管理净刮。
  5. @Table里面加了一個primary屬性(對應@TablePrimary)剥哑,表示是否為主表,為了支持多個Entity對應一個數(shù)據(jù)庫表(正常用不到請忽略_)淹父。
  6. @Table里面加了一個dsName屬性(對應@DsName)株婴,可以配合MP的多數(shù)據(jù)框架實現(xiàn)不同的表在不同數(shù)據(jù)源下創(chuàng)建。
  7. 數(shù)據(jù)庫類型映射改動增加對MySQL8的支持暑认,Double數(shù)據(jù)類型困介,自動保留2位小數(shù),BigDecimal類型保留4位小數(shù)蘸际。
  8. 數(shù)據(jù)庫表名和字段名的生成會參照mybatis-plus的配置:mybatis-plus.global-config.db-config.table-underlinemybatis-plus.configuration.map-underscore-to-camel-case決定是否自動駝峰轉(zhuǎn)下劃線座哩,完成了跟mybatis-plus的一致性。
@Data
// @Table標記的可被識別為需要自動創(chuàng)建表的Entity
@Table(comment = "用戶")
public class User {

    // 自動識別id屬性名為主鍵
    // @IsAutoIncrement聲明為自增主鍵粮彤,什么都不聲明的話根穷,默認為雪花算法的唯一主鍵(MP的自帶功能),推薦默認便于后期的數(shù)據(jù)分布式存儲等處理导坟。
    @IsAutoIncrement
    // 字段注釋屿良、類型、長度惫周。@Column的所有屬性均有獨立的注解對應尘惧,具體請參照后面的注解介紹
    @Column(comment = "主鍵", type = MySqlTypeConstant.BIGINT, length = 32)
    private String id;

    // 索引
    @Index
    // 非空
    @IsNotNull
    @ColumnComment("名字")
    private String name;

    // 唯一索引
    @Unique
    // 非空
    @IsNotNull
    @ColumnComment("手機號")
    private String phone;

    // 省略其他屬性
    ......
}
// 啟用自動生成數(shù)據(jù)庫表功能,此處簡化了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_

數(shù)據(jù)填充

可以在數(shù)據(jù)插入或更新的時候漾狼,自動賦值數(shù)據(jù)操作人重慢、操作時間、默認值等屬性逊躁。

以文章發(fā)布為例,講解一下數(shù)據(jù)填充的基本用法隅熙。通過如下例子可發(fā)現(xiàn)稽煤,在創(chuàng)建Artice的時候核芽,我們無需再去關心過多的與業(yè)務無關的字段值,只需要關心title酵熙、content兩個核心數(shù)據(jù)即可轧简,其他的數(shù)據(jù)均會被框架處理。

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

    // 字符串類型的ID匾二,默認也是雪花算法的一串數(shù)字(MP的默認功能)
    @ColumnComment("主鍵")
    private String id;

    @ColumnComment("標題")
    private String title;

    @ColumnComment("內(nèi)容")
    private String content;

    // 文章默認激活狀態(tài)
    @DefaultValue("ACTIVE")
    @ColumnComment("內(nèi)容")
    // ActicleStatusEnum(ACTIVE, INACTIVE)
    private ActicleStatusEnum status;

    @ColumnComment("發(fā)布時間")
    // 插入數(shù)據(jù)時候會自動獲取系統(tǒng)當前時間賦值哮独,支持多種數(shù)據(jù)類型,具體可參考@OptionDate注解詳細介紹
    @InsertOptionDate
    private Date publishedTime;

    @ColumnComment("發(fā)布人")
    // 插入的時候察藐,根據(jù)UserIdAutoFillHandler自動填充用戶id
    @InsertOptionUser(UserIdAutoFillHandler.class)
    private String publishedUserId;

    @ColumnComment("發(fā)布人名字")
    // 插入的時候皮璧,根據(jù)UserIdAutoFillHandler自動填充用戶名字
    @InsertOptionUser(UsernameAutoFillHandler.class)
    private String publishedUsername;

    @ColumnComment("最后更新時間")
    // 插入和更新數(shù)據(jù)時候會自動獲取系統(tǒng)當前時間賦值,支持多種數(shù)據(jù)類型分飞,具體可參考@OptionDate注解詳細介紹
    @InsertUpdateOptionDate
    private Date publishedTime;

    @ColumnComment("最后更新人")
    // 插入和更新的時候悴务,根據(jù)UserIdAutoFillHandler自動填充用戶id
    @InsertUpdateOptionUser(UserIdAutoFillHandler.class)
    private String publishedUserId;

    @ColumnComment("最后更新人名字")
    // 插入和更新的時候,根據(jù)UserIdAutoFillHandler自動填充用戶名字
    @InsertUpdateOptionUser(UsernameAutoFillHandler.class)
    private String publishedUsername;
}
/**
 * 全局獲取用戶ID
 * 此處實現(xiàn)IOptionByAutoFillHandler接口和AutoFillHandler接口均可譬猫,建議實現(xiàn)IOptionByAutoFillHandler接口讯檐,
 * 因為框架內(nèi)的BaseEntity默認需要IOptionByAutoFillHandler的實現(xiàn)。后面會講到BaseEntity的使用染服。
 */
@Component
public class UserIdAutoFillHandler implements IOptionByAutoFillHandler<String> {

    /**
     * @param object 當前操作的數(shù)據(jù)對象
     * @param clazz  當前操作的數(shù)據(jù)對象的class
     * @param field  當前操作的數(shù)據(jù)對象上的字段
     * @return 當前登錄用戶id
     */
    @Override
    public String getVal(Object object, Class<?> clazz, Field field) {
        RequestAttributes requestAttributes = RequestContextHolder.currentRequestAttributes();
        HttpServletRequest request = ((ServletRequestAttributes)requestAttributes).getRequest();
        // 配合網(wǎng)關或者過濾器别洪,token校驗成功后就把用戶信息塞到header中
        return request.getHeader("user-id");
    }
}
/**
 * 全局獲取用戶名
 */
@Component
public class UsernameAutoFillHandler implements AutoFillHandler<String> {

    /**
     * @param object 當前操作的數(shù)據(jù)對象
     * @param clazz  當前操作的數(shù)據(jù)對象的class
     * @param field  當前操作的數(shù)據(jù)對象上的字段
     * @return 當前登錄用戶id
     */
    @Override
    public String getVal(Object object, Class<?> clazz, Field field) {
        RequestAttributes requestAttributes = RequestContextHolder.currentRequestAttributes();
        HttpServletRequest request = ((ServletRequestAttributes)requestAttributes).getRequest();
        // 配合網(wǎng)關或者過濾器,token校驗成功后就把用戶信息塞到header中
        return request.getHeader("user-name");
    }
}

關聯(lián)查詢

數(shù)據(jù)關聯(lián)查詢的解決方案柳刮,替代sql中的join方式蕉拢,通過注解關聯(lián)多表之間的關系,查詢某實體的時候诚亚,自動帶出其關聯(lián)性的數(shù)據(jù)實體晕换。

本示例以比較復雜的通過中間表關聯(lián)數(shù)據(jù)的案例來講解下,用戶和角色之間多對多站宗,通過中間表進行數(shù)據(jù)級聯(lián)闸准,@BindEntity*系列是關聯(lián)Entity的數(shù)據(jù),@BindField*系列是關聯(lián)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想關聯(lián)對應的Rule集合,中間表是UserRule
    @BindEntityByMid(conditions = @MidCondition(
            midEntity = UserRole.class, selfMidField = "userId", joinMidField = "roleId"
    ))
    private List<Role> roles;
}
@Data
@Table(comment = "用戶-角色關聯(lián)關系")
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;

    /**
     * 根據(jù)用戶的名字模糊查詢所有用戶的詳細信息
     */
    @Transactional(readOnly = true)
    public List<UserDetailWithRoleDto> searchUserWithRuleByName(String name) {

        // MP的lambda查詢方式
        List<User> userList = userRepository.lambdaQuery()
                .eq(name != null, User::getUsername, name)
                .list();
        // 關鍵步驟义屏,指定關聯(lián)角色數(shù)據(jù)。如果你打開sql打印,會看到3條sql語句闽铐,第一條根據(jù)id去User表查詢user信息蝶怔,第二條根據(jù)userId去UserRule中間表查詢所有的ruleId,第三條sql根據(jù)ruleId集合去Rule表查詢?nèi)康臋?quán)限
        // 用法一兄墅、指定屬性關聯(lián)踢星。
        Binder.bindOn(userList, User::getRoles);
        // 用法二、全關聯(lián)隙咸。此種用法默認關聯(lián)user下所有聲明需要綁定的屬性
        // Binder.bind(userList);

        return UserMapping.MAPPER.toDto5(userList);
    }

    /**
     * 根據(jù)用戶的名字模糊查詢所有用戶的詳細信息沐悦,等價于上一個查詢方式
     */
    @Transactional(readOnly = true)
    public List<UserDetailWithRoleDto> searchUserWithRuleByName2(String name) {

        // 本框架拓展的lambda查詢器lambdaQueryPlus,增加了bindOne五督、bindList藏否、bindPage
        // 顯然這是一種更加簡便的查詢方式,但是如果存在多級深度的關聯(lián)關系概荷,此種方法就不適用了秕岛,還需要借助Binder
        List<User> userList = userRepository.lambdaQueryPlus()
                .eq(name != null, User::getUsername, name)
                    // 用法一、指定屬性關聯(lián)误证。
                .bindList(User::getRoles);
                        // 用法二继薛、全關聯(lián)。
                        // .bindList();

        return UserMapping.MAPPER.toDto5(userList);
    }
}

==提示==: 假如存在此種場景:User愈捅、Role遏考、Menu三個實體,他們之間的關系是:User 多對多 Role蓝谨、Role 多對多Menu灌具,當我查詢出User的集合后,如何獲取Role和Menu的數(shù)據(jù)呢譬巫?

// 數(shù)據(jù)庫查詢出了用戶列表 【1】
List<User> userList = userRepository.list();
// 為所有用戶關聯(lián)角色信息 【2】
Binder.bindOn(userList, User::getRoles);
// 為所有角色信息關聯(lián)菜單信息 【3】
// Deeper為一個深度遍歷工具咖楣,可以深入到對象的多層屬性內(nèi)部,從而獲取全局上該層級的所有對象同一屬性
Binder.bindOn(Deeper.with(userList).inList(User::getRoles), Role::getMenus);
注意??:【2】和【3】存在順序依賴芦昔,必須先執(zhí)行【2】才能執(zhí)行【3】

數(shù)據(jù)冗余

當其他表的數(shù)據(jù)需要作為當前表的查詢條件的時候诱贿,多數(shù)情況下會使用sql的join語法,另一種方案是做數(shù)據(jù)冗余咕缎,將其他表的字段冗余到當前表珠十,但是牽扯一個數(shù)據(jù)修改后同步的問題,本框架可以解決凭豪。

假設用戶評論的場景焙蹭,評論上需要冗余用戶名和頭像,如果用戶的名字和頭像有改動嫂伞,則需要同步新的改動孔厉,代碼如下:

@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("評論內(nèi)容")
    private String content;

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

    /* 基于該注解拯钻,框架會自動注冊監(jiān)聽EntityUpdateEvent事件,User的updateById和updateBatchById兩個方法會自動發(fā)布EntityUpdateEvent事件烟馅。
     * 因此说庭,除了mybatis-plus的updateById和updateBatchById兩個更新方法外然磷,其他數(shù)據(jù)更新方式(比如手動寫sql的形式)不會觸發(fā)數(shù)據(jù)自動更新郑趁,需要用戶自己拋出EntityUpdateEvent事件,完成數(shù)據(jù)自動更新
     */
    @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;
}

動態(tài)條件

適用場景:數(shù)據(jù)篩選姿搜,比如根據(jù)不同權(quán)限獲取不同數(shù)據(jù)寡润,用戶只能看到自己的數(shù)據(jù),管理員能看到所有人的數(shù)據(jù)舅柜。

此種場景梭纹,我們通常需要在每一個查詢、更新致份、刪除的sql操作上都追加上某個條件变抽,很容易忘記,但是可以抽象成注解直接配置到Entity上氮块,就省去了每個數(shù)據(jù)操作關心這個特殊條件了绍载。

/**
 * congfig中注冊動態(tài)條件攔截器【1.3.0之前的版本(不包括1.3.0)可以忽略,不注冊該Bean】
 */
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
    MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
    // 添加動態(tài)條件滔蝉,若同時添加了其他的攔截器击儡,繼續(xù)添加即可
    interceptor.addInnerInterceptor(new DynamicConditionInterceptor());
    return interceptor;
}
@Data
@Table(comment = "文章")
public class Article {

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

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

    @ColumnComment("發(fā)布人")
    @InsertOptionUser(UserIdAutoFillHandler.class)
    // 添加了該注解后,針對文章的查詢蝠引、修改阳谍、刪除操作,均會被自動帶上 published_user_id=?或者published_user_id in (?)的條件螃概,?值來自于CurrentUserDynamicConditionHandler的values()返回值
    @DynamicCondition(CurrentUserDynamicConditionHandler.class)
    private String publishedUserId;
    
    // 省略其他字段
    ......
}
@Component
public class CurrentUserDynamicConditionHandler implements IDynamicConditionHandler {

    @Resource
    private HttpServletRequest request;

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

    @Override
    public boolean enable() {
        // 簡單例子:header中取用戶權(quán)限融蹂,如果是非管理員則執(zhí)行該過濾條件旺订,如果是管理員默認查全部,返回false超燃,本動態(tài)條件失效
        String userRule = request.getHeader("USER_ROLE");
        return !"ADMIN".equals(userRule);
    }
}

BaseEntity使用

通常的表設計中区拳,都會要求添加一些審計數(shù)據(jù),比如創(chuàng)建人意乓、創(chuàng)建時間樱调、最后修改人约素、最后修改時間,但是這些屬性又不應該屬于業(yè)務的笆凌,更多的是為了數(shù)據(jù)管理使用的圣猎。如果業(yè)務需要使用的話,建議起一個有意義的業(yè)務名稱與上述的創(chuàng)建時間區(qū)分開乞而,比如用戶的注冊時間(registrationTime)送悔。為了簡化數(shù)據(jù)審計字段的工作量,框架內(nèi)部集成了BaseEntity

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

    // 這里就是數(shù)據(jù)填充樣例那里提到的IOptionByAutoFillHandler接口
    // 此處單獨指定一個標記性的接口是為了區(qū)別用戶其他數(shù)據(jù)的自動填充爪模,例如用戶名欠啤、用戶電話等都會實現(xiàn)AutoFillHandler接口,框架上根據(jù)該接口無法拿到唯一的實現(xiàn)屋灌,因此同樣IOptionByAutoFillHandler在整個系統(tǒng)中也只能有一個實現(xiàn)洁段,不然會報錯。
    @InsertOptionUser(IOptionByAutoFillHandler.class)
    @ColumnComment("創(chuàng)建人")
    protected ID_TYPE createBy;
    @InsertUpdateOptionUser(IOptionByAutoFillHandler.class)
    @ColumnComment("最后更新人")
    protected ID_TYPE updateBy;
    @InsertOptionDate
    @ColumnComment("創(chuàng)建時間")
    protected TIME_TYPE createTime;
    @InsertUpdateOptionDate
    @ColumnComment("最后更新時間")
    protected TIME_TYPE updateTime;
}

還存在某些情況下數(shù)據(jù)表要求設計成邏輯刪除(邏輯刪除存在很多弊端共郭,不建議無腦所有表都設計為邏輯刪除)祠丝,所以框架同時提供了一個BaseLogicEntity,該實現(xiàn)方式利用的是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使用

建議開發(fā)中以此為數(shù)據(jù)基本操作類写半,而不是以*Mapper為基礎操作類,如果需要使用*Mapper中的方法憾赁,可以直接通過getMapper()取得Entity對應的*Mapper類污朽,此類與*Mapper類相比做了很多的增強功能,尤其是其lambda語法龙考,非常高效便捷蟆肆。

// 集成了MP的ServiceImpl,實現(xiàn)了IBaseRepository接口(內(nèi)部拓展了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) {
            // 數(shù)據(jù)自動更新@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) {
            // 數(shù)據(jù)自動更新@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

  1. 新增primary屬性蛇损,對應@TablePrimary
  2. isNull屬性為了一致性改為了isNotNull屬性默認false
  3. 新增dsName屬性,對應@DsName

@TableCharset

@TableComment

@TableEngine

@TablePrimary

新增注解坛怪,等同@Table中的primary屬性淤齐,在多個Entity映射一張表的情況下,確定主Entity是哪個袜匿,數(shù)據(jù)表生成的時候根據(jù)主表來生成更啄。

@DsName

新增注解,等同@Table中的dsName屬性居灯,在多數(shù)據(jù)源場景下祭务,指定某個表的數(shù)據(jù)源内狗。

@IgnoreTable

@EnableTimeSuffix

@Column

@ColumnComment

@ColumnDefault

原@DefaultValue,跟本框架中的數(shù)據(jù)插入的時候指定默認值的注解重名了义锥,因此把這里改名字了

@ColumnType

@IsAutoIncrement

@IsKey

@IsNotNull

@IsNativeDefValue

@Unique

@Index

@IgnoreUpdate


數(shù)據(jù)填充類注解

@OptionDate

描述:

自動賦值數(shù)據(jù)操作時間柳沙。需結(jié)合mybatis-plus原框架注解@TableField (該注解的使用請查看官方文檔,懶得看的話拌倍,請往下讀赂鲤,有驚喜)一并使用才有效。

被標注的字段贰拿,在可允許的類型范圍(String蛤袒、Long熄云、long膨更、DateLocalDate缴允、LocalDateTime)內(nèi)荚守,數(shù)據(jù)被操作的情況下,會自動被賦值上當前時間练般。

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

字段:

屬性 類型 必需 默認值 描述
format String 非必需 yyyy-MM-dd HH:mm:ss 如果字段類型為String薄料,需要制定字符串格式
override boolean 非必需 true 若對象上存在值敞贡,是否覆蓋

擴展注解:

注解 描述
@InsertOptionDate 基于@OptionDate的拓展,無需結(jié)合@TableField 摄职,數(shù)據(jù)插入的時候誊役,自動賦值數(shù)據(jù)操作時間。
@UpdateOptionDate 基于@OptionDate的拓展谷市,無需結(jié)合@TableField 蛔垢,數(shù)據(jù)更新注意:update(Wrapper<T> updateWrapper)方法除外)的時候,自動賦值數(shù)據(jù)操作時間迫悠。
@InsertUpdateOptionDate 基于@OptionDate的拓展鹏漆,無需結(jié)合@TableField ,數(shù)據(jù)插入创泄、更新注意:update(Wrapper<T> updateWrapper)方法除外)的時候艺玲,自動賦值數(shù)據(jù)操作時間。

@OptionUser

描述:

指定實現(xiàn)方式鞠抑,自動賦值數(shù)據(jù)操作人員信息饭聚。需結(jié)合mybatis-plus原框架注解@TableField (該注解的使用請查看官方文檔,懶得看的話碍拆,請往下讀若治,有驚喜)一并使用才有效慨蓝。

被標注的字段,會根據(jù)@OptionUserAuditHandler的實現(xiàn)來返回對應的值端幼。

通常的實現(xiàn)方案都是用戶信息(id礼烈、name等)放入header中,全局定義函數(shù)來獲取婆跑。

字段:

屬性 類型 必需 默認值 描述
value Class<? extends AuditHandler<?>> 必需 自定義用戶信息生成方式
override boolean 非必需 true 若對象上存在值此熬,是否覆蓋

擴展注解:

注解 描述
@InsertOptionUser 基于@OptionUser的拓展,無需結(jié)合@TableField 滑进,數(shù)據(jù)插入的時候犀忱,自動賦值操作人信息。
@UpdateOptionUser 基于@OptionUser的拓展扶关,無需結(jié)合@TableField 阴汇,數(shù)據(jù)更新注意:update(Wrapper<T> updateWrapper)方法除外)的時候,自動賦值操作人信息节槐。
@InsertUpdateOptionUser 基于@OptionUser的拓展搀庶,無需結(jié)合@TableField ,數(shù)據(jù)插入铜异、更新注意:update(Wrapper<T> updateWrapper)方法除外)的時候哥倔,自動賦值操作人信息。

@DefaultValue

描述:

數(shù)據(jù)插入的時候字段的默認值揍庄,支持類型: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等),需要制定字符串格式

關聯(lián)查詢類注解

@BindField

描述:

綁定其他Entity的某個字段蚂子,可實現(xiàn)一對一沃测、一對多的綁定查詢。

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

字段:

屬性 類型 必需 默認值 描述
entity Class<?> 被關聯(lián)的Entity
field String 被關聯(lián)的Entity的具體字段
conditions @JoinCondition[] 關聯(lián)Entity所需要的條件
customCondition String 被關聯(lián)的Entity所需要的額外條件芽突,通常指被關聯(lián)的Entity自身的特殊條件,例如:enable=1 and is_deleted=0
orderBy @JoinOrderBy[] 排序條件董瞻,被關聯(lián)的Entity或者字段為結(jié)果集的時候生效

@BindEntity

描述:

綁定其他Entity寞蚌,可實現(xiàn)一對一、一對多的綁定查詢钠糊。

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

字段:

屬性 類型 必需 默認值 描述
entity Class<?> 字段聲明類型 被關聯(lián)的Entity,不再需要顯示的指明抄伍,默認取字段上的聲明類型
conditions @JoinCondition[] 關聯(lián)Entity所需要的條件
customCondition String 被關聯(lián)的Entity所需要的額外條件艘刚,通常指被關聯(lián)的Entity自身的特殊條件,例如:enable=1 and is_deleted=0
orderBy @JoinOrderBy[] 排序條件截珍,被關聯(lián)的Entity或者字段為結(jié)果集的時候生效
deepBind boolean false 深度綁定攀甚,列表數(shù)據(jù)的情況下會產(chǎn)生性能問題箩朴。(不熟悉的,不建議使用)

@JoinCondition

描述:

綁定條件

字段:

屬性 類型 必需 默認值 描述
selfField String 關聯(lián)Entity所需的自身字段
joinField String "id" 被關聯(lián)Entity的關聯(lián)字段秋度,默認為關聯(lián)Entity的id

@JoinOrderBy

描述:

綁定結(jié)果的排序

字段:

屬性 類型 必需 默認值 描述
field String 被關聯(lián)的Entity中結(jié)果集排序字段
isAsc boolean false 排序炸庞,true:正序,false:倒序

@BindFieldByMid

描述:

通過中間關系Entity的形式綁定其他Entity的某個字段荚斯,可實現(xiàn)一對一埠居、一對多、多對多的綁定查詢事期。

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

字段:

屬性 類型 必需 默認值 描述
entity Class<?> 被關聯(lián)的Entity
field String 被關聯(lián)的Entity的具體字段
conditions @MidCondition 中間表關聯(lián)條件
customCondition String 被關聯(lián)的Entity所需要的額外條件,通常指被關聯(lián)的Entity自身的特殊條件兽泣,例如:enable=1 and is_deleted=0
orderBy @JoinOrderBy[] 排序條件绎橘,被關聯(lián)的Entity或者字段為結(jié)果集的時候生效

@BindEntityByMid

描述:

通過中間關系Entity的形式綁定其他Entity,可實現(xiàn)一對一撞叨、一對多金踪、多對多的綁定查詢。

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

字段:

屬性 類型 必需 默認值 描述
entity Class<?> 被關聯(lián)的Entity
conditions @MidCondition 中間表關聯(lián)條件
customCondition String 被關聯(lián)的Entity所需要的額外條件,通常指被關聯(lián)的Entity自身的特殊條件法希,例如:enable=1 and is_deleted=0
orderBy @JoinOrderBy[] 排序條件枷餐,被關聯(lián)的Entity或者字段為結(jié)果集的時候生效
deepBind boolean false 深度綁定,列表數(shù)據(jù)的情況下會產(chǎn)生性能問題苫亦。(不熟悉的毛肋,不建議使用)

@MidCondition

描述:

中間表條件描述

字段:

屬性 類型 必需 默認值 描述
midEntity Class<?> 中間表Entity,需要對應創(chuàng)建其Mapper
selfField String "Id" 關聯(lián)Entity所需的自身字段
selfMidField String 關聯(lián)Entity所需的自身字段屋剑,中間表字段名
joinField String "id" 被關聯(lián)Entity的關聯(lián)字段
joinMidField String 被關聯(lián)Entity的關聯(lián)字段润匙,中間表字段名

數(shù)據(jù)同步注解

@DataSource

描述:

通過注解指定數(shù)據(jù)來源,底層框架自動通過Spring中的事件機制監(jiān)聽EntityUpdateEvent事件唉匾,完成數(shù)據(jù)自動更新孕讳。在BaseRepository<Mapper, Entity>的基類中,默認實現(xiàn)了updateById巍膘、updateBatchById兩個方法自動發(fā)布EntityUpdateEvent事件厂财,所以只要對應Entity的Repository繼承了BaseRepository<Mapper, Entity>便具備了通過ID更新數(shù)據(jù)的自動同步數(shù)據(jù)的功能。

拓展:分布式情況下如何同步其他服務的數(shù)據(jù)_峡懈?不妨先想一想璃饱。其實sourceName屬性就是為此情況預留的,引入外部MQ肪康,監(jiān)聽Spring下的EntityUpdateEvent事件荚恶,然后推送至MQ撩穿,另一邊消費MQ中的事件,再還原出EntityUpdateEvent事件廣播到各個系統(tǒng)即可谒撼,這其中還需要考慮和解決時序和事務的問題冗锁。

字段:

屬性 類型 必需 默認值 描述
source Class<?> 否,與sourceName二選一 Void.class 數(shù)據(jù)來源的Entity class
sourceName String 否嗤栓,與source二選一 "" 數(shù)據(jù)來源的Entity class 的全路徑名稱(包名.類名)
field String 數(shù)據(jù)來源的Entity對應的屬性
conditions Condition[] 被關聯(lián)的Entity所需要的條件

@Condition

描述:

數(shù)據(jù)來源的關聯(lián)條件

字段:

屬性 類型 必需 默認值 描述
selfField String 關聯(lián)數(shù)據(jù)來源Entity所需的自身字段
sourceField String "id" 數(shù)據(jù)來源的Entity的字段冻河,默認為id

動態(tài)條件注解

@DynamicCondition

描述:

適用場景:數(shù)據(jù)篩選,比如根據(jù)不同權(quán)限獲取不同數(shù)據(jù)茉帅,用戶只能看到自己的數(shù)據(jù)叨叙,管理員能看到所有人的數(shù)據(jù)。

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

字段:

屬性 類型 必需 默認值 描述
value Class<? extends IDynamicConditionHandler> IDynamicConditionHandler接口有兩個方法擂错,enable()決定了該條件是否生效,values()是條件匹配的值樱蛤。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末钮呀,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子昨凡,更是在濱河造成了極大的恐慌爽醋,老刑警劉巖,帶你破解...
    沈念sama閱讀 223,002評論 6 519
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件便脊,死亡現(xiàn)場離奇詭異蚂四,居然都是意外死亡,警方通過查閱死者的電腦和手機哪痰,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,357評論 3 400
  • 文/潘曉璐 我一進店門遂赠,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人晌杰,你說我怎么就攤上這事跷睦。” “怎么了肋演?”我有些...
    開封第一講書人閱讀 169,787評論 0 365
  • 文/不壞的土叔 我叫張陵抑诸,是天一觀的道長。 經(jīng)常有香客問我惋啃,道長哼鬓,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 60,237評論 1 300
  • 正文 為了忘掉前任边灭,我火速辦了婚禮异希,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己称簿,他們只是感情好扣癣,可當我...
    茶點故事閱讀 69,237評論 6 398
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著憨降,像睡著了一般父虑。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上授药,一...
    開封第一講書人閱讀 52,821評論 1 314
  • 那天士嚎,我揣著相機與錄音,去河邊找鬼悔叽。 笑死莱衩,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的娇澎。 我是一名探鬼主播笨蚁,決...
    沈念sama閱讀 41,236評論 3 424
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼趟庄!你這毒婦竟也來了括细?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 40,196評論 0 277
  • 序言:老撾萬榮一對情侶失蹤戚啥,失蹤者是張志新(化名)和其女友劉穎奋单,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體虑鼎,經(jīng)...
    沈念sama閱讀 46,716評論 1 320
  • 正文 獨居荒郊野嶺守林人離奇死亡辱匿,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,794評論 3 343
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了炫彩。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,928評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡絮短,死狀恐怖江兢,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情丁频,我是刑警寧澤杉允,帶...
    沈念sama閱讀 36,583評論 5 351
  • 正文 年R本政府宣布,位于F島的核電站席里,受9級特大地震影響叔磷,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜奖磁,卻給世界環(huán)境...
    茶點故事閱讀 42,264評論 3 336
  • 文/蒙蒙 一改基、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧咖为,春花似錦秕狰、人聲如沸稠腊。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,755評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽歼冰。三九已至臭杰,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間叹放,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,869評論 1 274
  • 我被黑心中介騙來泰國打工挠羔, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留井仰,地道東北人。 一個月前我還...
    沈念sama閱讀 49,378評論 3 379
  • 正文 我出身青樓褥赊,卻偏偏與公主長得像糕档,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子拌喉,可洞房花燭夜當晚...
    茶點故事閱讀 45,937評論 2 361

推薦閱讀更多精彩內(nèi)容