# sqltomongo-spring-boot-starter **Repository Path**: listen_w/sqltomongo-spring-boot-starter ## Basic Information - **Project Name**: sqltomongo-spring-boot-starter - **Description**: 实现一个 用SQL查询 Mongo 数据库的工具 - **Primary Language**: Java - **License**: Apache-2.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 13 - **Forks**: 6 - **Created**: 2022-06-03 - **Last Updated**: 2025-01-14 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README 我报名参加金石计划1期挑战——瓜分10万奖池,这是我的第1篇文章,[点击查看活动详情](https://s.juejin.cn/ds/jooSN7t "https://s.juejin.cn/ds/jooSN7t")” ## 前言 之前我写过一个工具,就是通过 **“原生SQL去查Mongo数据”**,具体可以查看我的这篇博文:[听说你不会Mongo的API?我写个插件用SQL去查Mongo](https://juejin.cn/post/7104983592182218760) 写完这个工具后就没有再管它了,当时就是为了实现这样的功能,并没有考虑代码的优雅性,以及可扩展性。 后来我重新审视这个工具时,感觉这个代码乱糟糟的(也许以后看这个版本也是乱糟糟),所以我决定将这个工具重构一下。 最初的想法就是,增加这个工具的扩展性,因为我的这个工具有一些功能是没有支持的,可以交给使用者自己去扩展, 那这样的话,易扩展性就显得比较重要了。 说到扩展性,我觉得常用的就是使用模板方法设计模式,或者回调机制,把不变的部分做成模板,把灵活变动的地方交由用户自己实现,但是我在分析工具核心代码时,觉得这几种方式都不好。 > 注意:看这篇文章之前,请移步我写的另一篇文章《[听说你不会Mongo的API?我写个插件用SQL去查Mongo](https://juejin.cn/post/7104983592182218760)》 ## 代码 代码托管在 Gitee 上,代码地址:https://gitee.com/listen_w/sqltomongo-spring-boot-starter 现在 master 分支还是第一版本的代码,新的代码在这个 **restructure** 分支上 ![image.png](https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/9f7fc76e56924c63bb502b0470b10382~tplv-k3u1fbpfcp-watermark.image?) **Github 的代码地址** :https://github.com/jettwangcj/sqltomongo-spring-boot-starter ## 哪些地方需要扩展 其实在重构之前我一直在考虑需要重构什么?哪些点可以改进?后来决定以扩展性为主。 既然要扩展,就要考虑哪些地方留给用户扩展的口子,我这次主要留了三个地方,一个是**核心的SQL执行器(新增概念,Executor),第二个是SQL解析器(parser包下面),第三个是SQL分析器(新增概念,analyzer包下)** ## 这次重构变化有哪些 这次重构,主要借鉴了 **Mybatis** 的思想,尤其是拦截器机制,这个版本主要有这些变化: 1. SQL查询增加可配置缓存; 2. 支持注解配置SQL语句; 3. 增加配置核心类,**Configuration**; 4. 使用拦截器机制实现可扩展性; 5. 使用设计模式:代理模式、装饰者模式、责任链模式、建造者模式、适配器模式、观察者模式; ## 整体概览 1. 项目标注注解 **`EnableSqlToMongoMapper`** 的作用: ![image.png](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/eee24c2e0a0b4271b2f81a0bccdfeeb1~tplv-k3u1fbpfcp-watermark.image?) 由上可以知道,这个注解主要是导入了 **`SqlToMongoRegistrar`**,而这个类的作用就是为标注 **`SqlToMongoMapper`** 的接口创建代理类,最终是在核心配置类 **`Configuration`** 中注册代理对象,具体后面专门分析。 2. 项目启动后,根据springboot自动装配特性,会加载配置类 **`SqlToMongoAutoConfiguration`**,这个配置类会创建一些 Bean ![image.png](https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/01c68522a9694806b7ae24c150cf4588~tplv-k3u1fbpfcp-watermark.image?) 简单看一下各 Bean 的作用 - **`MongoTemplateProxy`** :继承 `MongoTemplate`,主要是重写 `doUpdate` 方法,当Mongo文档更新后在此方法发送更新缓存的通知; - **`SaveMongoEventListener`**:继承 `AbstractMongoEventListener`,当 Mongo 文档保存时发送更新缓存的通知; - **`InterceptorConfigurer`**:拦截器配置,默认实现是拦截器适配器:`InterceptorConfigurerAdapter`,它会添加一个拦截器模板: `InterceptorTemplate`; - **`Configuration`**:核心配置类,后面具体分析; - **`ClearCacheListener`**:继承 `ApplicationListener`,监听清除缓存事件通知; - **`SqlSession`**:发起 Mongo 查询请求,实际会通过 `Executor` 实现查询Mongo数据库 ; - **`SQLToMongoTemplate`**:方便 Mongo 查询的工具,通过 `SqlSession` 查询; 3. 一条SQL转Mongo语法过程 ![image.png](https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/db29b861c6734cbc902d69da28f4a5ef~tplv-k3u1fbpfcp-watermark.image?) ## 工程目录结构 ``` sqltomongo-spring-boot-starter └── src └── main └── java ├──com.rrtv │ ├── adapter │ │ └── MatchExpressionVisitorAdapter.java ------ 解析过滤匹配的 ExpressionVisitorAdapter │ ├── analyzer ------ SQL分析器,将SQL解析的元数据封装成 Mongo API │ │ ├── AbstractAnalyzer.java │ │ ├── Analyzer.java │ │ ├── GroupAnalyzer.java │ │ ├── HavingAnalyzer.java │ │ ├── JoinAnalyzer.java │ │ ├── LimitAnalyzer.java │ │ ├── MatchAnalyzer.java │ │ ├── ProjectAnalyzer.java │ │ └── SortAnalyzer.java │ ├── annotation │ │ ├── EnableSqlToMongoMapper.java ------ 启动类注解 │ │ ├── Intercepts.java ------ 插件注解 │ │ ├── Select.java ------ 查询注解 │ │ ├── Signature.java ------ 插件注解 │ │ └── SqlToMongoMapper.java ------ Mapper 接口类注解 │ ├── binding ------ 绑定,Mapper接口代理注册Bean │ │ ├── MapperAnnotationBuilder.java ------ Mapper注解解析,解析 Select 注解 │ │ ├── MapperProxy.java │ │ ├── MapperProxyFactory.java │ │ └── SqlToMongoMapperFactoryBean.java │ ├── cache ------ 缓存相关 │ │ ├── Cache.java │ │ ├── CacheManager.java ------ 缓存管理器 │ │ ├── ClearCacheEvent.java ------ 清除缓存事件 │ │ ├── ClearCacheListener.java ------ 清除缓存监听器 │ │ ├── ConcurrentHashMapCache.java │ │ ├── DefaultCacheManager.java │ │ ├── MongoTemplateProxy.java │ │ └── SaveMongoEventListener.java ------ Mongo 监听器 │ ├── common │ │ ├── AggregationFunction.java ------ 聚合函数枚举 │ │ ├── ConversionFunction.java ------ 转化函数枚举 │ │ ├── ParserPartTypeEnum.java │ │ └── MongoParserResult.java ------ SQL解析后封装Mongo API 结果 │ ├── configure │ │ ├── SqlToMongoAutoConfiguration.java ------ 自动配置 │ │ ├── SqlToMongoMapperFactoryBean.java ------ SqlToMongoMapper 工厂Bean │ │ └── SqlToMongoRegistrar.java ------ Mapper 接口 注册 │ ├── exception ------ 自定义异常 │ │ ├── BindingException.java │ │ ├── NotSupportFunctionException.java │ │ ├── NotSupportSubSelectException.java │ │ ├── PluginException.java │ │ ├── SqlParameterException.java │ │ ├── SqlParserException.java │ │ ├── SqlTypeException.java │ │ └── TableAssociationException.java │ ├── executor ------ 具体执行器 │ │ ├── CachingExecutor.java │ │ ├── DefaultExecutor.java │ │ └── Executor.java │ ├── orm │ │ ├── Configuration.java ------ 核心配置类,重点 │ │ ├── ConfigurationBuilder.java │ │ ├── DefaultSqlSession.java ------ SqlSession 实现类 │ │ ├── DomParser.java ------ Dom 解析 │ │ ├── SqlSession.java │ │ ├── SqlSessionBuilder.java │ │ └── XNode.java ------ xml 解析结果封装 │ ├── parser ------ SQL 解析 │ │ ├── data ------ SQL 各个部分解析结果 │ │ │ ├── GroupData.java │ │ │ ├── LimitData.java │ │ │ ├── LookUpData.java │ │ │ ├── MatchData.java │ │ │ ├── PartSQLParserData.java │ │ │ ├── PartSQLParserResult.java │ │ │ ├── ProjectData.java │ │ │ └── SortData.java │ │ ├── GroupSQLParser.java ------ 解析 SQL 分组 │ │ ├── HavingSQLParser.java ------ 解析 SQL Having │ │ ├── JoinSQLParser.java ------ 解析 SQL 表关联 │ │ ├── LimitSQLParser.java ------ 解析 SQL Limit │ │ ├── PartSQLParser.java ------ 解析 SQL Limit │ │ ├── OrderSQLParser.java ------ 解析 SQL 排序 │ │ ├── ProjectSQLParser.java ------ 解析 SQL 查询字段 │ │ ├── SelectSQLTypeParser.java ------ SQL 查询解析器,调用各个解析类解析SQL,并将元数据封装 Mongo 查询API │ │ └── WhereSQLParser.java ------ 解析 SQL where 条件 │ ├── plugin ------ 插件相关,用于扩展 │ │ ├── Interceptor.java ------ 拦截器接口 │ │ ├── InterceptorChain.java ------ 拦截器链,封装所有拦截器 │ │ ├── InterceptorConfigurer.java ------ 拦截器配置,用于自定义拦截器 │ │ ├── InterceptorConfigurerAdapter.java ------ 拦截器配置适配器,默认添加拦截器模板 │ │ ├── InterceptorTemplate.java ------ 拦截器模板 │ │ ├── Invocation.java │ │ └── Plugin.java ------ 插件具体逻辑 │ ├── util │ │ ├── SqlCommonUtil.java ------ SQL 公共 util │ │ ├── SqlParameterSetterUtil.java ------ SQL 设置参数 util │ │ ├── SqlSupportedSyntaxCheckUtil.java ------ SQL 支持语法检查 util │ │ └── StringUtils.java │ └── SQLToMongoTemplate.java ------ 用于Mongo 查询的 bean,使用者直接注入该 Bean └── resources └── META-INF └── spring.factories ``` 相对之前的最初版本,这里扩展了不少,有兴趣的可以去看看之前的那篇文章,这里主要加了 `plugin` 、`cache`、`executor`、`binding` 包,而且把原来SQL解析和Mongo元数据分析部分都单独拆了出来。 ## 模仿 **Mybatis**,新增 **Configuration** 配置类概念 `Configuration` 和 `Mybatis`中的`Configuration`思想一样,这里也是整个项目的核心配置类,上面我在介绍 **"整体概览"** 时讲到 `SqlToMongoAutoConfiguration`配置类,由那张图可以知道,`Configuration` 基本被所有的Bean依赖,所以说它是个粘合剂也不为过。 ![image.png](https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/01c68522a9694806b7ae24c150cf4588~tplv-k3u1fbpfcp-watermark.image?) `Configuration` 封装了一些最核心的配置,比如:缓存配置、Mapper代理、SQL解析映射、拦截器、SQL解析器、分析器等等,以下是 `Configuration` 的结构: ![image.png](https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/28a9eeef95bf48c4956a3d57d6adab7e~tplv-k3u1fbpfcp-watermark.image?) 简单介绍几个核心方法: - `getAnalyzerInstance`:创建 SQL 分析器责任链 (被拦截器链代理的),使用单利设计模式; - `getPartSQLParserInstance`:获取SQL各个部分解析器,具备缓存作用; - `newPartSQLParser`:创建SQL解析器(被拦截器链代理的); - `newExecutor`:创建执行器(缓存执行器 和 默认执行器),并对执行器增加拦截器处理; - `addInterceptor`:添加拦截器; - `getMapper`:获取Mapper代理对象; - `addMapper`:添加Mapper代理对象,并解析出 @Select 注解 ### 创建 `Configuration` 配置流程 `Configuration` Bean 会在springboot自动装配时创建(`SqlToMongoAutoConfiguration`配置类) ![image.png](https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/efa60bd2961a48d8ba5fefda37d406fc~tplv-k3u1fbpfcp-watermark.image?) 通过 `ConfigurationBuilder` 建造者设计模式创建 `Configuration` 对象,由下图可见,创建 `Configuration`时,主要有以下工作: - 解析XML文件,目前只是解析 Select注解,获取SQL语句 - 添加拦截器 - 设置缓存 - 设置缓存管理器 ![image.png](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/46e07b4faf284b168ec0f8944ec69772~tplv-k3u1fbpfcp-watermark.image?) `Configuration`作为最核心的配置,后面结合各部分功能一起说明。 ## 如何增加SQL注解配置功能 原先只支持 XML 解析SQL,类似MyBatis的Mapper.xml,原理就是根据Spring boot自动装配,创建Bean时扫描包路径,使用 Dom4j解析xml,解析出\