• 设为首页
  • 点击收藏
  • 手机版
    手机扫一扫访问
    迪恩网络手机版
  • 关注官方公众号
    微信扫一扫关注
    公众号

mybatis-plus-ext: mybatis-plus框架的拓展包,在框架原有基础上做了进一步的轻度封装 ...

原作者: [db:作者] 来自: 网络 收藏 邀请

开源软件名称:

mybatis-plus-ext

开源软件地址:

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

开源软件介绍:

简介

本框架结合公司日常业务场景,对Mybatis-Plus 做了进一步的拓展封装,即保留MP原功能,又添加更多有用便捷的功能。具体拓展体现在数据自动填充(类似JPA中的审计)关联查询(类似sql中的join)自动建表(仅支持mysql)冗余数据自动更新动态条件等功能做了补充完善。其中自动建表,是在A.CTable 框架上的基础上改进适配本框架的,只保留了其表创建功能,因此改动较大不与原框架兼容。

前言

如果感觉框架对您有所帮助,请给个小星星⭐️,作者二线不知名小公司码农一枚,欢迎来撩共同进步。image-20210826172002744

「二维码看不到的查看项目文档下"微信.png"」

原理介绍

​ 基于注解的形式,将日常工作中重复的模板式代码进行了封装,底层实现完全调用的Mybatis-Plus的框架,全都是走的单表查询的方式,所以不用担心数据库兼容问题(自动建表功能除外,只支持mysql),同样也不需要担心性能问题(前提是正确使用[捂脸]),因为框架内部会自动做查询整合。

快速开始

引入jar包

starter内自带了MybatisPlus及spring-boot的依赖管理,如果要更改springboot的版本,可以排除掉,但是如果要变更MybatisPlus的版本,请注意了,框架中重写了MP中的TableInfoHelper类,不同版本的MP该类有所变动,同时框架内也采用了MP的部分工具类,例如LambdaUtils、ReflectionKit等在不同的版本也有所变动,需要小心,哈哈哈哈,可以联系我帮你改~~

框架在设计上,尽量以拓展的功能为单位做了模块拆分,所有功能均能独立引入也可以合并引入,大家视情况选用吧。

<!-- !!!重点说明,框架内部已经引入了MybatisPlus的包,自己项目中的需要去掉!!! --><!-- 全功能整体引入 --><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><!-- 如果想只引入关联查询 --><dependency>    <groupId>com.tangzc</groupId>    <artifactId>mybatis-plus-ext-bind</artifactId>    <version>{maven仓库搜索最新版}</version></dependency><!-- 如果想只引入数据冗余(关联更新) --><dependency>    <groupId>com.tangzc</groupId>    <artifactId>mybatis-plus-ext-datasource</artifactId>    <version>{maven仓库搜索最新版}</version></dependency><!-- 如果想只引入动态条件 --><dependency>    <groupId>com.tangzc</groupId>    <artifactId>mybatis-plus-ext-condition</artifactId>    <version>{maven仓库搜索最新版}</version></dependency>

自动建表

根据实体上的注解及字段注解自动创建、更新数据库表。

本模块核心代码采用了A.CTable框架,因该框架与本框架的需求不太符合,因此就对其进行了一版魔改,具体改动如下:

  1. 官方的设计思路是默认Bean下的所有字段均不是表字段,需要手动通过@Column声明,我在引用过来之后,改为了默认所有字段均为表字段,只有被MP的@TableField(exist=false)修饰的才会被排除,具备@TableField(exist=false)功能的注解有:@Exclude、@Bind**系列,他们集成了@TableField,且内置exist属性为false了。
  2. A.CTable框架内部集成了类似MP的功能,不如MP完善,所以我也剔除掉了,顺带解决了不兼容和bug。
  3. 像DefaultValue注解与本框架内部注解重名了,因此改名为ColumnDefault。
  4. 整理了一遍内部的注解,利用spring的AliasFor做了关联,更方便管理。
  5. @Table里面加了一个primary属性(对应@TablePrimary),表示是否为主表,为了支持多个Entity对应一个数据库表(正常用不到请忽略^_^)。
  6. @Table里面加了一个dsName属性(对应@DsName),可以配合MP的多数据框架实现不同的表在不同数据源下创建。
  7. 数据库类型映射改动增加对MySQL8的支持,Double数据类型,自动保留2位小数,BigDecimal类型保留4位小数。
  8. 数据库表名和字段名的生成会参照mybatis-plus的配置:mybatis-plus.global-config.db-config.table-underlinemybatis-plus.configuration.map-underscore-to-camel-case决定是否自动驼峰转下划线,完成了跟mybatis-plus的一致性。
@Data// @Table标记的可被识别为需要自动创建表的Entity@Table(comment = "用户")public class User {    // 自动识别id属性名为主键    // @IsAutoIncrement声明为自增主键,什么都不声明的话,默认为雪花算法的唯一主键(MP的自带功能),推荐默认便于后期的数据分布式存储等处理。    @IsAutoIncrement    // 字段注释、类型、长度。@Column的所有属性均有独立的注解对应,具体请参照后面的注解介绍    @Column(comment = "主键", type = MySqlTypeConstant.BIGINT, length = 32)    private String id;    // 索引    @Index    // 非空    @IsNotNull    @ColumnComment("名字")    private String name;    // 唯一索引    @Unique    // 非空    @IsNotNull    @ColumnComment("手机号")    private String phone;    // 省略其他属性    ......}
// 启用自动生成数据库表功能,此处简化了A.CTable的复杂配置,均采用默认配置@EnableAutoTable@SpringBootApplicationpublic class DemoApplication {    public static void main(String[] args) {        SpringApplication.run(DemoApplication.class, args);    }}
# actable的配置信息保留了如下几项,均做了默认配置,正常无需配置actable.table.auto=updateactable.model.pack=[Spring启动类所在包]actable.database.type=mysqlactable.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的使用。 */@Componentpublic 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");    }}
/** * 全局获取用户名 */@Componentpublic 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@Servicepublic class UserService {    // UserRepository继承了BaseRepository<UserMapper, User>,后面会讲BaseRepository    @Resource    private UserRepository userRepository;    /**     * 根据用户的名字模糊查询所有用户的详细信息     */    @Transactional(readOnly = true)    public List<UserDetailWithRoleDto> searchUserWithRuleByName(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);      	// 用法二、全关联。此种用法默认关联user下所有声明需要绑定的属性        // Binder.bind(userList);        return UserMapping.MAPPER.toDto5(userList);    }    /**     * 根据用户的名字模糊查询所有用户的详细信息,等价于上一个查询方式     */    @Transactional(readOnly = true)    public List<UserDetailWithRoleDto> searchUserWithRuleByName2(String name) {        // 本框架拓展的lambda查询器lambdaQueryPlus,增加了bindOne、bindList、bindPage        // 显然这是一种更加简便的查询方式,但是如果存在多级深度的关联关系,此种方法就不适用了,还需要借助Binder        List<User> userList = userRepository.lambdaQueryPlus()                .eq(name != null, User::getUsername, name)          			// 用法一、指定属性关联。                .bindList(User::getRoles);      					// 用法二、全关联。      					// .bindList();        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), Role::getMenus);
注意

鲜花

握手

雷人

路过

鸡蛋
该文章已有0人参与评论

请发表评论

全部评论

专题导读
热门推荐
热门话题
阅读排行榜

扫描微信二维码

查看手机版网站

随时了解更新最新资讯

139-2527-9053

在线客服(服务时间 9:00~18:00)

在线QQ客服
地址:深圳市南山区西丽大学城创智工业园
电邮:jeky_zhao#qq.com
移动电话:139-2527-9053

Powered by 互联科技 X3.4© 2001-2213 极客世界.|Sitemap