GitLub 卡顿无法正常使用请移步: https://gitlab.com/ck-open/jumper
- 多条件数据查询时需要大量的编写 eq、lt、ge、like、in 等方法设置条件并且每个条件基本需要判空。
- 简单查询通常会有controller -> service -> mapper。即使没有service 在controller中也会存在重复性代码。
- 微服务环境下,获取其他服务数据信息 feignClient -> controller -> service -> mapper 每种数据获取都需要一套完整流程代码。
- A服务查询B服务的查询逻辑写在B服务的查询接口中
- 每个服务都暴露了很多非本服务业务范围的查询接口
- 简化条件参数设置过程,将基本查询抽象出共性以公共方式实现。
- 将普通查询功能的controller、service层抽取出来,提供公共方式,直接通过mapper来实现查询。
- 微服务下调用,实现公共controller查询方式后可以将feignClient 进行封装提供。并注入到容器中,方便使用。
- 在公共controller中提供常用条件 eq、lt、ge、like、in、orderByAsc、orderByDesc等。
- 消灭服务暴露的非业务性查询接口
- 消灭连表查询编写的Mapper 或 xml配置
所有方法中的 条件对象 QueryDto、eq 或 like 都非必传 可以为 null
Integer current 查询页 Integer size 单页条数 IPage<R> page 分页对象 ConsumerConvert<T, R> convert 查询出的结果集PO 转 返回值Dto的实现函数 IService<R> service 实现Mybatis Plus IService<R> 接口的实现对象 QueryDto dto 统一查询条件对象 Object eq 等 条件对象 Object like 包含 条件对象 Object lt 小于 条件的参数对象 Object ge 大于等于 条件的参数对象 LambdaQueryWrapper<T> lambdaQueryWrapper LambdaQueryChainWrapper<T> lambdaQueryWrapper
- public static <T> QueryDto<T> buildQueryDto(T eq, T like)
- public static <T> IPage<T> buildIPage(Integer current, Integer size)
IPage<T> page = QueryUtil.buildIPage(1, 20);
- public static <T, R> IPage<T> buildIPage(IPage<R> page, ConsumerConvert<T, R> convert)
IPage<Dto> result = QueryUtil.buildIPage(page,PoToDtoConvert::PoToDto);或者
IPage<Dto> result = QueryUtil.buildIPage(page,i->{ Dto dto = new Dto(); dto.setXXX(i.getXXX()); return dto; });
- public static <T, R> IPage<T> queryPage(IService<R> service, QueryDto dto, ConsumerConvert<T, R> convert)
- public static <T, R> IPage<T> queryPage(BaseMapper<R> mapper, QueryDto dto, ConsumerConvert<T, R> convert)
- public static <T, R> List<T> query(IService<R> service, QueryDto dto, ConsumerConvert<T, R> convert)
- public static <T, R> List<T> query(BaseMapper<R> mapper, QueryDto dto, ConsumerConvert<T, R> convert)
// 返回分页 IPage<Dto> result = QueryUtil.queryPage(XXXDBService,query,i->Convert.convert(Dto.class,i)); // 或者 IPage<Dto> result = QueryUtil.queryPage(XXXDBService,query,i->{ Dto dto = new Dto(); dto.setXXX(i.getXXX()); return dto; }); // 返回列表 List<Dto> result = QueryUtil.query(XXXDBService,query,i->Convert.convert(Dto.class,i)); // 或者 List<Dto> result = QueryUtil.query(XXXDBService,query,i->{ Dto dto = new Dto(); dto.setXXX(i.getXXX()); return dto; });
- public static <R> IPage queryPage(IService<R> service, QueryDto dto)
- public static <R> IPage queryPage(BaseMapper<R> mapper, QueryDto dto)
- public static <R> List<R> query(IService<R> service, QueryDto dto)
- public static <R> List<R> query(BaseMapper<R> mapper, QueryDto dto)
// 返回分页 IPage<Po> result = QueryUtil.queryPage(XXXDBService,query); // 返回列表 List<Po> result = QueryUtil.query(XXXDBService,query);
- public static <T> LambdaQueryChainWrapper<T> setLike(LambdaQueryChainWrapper<T> lambdaQueryWrapper, Object like, Class<T> entityClass)
- public static <T> LambdaQueryWrapper<T> setLike(LambdaQueryWrapper<T> lambdaQueryWrapper, Object like, Class<T> entityClass)
// IService 用法 LambdaQueryChainWrapper<Po> queryChainWrapper = QueryUtil.setLike(XXXDBService.lambdaQuery(), query.getLike(), Po.class); IPage<Po> page = queryChainWrapper.page(QueryUtil.buildIPage(query.getCurrent(), query.getSize())); IPage<Dto> result = QueryUtil.buildIPage(page,PoToDtoConvert::PoToDto); // BaseMapper 用法 LambdaQueryChainWrapper<Po> queryWrapper = QueryUtil.setLike(new LambdaQueryWrapper<>(), query.getLike(), Po.class); IPage<Po> page = XXXMapper().selectPage(QueryUtil.buildIPage(query.getCurrent(), query.getSize()),queryWrapper); IPage<Dto> result = QueryUtil.buildIPage(page,PoToDtoConvert::PoToDto);
- public static <T> LambdaQueryChainWrapper<T> setEq(LambdaQueryWrapper<T> lambdaQueryWrapper, Object eq, Class<T> entityClass)
- public static <T> LambdaQueryWrapper<T> setEq(LambdaQueryWrapper<T> lambdaQueryWrapper, Object eq, Class<T> entityClass)
// IService 用法 LambdaQueryChainWrapper<Po> queryChainWrapper = QueryUtil.setEq(XXXDBService.lambdaQuery(), query.getEq(), Po.class); IPage<Po> page = queryChainWrapper.page(QueryUtil.buildIPage(query.getCurrent(), query.getSize())); IPage<Dto> result = QueryUtil.buildIPage(page,PoToDtoConvert::PoToDto); // BaseMapper 用法 LambdaQueryChainWrapper<Po> queryWrapper = QueryUtil.setEq(new LambdaQueryWrapper<>(), query.getEq(), Po.class); IPage<Po> page = XXXMapper().selectPage(QueryUtil.buildIPage(query.getCurrent(), query.getSize()),queryWrapper); IPage<Dto> result = QueryUtil.buildIPage(page,PoToDtoConvert::PoToDto);
- public static <T> LambdaQueryChainWrapper<T> setEqAndLike(LambdaQueryChainWrapper<T> lambdaQueryWrapper, Object eq, Object like, Class<T> entityClass)
- public static <T> LambdaQueryChainWrapper<T> setEqAndLike(IService<T> service, Object eq, Object like)
- public static <T> LambdaQueryWrapper<T> setEqAndLike(LambdaQueryWrapper<T> lambdaQueryWrapper, Object eq, Object like, Class<T> entityClass)
- public static <T> LambdaQueryWrapper<T> setEqAndLike(Object eq, Object like, Class<T> entityClass)
// IService 用法 LambdaQueryChainWrapper<Po> queryChainWrapper = QueryUtil.setEqAndLike(XXXDBService.lambdaQuery() , query.getEq(), query.getLike(), Po.class); IPage<Po> page = queryChainWrapper.page(QueryUtil.buildIPage(query.getCurrent(), query.getSize())); IPage<Dto> result = QueryUtil.buildIPage(page,PoToDtoConvert::PoToDto); LambdaQueryChainWrapper<Po> queryChainWrapper = QueryUtil.setEqAndLike(XXXDBService, query.getEq(), query.getLike()); IPage<Po> page = queryChainWrapper.page(QueryUtil.buildIPage(query.getCurrent(), query.getSize())); IPage<Dto> result = QueryUtil.buildIPage(page,PoToDtoConvert::PoToDto); // BaseMapper 用法 LambdaQueryWrapper<Po> queryWrapper = QueryUtil.setEqAndLike(new LambdaQueryWrapper<>() , query.getEq(), query.getLike(), Po.class); IPage<Po> page = XXXMapper().selectPage(QueryUtil.buildIPage(query.getCurrent(), query.getSize()),queryWrapper); IPage<Dto> result = QueryUtil.buildIPage(page,PoToDtoConvert::PoToDto); LambdaQueryWrapper<Po> queryWrapper = QueryUtil.setEqAndLike(query.getEq(), query.getLike(), Po.class); IPage<Po> page = XXXMapper().selectPage(QueryUtil.buildIPage(query.getCurrent(), query.getSize()),queryWrapper); IPage<Dto> result = QueryUtil.buildIPage(page,PoToDtoConvert::PoToDto);
- public static <T> LambdaQueryChainWrapper<T> setLtAndGe(LambdaQueryChainWrapper<T> lambdaQueryWrapper, Object lt, Object ge, Class<T> entityClass)
- public static <T> LambdaQueryWrapper<T> setLtAndGe(LambdaQueryWrapper<T> lambdaQueryWrapper, Object lt, Object ge, Class<T> entityClass)
// IService 用法 LambdaQueryChainWrapper<Po> queryChainWrapper = QueryUtil.setLtAndGe(XXXDBService.lambdaQuery() , query.getLt(), query.getGe(), Po.class); IPage<Po> page = queryChainWrapper.page(QueryUtil.buildIPage(query.getCurrent(), query.getSize())); IPage<Dto> result = QueryUtil.buildIPage(page,PoToDtoConvert::PoToDto); // BaseMapper 用法 LambdaQueryWrapper<Po> queryWrapper = QueryUtil.setLtAndGe(new LambdaQueryWrapper<>() , query.getLt(), query.getGe(), Po.class); IPage<Po> page = XXXMapper().selectPage(QueryUtil.buildIPage(query.getCurrent(), query.getSize()),queryWrapper); IPage<Dto> result = QueryUtil.buildIPage(page,PoToDtoConvert::PoToDto);一般用不到以下方法
- public static <T> LambdaQueryChainWrapper<T> setWrapper(LambdaQueryChainWrapper<T> lambdaQueryWrapper, QueryDto<?> dto, Class<T> entityClass)
- public static <T> LambdaQueryWrapper<T> setWrapper(LambdaQueryWrapper<T> lambdaQueryWrapper, QueryDto<?> dto, Class<T> entityClass)
- public static <T> LambdaQueryChainWrapper<T> setWrapper(LambdaQueryChainWrapper<T> lambdaQueryWrapper, Object eq, Object like, Object lt, Object ge, Class<T> entityClass)
- public static <T> LambdaQueryWrapper<T> setWrapper(LambdaQueryWrapper<T> lambdaQueryWrapper, Object eq, Object like, Object lt, Object ge, Class<T> entityClass)
// IService 用法 LambdaQueryChainWrapper<Po> queryChainWrapper = QueryUtil.setWrapper(XXXDBService.lambdaQuery() , query.getEq(), query.getLike(), query.getLt(), query.getGe(), Po.class); IPage<Po> page = queryChainWrapper.page(QueryUtil.buildIPage(query.getCurrent(), query.getSize())); IPage<Dto> result = QueryUtil.buildIPage(page,PoToDtoConvert::PoToDto); // BaseMapper 用法 LambdaQueryWrapper<Po> queryWrapper = new LambdaQueryWrapper<>(); QueryUtil.setWrapper(queryWrapper, query.getEq(), query.getLike(), query.getLt(), query.getGe(), Po.class); IPage<Po> page = XXXMapper().selectPage(QueryUtil.buildIPage(query.getCurrent(), query.getSize()),queryWrapper); IPage<Dto> result = QueryUtil.buildIPage(page,PoToDtoConvert::PoToDto);
- public static <T> void in(Map<String, List<?>> in, Class<?> entityClass, LambdaQueryChainWrapper<T> lambdaQueryWrapper)
- public static <T> void in(Map<String, List<?>> in, Class<?> entityClass, LambdaQueryWrapper<T> lambdaQueryWrapper)
- public static <T> void orderBy(List<String> order, boolean desc, Class<?> entityClass, LambdaQueryChainWrapper<T> lambdaQueryWrapper)
- public static <T> void orderBy(List<String> order, boolean desc, Class<?> entityClass, LambdaQueryWrapper<T> lambdaQueryWrapper)
@ApiModel(value = "统一查询条件报文体") @Data @Accessors(chain = true) public class QueryDto<T> { @ApiModelProperty(value = "分页-单页数量", dataType = "integer", allowableValues = "20") private Integer size = 20; @ApiModelProperty(value = "分页-页码", dataType = "integer", allowableValues = "1") private Integer current = 1; @ApiModelProperty(value = "等条件") private T eq; @ApiModelProperty(value = "包含条件") private T like; @ApiModelProperty(value = "小于条件") private T lt; @ApiModelProperty(value = "大于等于条件") private T ge; @ApiModelProperty(value = "其他条件") private T other; @ApiModelProperty(value = "in条件") private Map<String, List<?>> in; @ApiModelProperty(value = "排序字段-升序") private List<String> orderByAsc; @ApiModelProperty(value = "排序字段-降序") private List<String> orderByDesc; }{ "eq": { "name": "xxxName" }, "like": { "address": "xxx街道" }, "ge": { "createTimr": "2023-01-01 00:00:00" }, "lt": { "createTimr": "2023-01-02 00:00:00" }, "in": { "id": [125,52,51] }, "orderByDesc": [], "orderByAsc": [] }
- 获取 BaseMapper<T> 中T的类型
public static <T> Class<T> getMapperEntityClass(BaseMapper<T> mapper)
- 获取SFunction 函数方法所表述的属性名
public static <T> String lambdaFieldName(SFunction<T, ?> function)
- 构建查询对象
public static <T> QueryDto<T> buildQueryDto(T eq, T like)
- 据大于等于时间 加分量获取小于时间
例如:2023-01-01 00:00:00 小于时间为2023-01-02 00:00:00 用法:lt = getLtByGe(ge,"D",1); public static Date getLtByGe(Date ge, char unit, int val)
此工具主要简化 Mybatis Plus 基于 Lambda 的操作代码。
所以连表或自定义SQL查询 需要依赖于Mybatis Plus 创建
- 连表自定义查询示例
public interface CustomMapper extends BaseMapper<CustomPo>{ @Select("select a.name,a.age,a.address,b.username,b.phone" + " from UserDetail a left join UserAccount b" + " ${ew.customSqlSegment}") List<CustomPo> find(@Param("ew") Wrapper<CustomPo> wrapper); @Data @Accessors(chain = true) class CustomPo { @TableField("a.name") private String name; @TableField("a.age") private Integer age; @TableField("a.address") private String address; @TableField("b.username") private String username; @TableField("b.phone") private String phone; } }或者 (使用 queryPage 或 queryList 方法需要下面这样写)
public interface CustomMapper extends BaseMapper<CustomPo>{ @TableName(value = " UserDetail a left join UserAccount b ") @Data @Accessors(chain = true) class CustomPo { @TableField("a.name") private String name; @TableField("a.age") private Integer age; @TableField("a.address") private String address; @TableField("b.username") private String username; @TableField("b.phone") private String phone; } }
- 使用 QueryUtil 查询
// 分页 IPage<Dto> result = QueryUtil.queryPage(customMapper,query,i->Convert.convert(Dto.class,i)); // 列表 List<Dto> result = QueryUtil.query(customMapper,query,i->Convert.convert(Dto.class,i)); // 返回数据对象 List<Po> result = QueryUtil.query(customMapper,query); ...(方法操作与上述一直)
- 解决微服务间查询数据 创建查询接口和查询相关代码
- 基于上述工具类构建一个公共Controller 根据表名(BaseMapper注册的BeanName) 动态查询表数据的实现
- 请求方式:Post
- 实例: 127.0.0.1:8080/jumper/page/user 或 127.0.0.1:8080/jumper/list/user
- 路径最后一级为变量 表示 IOC 中的 BaseMapper Bean Name
- page与list 表示查询分页数据 和 例表数据
微服务间调用使用FeignClient 即可。 也可在有接口权鉴的环境下对外暴露,或页面查询用。
public interface ServiceFeign { @PostMapping(value = "/jumper/page/userInfo") FeignResult<IPage<UserInfoDto>> queryUserInfoPage(QueryUtil.QueryDto dto); @PostMapping(value = "/jumper/list/userInfo") FeignResult<List<UserInfoDto>> queryUserInfoList(QueryUtil.QueryDto dto); class ServiceFeignImpl implements ServiceFeign { @Override public FeignResult<IPage<UserInfoDto>> queryUserInfoPage(QueryUtil.QueryDto dto) { return null; } @Override public FeignResult<List<UserInfoDto>> queryUserInfoList(QueryUtil.QueryDto dto) { return null; } } }/** * @ClassName JumperQueryController * @Description 依赖与 QueryUtil 工具构建的 公共数据查询接口 * @Author Cyk * @Version 1.0 * @since 2023/3/15 16:53 **/ @RequestMapping("/jumper") @RestController public class JumperQueryController { @Resource private ApplicationContext applicationContext; @PostMapping("page/{mapperBeanName}") public <T> ResultBody<IPage<?>> queryPage(@PathVariable(value = "mapperBeanName") String mapperBeanName, @RequestBody(required = false) QueryUtil.QueryDto dto) { BaseMapper<T> mapper = getBaseMapper(mapperBeanName); if (mapper != null) { IPage<T> page = QueryUtil.queryPage(mapper, dto); ResultBody result = convertDto(mapper, iConvert -> ResultBody.ok(QueryUtil.buildIPage(page, iConvert))); if (result != null) { return result; } // 返回数据对象 return ResultBody.ok(page); } // 返回方法不存在提示 return ResultBody.build(ErrorCode.NOT_FOUND); } @PostMapping("list/{mapperBeanName}") public <T> ResultBody<List<?>> queryList(@PathVariable(value = "mapperBeanName") String mapperBeanName, @RequestBody(required = false) QueryUtil.QueryDto dto) { BaseMapper<T> mapper = getBaseMapper(mapperBeanName); if (mapper != null) { List<T> list = QueryUtil.query(mapper, dto); ResultBody result = convertDto(mapper, iConvert -> ResultBody.ok(QueryUtil.convertList(list, iConvert))); if (result != null) { return result; } // 返回数据对象 return ResultBody.ok(list); } // 返回方法不存在提示 return ResultBody.build(ErrorCode.NOT_FOUND); } /** * 转换 Dto 对象 * * @param mapper * @param function */ private <T> ResultBody<?> convertDto(BaseMapper<T> mapper, Function<QueryUtil.IConvert, ResultBody<?>> function) { Map<String, QueryUtil.IConvert> convertBeans = applicationContext.getBeansOfType(QueryUtil.IConvert.class); for (QueryUtil.IConvert convert : convertBeans.values()) { Method convertMethod = convert.getClass().getMethods()[0]; Class<?> poType = convertMethod.getParameterTypes()[0]; if (poType.isAssignableFrom(QueryUtil.getMapperEntityClass(mapper))) { // 返回转换Dto后的结果 return function.apply(convert); } } return null; } /** * 获取 BaseMapper<T> 实例 * * @param mapperBeanName * @param <T> * @return */ private <T> BaseMapper<T> getBaseMapper(String mapperBeanName) { try { if (!mapperBeanName.endsWith("Mapper")) mapperBeanName += "Mapper"; return applicationContext.getBean(mapperBeanName, BaseMapper.class); } catch (Exception ignored) { } return null; } }
- 动态 FeignClient 客户端 使用方式
@RestController public class Controller { @Resource private JumperFeign jumperFeign; @PostMapping("/query/data") public TResult<IPage<DataDTO>> query(@RequestBody QueryUtil.QueryDto<DataDTO> query) { TResult<IPage<DataDTO>> result = jumperFeign.getClient("serverId").queryPage("MapperBeanName",query); return callBackLogDTOS; } }
- 动态创建 FeignClient 实现代码
/** * 动态创建FeignClient 工具类 */ @Component public class FeignClientUtils implements ApplicationContextAware { private static ApplicationContext applicationContext = null; private static final Map<String, Object> BEAN_CACHE = new ConcurrentHashMap<>(); @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { if (FeignClientUtils.applicationContext == null) { FeignClientUtils.applicationContext = applicationContext; } } public static <T> T build(String serverName, String url, Class<T> targetClass) { return buildClient(serverName, url, targetClass, null); } public static <T> T build(String serverName, Class<T> targetClass) { return buildClient(serverName, null, targetClass, null); } public static <T> T build(String serverName, String url, Class<T> targetClass, Class<? extends T> fallback) { return buildClient(serverName, url, targetClass, fallback); } public static <T> T build(String serverName, Class<T> targetClass, Class<? extends T> fallback) { return buildClient(serverName, null, targetClass, fallback); } @SuppressWarnings("unchecked") private static <T> T buildClient(String serverName, String url, Class<T> targetClass, Class<? extends T> fallback) { T t = (T) BEAN_CACHE.get(serverName); if (Objects.isNull(t)) { FeignClientBuilder.Builder<T> builder = new FeignClientBuilder(applicationContext).forType(targetClass, serverName).url(url).fallback(fallback); t = builder.build(); BEAN_CACHE.put(serverName, t); } return t; } }
- 动态创建 JumperFeignClient 客户端 实现代码
public class JumperFeign { /** * 获取 JumperFeignClient * * @param serverName 服务名称(ServerId) * @return */ public JumperFeignClient getClient(String serverName) { return FeignClientUtils.build(serverName, JumperFeignClient.class, JumperFeignClientImpl.class); } /** * 获取 JumperFeignClient * * @param serverName 服务名称(ServerId) * @return */ public JumperFeignClient getClient(String serverName,String url) { return FeignClientUtils.build(serverName, url, JumperFeignClient.class, JumperFeignClientImpl.class); } public interface JumperFeignClient { @PostMapping("/jumper/page/{mapperBeanName}") <T> TResult<IPage<T>> queryPage(@PathVariable(value = "mapperBeanName") String mapperBeanName, @RequestBody(required = false) QueryUtil.QueryDto<?> dto); @PostMapping("/jumper/list/{mapperBeanName}") <T> TResult<List<T>> queryList(@PathVariable(value = "mapperBeanName") String mapperBeanName, @RequestBody(required = false) QueryUtil.QueryDto<?> dto); } public static class JumperFeignClientImpl implements JumperFeignClient { @Override public <T> TResult<IPage<T>> queryPage(String mapperBeanName, QueryUtil.QueryDto<?> dto) { return TResult.build(0, "服务不可用"); } @Override public <T> TResult<List<T>> queryList(String mapperBeanName, QueryUtil.QueryDto<?> dto) { return TResult.build(0, "服务不可用"); } } }
- 通过自定义Sql语句(不支持sql中有子查询)动态生成 继承自BaseMapper的 interface.class 文件以及 Po内部类。
- 提供 动态BaseMapper 在Spring 容器的注入、销毁和重载方法
- 动态生成的 BaseMapper 可直接使用公共查询接口进行查询
生成过程如下:
- 将 sql 语句解析成字符串表述的 XXXBaseMapper.java 源码文本字符串。
- 将 XXXBaseMapper.java 源码文本字符串 通过 JavaCompiler 编译成.class文件。
- 将编译后的.class文件写出的指定的项目目录下,并加载到JVM。
- 将 得到的 Class<?> 接口 加载到 SqlSessionTemplate.Configuration 的 Mapper 列表中。
- 根据加载的 Class<?> 接口 构建BaseMapper 默认实现对象,并加载到 Spring 容器。
- 此时就可以通过上面介绍的 JumperQueryController 公共查询接口进行相关查询操作了。
- 生成的源码文件不会文件保存,但会在日志中打印。
- Sql中不需要携带 where 条件,携带也将会被忽略,所有的查询条件都需要在使用时由使用者指定。
- Sql 中不可以使用 select * 查询,因为需要根据指定的参数构建Po对象。可以指定别名,不指定将自动驼峰。
- Sql 生成过程示例:
select uc.name,uc.password,c.customer_code,c.nick_name,c.card_type,c.card_id from user_credentials uc left join customer c on uc.customer_code=c.customer_codepackage jumper.db.mapper; import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableName; import org.apache.ibatis.annotations.Mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import lombok.Data; import java.io.Serializable; public interface UserInfoMapper extends BaseMapper<UserInfoMapper.UserInfo> { @Data @TableName(value = " user_credentials uc left join customer c on uc.customer_code=c.customer_code ") public static class UserInfo implements Serializable{ @TableField("uc.name") private Object name; @TableField("uc.password") private Object password; @TableField("c.customer_code") private Object customerCode; @TableField("c.nick_name") private Object nickName; @TableField("c.card_type") private Object cardType; @TableField("c.card_id") private Object cardId; } }继承 SqlCompileConfiguration Sql 编译配置类,重写父类方法实现构建过程介入。
/** * 参与 Sql 编译 BaseMapper java源码过程,自定义构建规则。重写父类方法实现 */ @Configuration public class MySqlCompileConfiguration extends SqlCompileConfiguration { }
- 向容器中注入 SqlCompileSupplier 接口实例。
- 在服务启动后会调用 SqlCompileSupplier接口的 getSql() 方法进行自动构建 BaseMapper 并注入容器。
- 示例:
@Configuration public class SqlCompile implements SqlCompileSupplier { /** * 需要编译的 Sql 列表 * key: beanName value: Sql语句 * * @return */ @Override public Map<String, String> getSql() { Map<String,String> sqlMap = new HashMap<>(); sqlMap.put("UserInfo","select uc.name,uc.password,c.customer_code,c.nick_name,c.card_type,c.card_id from user_credentials uc left join customer c on uc.customer_code=c.customer_code"); return sqlMap; } }以下接口已集成,可以参考在项目中自定义改造。接口默认不可用 需要在yml配置中添加开启参数 接口示例:
@Api(tags = "动态加载BaseMapper") @RequestMapping("/jumperSqlCompile") public class SqlCompileBaseMapperController { @Resource private SqlCompileBaseMapper sqlCompileBaseMapper; @ApiOperation(value = "Sql动态构建BaseMapper") @GetMapping("/registry") @ResponseBody public TResult<Boolean> registryMapper(@RequestParam(value = "className") String className, @RequestParam(value = "sql") String sql) { return TResult.ok(sqlCompileBaseMapper.registryBaseMapper(className, sql)); } @ApiOperation(value = "重置Sql动态构建BaseMapper") @GetMapping("/reset") @ResponseBody public TResult<Boolean> resetMapper(@RequestParam(value = "className") String className, @RequestParam(value = "sql") String sql) { return TResult.ok(sqlCompileBaseMapper.resetBaseMapper(className, sql)); } @ApiOperation(value = "卸载BaseMapper") @GetMapping("/destroy") @ResponseBody public TResult<Boolean> destroyMapper(@RequestParam(value = "className") String className) { if (!className.endsWith("Mapper")) { className += "Mapper"; } return TResult.ok(sqlCompileBaseMapper.destroyBaseMapper(className)); } }{ "properties": [ { "name": "jumper.enabled", "type": "java.lang.Boolean", "description": "开启 Jumper 支持." }, { "name": "jumper.SqlCompile.controller", "type": "java.lang.Boolean", "description": "开启 Sql 编译注入 BaseMapper 接口." },{ "name": "jumper.package_mapper", "type": "java.lang.String", "description": "保存生成的BaseMapper class文件的包地址" },{ "name": "jumper.DbType", "type": "java.lang.String", "description": "数据库类型 默认Mysql" } ] }不指定编译后的 class文件存储包路径,则默认使用 jumper.db.mapper,保存在项目下。 使用 jumper.db.mapper 包路径时,注意Sql日志打印问题,如果需要打印,则需要设置此路径的日志级别。 结构如下:
├── classes ├── com 项目代码包 ├── jumper ├── db ├── mapper ├── UserInfoMapper.class
由于项目依赖与 Spring Boot、Spring Web、MyBatis-Plus。
并且 未杜绝使用中版本冲突项目未打包相关依赖, 所以项目中需要使用这自行引入相关Jar<dependencies> <!-- Spring核心依赖 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <!-- WEB 服务依赖 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- Mybatis-plus包 --> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.4.1</version> </dependency> <!-- 微服务跨越查询(本功能集成jar)--> <dependency> <groupId>io.github.ck-open</groupId> <artifactId>jumper-starter</artifactId> <version>1.0.4</version> </dependency> <!-- MySql依赖包 --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.30</version> </dependency> </dependencies>使用Maven 中央公共库或下面暂存库
<!-- 中央仓库暂存库镜像 --> <mirror> <id>s01.oss.sonatype</id> <name>Sonatype Maven Central</name> <url>https://s01.oss.sonatype.org/content/groups/public/</url> <mirrorOf>central</mirrorOf> </mirror>jumper: enabled: true # 启用跨服务数据查询功能 package_mapper: com.ck.db.po # 根据Sql 动态提供查询时指定生成的.class存储路径 SqlCompile: controller: true # 开启 Sql 编译并加载 BaseMapper.class Http操作接口支持
- 编译接口
127.0.0.1:8080/jumperSqlCompile/registry?className=userInfo&sql=select uc.name,uc.password,c.nick_name,c.customer_code,c.card_type,c.card_id,c.address from customer c left join user_credentials uc on c.customer_code=uc.customer_code
- 重新编译接口
127.0.0.1:8080/jumperSqlCompile/reset?className=userInfo&sql=select uc.name,uc.password,c.nick_name,c.customer_code,c.card_type,c.card_id,c.address from customer c left join user_credentials uc on c.customer_code=uc.customer_code
- 卸载接口
127.0.0.1:8080/jumperSqlCompile/destroy?className=userInfo
- 查询编译的接口
具体操作方式参考上面 公共查询接口实现 介绍。
两个接口:分页查询、列表查询。- 请求方式:POST - 条件传参:Content-Type = application/json - 127.0.0.1:8080/jumper/page/userInfo - 127.0.0.1:8080/jumper/list/userInfo