# toolkit-clean **Repository Path**: glin/toolkit-clean ## Basic Information - **Project Name**: toolkit-clean - **Description**: MyBatis开发增强;是MyBatis-Plus的替换方案 为什么会有这样一个框架? MyBatis-Plus的不足:1. 庞杂的类封装模式 2. selectone limit 1的问题 3. dao层对程序架构的侵入,破坏了整个架构风格的完整性 4. dao层对entity的依赖绑定 5. 分库分表的不足 6. Service层名称与实际作用的不匹配,引发整个项目命名错误的问题 - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2025-05-20 - **Last Updated**: 2025-07-18 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README ## 0️⃣ 简介 这是一个mybatis的增强工具,是MyBatis-Plus的替换方案 ## 一、用法 1. 需要被加强的DAO层(MyBatis Mapper 接口),必须使用@Mapper(org.apache.ibatis.annotations.Mapper)注解标识 2. 需要被加强的DAO层(MyBatis Mapper 接口),必须继承CrudMapper(pub.cleangao.mybatis.dao.CrudMapper) 3. 需要扫描的Entity必须使用@Table注解(pub.cleangao.mybatis.annotation.Table)标识(必要时使用@Database注解) 4. 需要在应用项目的resources目录创建mybatis-enhance.properties文件;(或任何可加载资源的目录中) *文件内容如下:* ``` ## 实体类扫描路径 module.orm.entity-package = com.example.demo.entity ## dao扫描路径 module.orm.mapper-package = com.example.demo.mapper ``` 因:此工具组件是服务于开发者;此诸配置项无运行期修改可能;所以只支持了单独创建一个配置文件这种方式。 :-) 5. SpringBoot项目:pom.xml Maven依赖;会自动加载启动 ``` pub.cleangao mybatis-springboot3 1.2.2 ``` 其中包含了springboot使用mybatis的依赖 ``` org.mybatis.spring.boot mybatis-spring-boot-starter 3.0.4 ``` 非SpringBoot项目:pom.xml ``` pub.cleangao mybatis 1.2.2 ``` // 启用组件;需要传入MyBatis的sqlSessionFactories pub.cleangao.mybatisComponent.start(sqlSessionFactoryList); ## 二、注解详解 1. pub.cleangao.mybatis.annotation.Table: 用于Entity,数据表对应的实体类 2. pub.cleangao.mybatis.annotation.Column: 用于实体类中的字段,可省略(自动驼峰和下划线转换) 3. pub.cleangao.mybatis.annotation.LogicDelete: 逻辑删除字段标识 4. pub.cleangao.mybatis.annotation.PrimaryKey: 主键标识 5. pub.cleangao.mybatis.annotation.Version: 乐观锁字段标识 6. pub.cleangao.mybatis.cleavage.UseCleavage: 用在Mapper方法上的分库分表注解 7. pub.cleangao.mybatis.annotation.Database: 用于实体类上, 标识数据库类型;分库辅助注解 ## 三、增删改查 #### 美中不足(设计如此):用户自定义mapper中,若存在与此组件定义同名方法,将被此组件方法覆盖。 ### 1. 增 - (1)int saveAllFields(E entity); 保存,保存所有字段 - (2)int save(E entity); 保存,忽略null字段 - (3)int saveMultiSet(Collection entities); 批量保存 - (4)int saveUnique(Collection entities) 批量保存,去除重复行,通过对象是否相对判断重复数据,实体类需要实现equals方法. - (5)int saveUnique(Collection entities, Comparator comparator) 批量保存,去除重复行,指定比较器判断 ### 2. 删 - (1)int delete(E entity); 删除记录(底层根据id删除),在有逻辑删除字段的情况下,做UPDATE操作。 - (2)int deleteById(I id); 根据id删除,在有逻辑删除字段的情况下,做UPDATE操作 - (3)int deleteByQuery(Query query); 根据条件删除,在有逻辑删除字段的情况下,做UPDATE操作 - (4)int forceDelete(E entity); 强制删除(底层根据id删除),忽略逻辑删除字段,执行DELETE语句 - (5)int forceDeleteById(I id); 根据id强制删除,忽略逻辑删除字段,执行DELETE语句 - (6)int forceDeleteByQuery(Query query); 根据条件强制删除,忽略逻辑删除字段,执行DELETE语句 - (7)int deleteByIds(Collection ids); 根据多个主键id删除,在有逻辑删除字段的情况下,做UPDATE操作 - (8)int deleteByColumn(String column, Object value); 根据指定字段值删除,在有逻辑删除字段的情况下,做UPDATE操作 ### 3. 改 - (1)int updateAllFields(E entity); 更新,更新所有字段 - (2)int update(E entity); 更新,忽略null字段 - (3)int updateByQuery(E entity,Query query); 根据条件更新 - (4)int updateByMap(Map map, Query query); 根据条件更新 ### 4. 查 - (1)E getById(I id); 根据主键查询 - (2)E forceById(I id); 根据主键查询强制查询,忽略逻辑删除字段 - (3)E getByQuery(Query query); 根据条件查找单条记录 - (4)List listSpecificColumns(List columns,Query query); 查询返回指定的列,返回实体类集合 - (5)E getByColumn(String column, Object value); 根据字段查询一条记录 - (6)long getCount(Query query); 查询总记录数 - (7)List listByColumn(String column, Object value); 根据字段查询结果集 - (8)List list(@Param("query") Query query); 查询结果集 - (9)E getSpecificColumns(List columns, Query query); 查询单条数据并返回指定字段 - (10) T getSpecificColumns(List columns, Query query, Class clazz); 查询单条数据返回指定字段并转换到指定类中 - (11) T getColumnValue(String column, Query query, Class clazz); 查询某一行某个字段值 - (12)List listByIds(Collection ids); 根据多个主键查询 - (13)List listInArray(String column, Object[] values); 根据多个字段值查询结果集 - (14)List listInCollection(String column, Collection values); 根据字段多个值查询结果集 - (15) List listSpecificColumns(List columns, Query query, Class clazz); 查询返回指定的列,返指定类集合 - (16) List listColumnValues(String column, Query query, Class clazz);查询指定列,返指定列集合 - (17) Page pageSpecificColumns(List columns, Query query, Class clazz); 查询返回指定的列,返回分页数据 - (18)Page page(Query query);分页查询 - (19) Page page(Query query, Class clazz); 查询结果集,并转换结果集中的记录 - (20) Page page(Query query, Class clazz); 查询结果集,并转换结果集中的记录 - (21) Page page(Query query, Supplier target); 查询结果集,并转换结果集中的记录 - (22) Page page(Query query, Function converter); 查询结果集,并转换结果集中的记录,转换处理每一行 - (23) Page pageAndConvert(Query query, Function, List> converter); 查询结果集,并转换结果集中的记录,转换处理list - (24) Page page(Query query, Supplier target, Consumer format); 查询结果集,并转换结果集中的记录,并对记录进行额外处理 ##### Query用法: ``` Query query = new Query(); query.eq("id", id); query.eq("password",123456); // 支持链式拼接 ``` ``` userMapper.page( new Query() .page(pageNo,3) .force() // force忽略逻辑删除 .eq("password","123456") // 使用AND连接查询条件 .orEq("id",2) // 使用OR连接查询条件 .between("id",1,10)); ``` ## 四、代码生成 代码生成工具,基于 [enjoy](https://gitee.com/jfinal/enjoy) 开发;能够生成当前服务节点各层的代码。当然,DAO层是基于此组件的 详细介绍请参考本项目的codegen-springboot-mavenplugin模块 ## 五、实现原理 ![组件加载流程](./images/此组件载入流程.png "组件加载流程") ## 六、结合Spring:AbstractRoutingDataSource的分库,和分表实现 分库分表,大体有两种实现方式: - 代理 - 程序代码组件 使用代理的方式往往存在两方面的问题:延迟,数据Downling的中间态。而此两个缺陷是工业和互联网所不能接受的。 所以提供了本地程序代码分库分表的实现。 所有ORM框架面对的是对底层(数据库)的Java对象操作;所以,分库分表是***应用层***的事,只有应用层能确定如何拆分、计算拆分结果,使用代理等,或安置在ORM层反而是相对颠倒的架构思想;所以,依赖于Spring框架实现分库分表最好。 ![分库分表运行流程](./images/分库分表运行流程.png "分库分表运行流程") ##### 使用方式 1. SpringBoot配置文件 ``` spring: datasource: master: driver-class-name: com.p6spy.engine.spy.P6SpyDriver url: jdbc:p6spy:mariadb://172.29.53.183:3306/managend?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=GMT%2B8 username: cleangao password: 123456 slave: driver-class-name: com.p6spy.engine.spy.P6SpyDriver url: jdbc:p6spy:mariadb://172.29.53.183:3306/managend?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=GMT%2B8 username: cleangao password: 123456 ``` 使用如上方式配置多个数据源,名称任意;此处名称为masterslave 2. SpringBoot配置数据源导出 - (1) 禁用SpringBoot的数据源自动配置@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class}) - (2) 配置数据源导出 ``` import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Primary; import pub.cleangao.mybatis.springboot.DynamicDataSource; import java.util.HashMap; import java.util.Map; @Configuration public class DataSourceConfiguration { @Bean("ds1") @ConfigurationProperties("spring.datasource.master") public DataSourceProperties masterDataSourceProperties() { return new DataSourceProperties(); } @Bean("ds2") @ConfigurationProperties("spring.datasource.slave") public DataSourceProperties slaveDataSourceProperties() { return new DataSourceProperties(); } @Primary @Bean public DynamicDataSource routingDataSource(@Qualifier("ds1") DataSourceProperties masterDataSourceProperties, @Qualifier("ds2") DataSourceProperties slaveDataSourceProperties ) { DynamicDataSource routingDataSource = new DynamicDataSource(); routingDataSource.setDefaultTargetDataSource(masterDataSourceProperties.initializeDataSourceBuilder().build()); Map dataSourceMap = new HashMap<>(); dataSourceMap.put("master", masterDataSourceProperties.initializeDataSourceBuilder().build()); dataSourceMap.put("slave", slaveDataSourceProperties.initializeDataSourceBuilder().build()); routingDataSource.setTargetDataSources(dataSourceMap); return routingDataSource; } } ``` 上述代码中DynamicDataSource是此组件定义,继承实现的AbstractRoutingDataSource ``` // 以下名称, 例中master\slave是动态切换数据源的关键。可定义为其他名称 dataSourceMap.put("master", masterDataSourceProperties.initializeDataSourceBuilder().build()); dataSourceMap.put("slave", slaveDataSourceProperties.initializeDataSourceBuilder().build()); ``` 3. 使用此组件的注解 - @DS 用于任一方法,建议使用在Service层 - @UseCleavage 仅可用于MyBatis的Mapper接口的方法上;用于其他方法上未被处理 - @Table 用于实体类 - @Column(cleavageParamIndex = 0) 用于实体类中的字段上 为了计算和切换新的表名,平均多耗时了6ms; 单库单表,直接禁用分库分表即可(@Table(cleavage = false)。分库分表到此完成。