├── .gitignore ├── DYNAMIC_DATASOURCE_README.md ├── LICENSE ├── Mybatis-README.md ├── README.md ├── README_DATA_PERMISSION.md ├── README_SPRING_BOOT.md ├── mybatis-sqlhelper-spring-boot ├── images │ └── sqlhelperTip.png ├── mybatis-sqlhelper-spring-boot-autoconfigure │ ├── pom.xml │ └── src │ │ ├── main │ │ ├── java │ │ │ └── io │ │ │ │ └── github │ │ │ │ └── heykb │ │ │ │ └── sqlhelper │ │ │ │ └── autoconfigure │ │ │ │ ├── HasConflictAutoConfiguration.java │ │ │ │ ├── NoConflictAutoConfiguration.java │ │ │ │ ├── SqlHelperAutoConfiguration.java │ │ │ │ ├── SqlHelperLogicDeleteProperties.java │ │ │ │ ├── SqlHelperMultiTenantProperties.java │ │ │ │ └── SqlHelperProperties.java │ │ └── resources │ │ │ └── META-INF │ │ │ └── spring.factories │ │ └── test │ │ ├── java │ │ └── io │ │ │ └── github │ │ │ └── heykb │ │ │ └── sqlhelper │ │ │ └── primary │ │ │ └── AutoConfigurationTests.java │ │ └── resources │ │ └── application.yml ├── mybatis-sqlhelper-spring-boot-samples │ ├── mybatis-spring-boot-dynamic-datasource │ │ ├── format.xml │ │ ├── license.txt │ │ ├── pom.xml │ │ └── src │ │ │ └── main │ │ │ ├── java │ │ │ └── sample │ │ │ │ └── mybatis │ │ │ │ └── xml │ │ │ │ ├── Application.java │ │ │ │ ├── config │ │ │ │ └── SqlHelperManagerAutoConfiguration.java │ │ │ │ ├── domain │ │ │ │ ├── City.java │ │ │ │ └── Hotel.java │ │ │ │ ├── mapper │ │ │ │ ├── CityMapper.java │ │ │ │ └── HotelMapper.java │ │ │ │ ├── service │ │ │ │ ├── AnotherTestService.java │ │ │ │ ├── TestService.java │ │ │ │ └── impl │ │ │ │ │ ├── AnotherTestServiceImpl.java │ │ │ │ │ └── TestServiceImpl.java │ │ │ │ └── sqlhelper │ │ │ │ └── SimpleTenantInfoHandler.java │ │ │ └── resources │ │ │ ├── application.properties │ │ │ ├── application.yml │ │ │ ├── data.sql │ │ │ ├── mybatis-config.xml │ │ │ ├── sample │ │ │ └── mybatis │ │ │ │ └── xml │ │ │ │ └── mapper │ │ │ │ ├── CityMapper.xml │ │ │ │ └── HotelMapper.xml │ │ │ └── schema.sql │ ├── mybatis-spring-boot-sample-xml │ │ ├── format.xml │ │ ├── license.txt │ │ ├── pom.xml │ │ └── src │ │ │ └── main │ │ │ ├── java │ │ │ └── sample │ │ │ │ └── mybatis │ │ │ │ └── xml │ │ │ │ ├── SampleXmlApplication.java │ │ │ │ ├── domain │ │ │ │ ├── City.java │ │ │ │ └── Hotel.java │ │ │ │ ├── mapper │ │ │ │ ├── CityMapper.java │ │ │ │ └── HotelMapper.java │ │ │ │ ├── service │ │ │ │ ├── TestService.java │ │ │ │ └── impl │ │ │ │ │ └── TestServiceImpl.java │ │ │ │ └── sqlhelper │ │ │ │ └── SimpleTenantInfoHandler.java │ │ │ └── resources │ │ │ ├── application.properties │ │ │ ├── data.sql │ │ │ ├── mybatis-config.xml │ │ │ ├── sample │ │ │ └── mybatis │ │ │ │ └── xml │ │ │ │ └── mapper │ │ │ │ ├── CityMapper.xml │ │ │ │ └── HotelMapper.xml │ │ │ └── schema.sql │ └── pom.xml ├── mybatis-sqlhelper-spring-boot-starter │ └── pom.xml └── pom.xml ├── mybatis-sqlhelper-spring ├── README.md ├── pom.xml └── src │ ├── main │ └── java │ │ └── io │ │ └── github │ │ └── heykb │ │ └── sqlhelper │ │ └── spring │ │ ├── PropertyLogicDeleteInfoHandler.java │ │ ├── SqlHelperPluginFactoryBean.java │ │ └── dynamicds │ │ ├── SpringSqlHelperDsManager.java │ │ └── SqlHelperDynamicDataSourceProxy.java │ └── test │ ├── java │ └── io │ │ └── github │ │ └── heykb │ │ └── sqlhelper │ │ └── spring │ │ └── test │ │ ├── BaseDataUtils.java │ │ ├── PluginTests.java │ │ ├── applicationContext.xml │ │ ├── dao │ │ ├── EmployeeMapper.java │ │ ├── EmployeeMapper.xml │ │ ├── PeopleMapper.java │ │ └── PeopleMapper.xml │ │ ├── db │ │ ├── createDb.sql │ │ └── logicDeleteTest.sql │ │ ├── domain │ │ ├── Employee.java │ │ └── People.java │ │ └── handlers │ │ ├── SimpleColumnFilterHandler.java │ │ └── SimpleTenantInfoHandler.java │ └── resources │ └── log4j.properties ├── mybatis-sqlhelper ├── pom.xml └── src │ ├── main │ └── java │ │ └── io │ │ └── github │ │ └── heykb │ │ └── sqlhelper │ │ ├── config │ │ ├── SqlHelperAutoDbType.java │ │ └── SqlHelperException.java │ │ ├── dynamicdatasource │ │ ├── ConnectionSubspaceTypeEnum.java │ │ ├── DefaultSqlHelperDsManager.java │ │ ├── LogicDsMeta.java │ │ ├── PrimaryDatasource.java │ │ ├── SimpleProxyDatasource.java │ │ ├── SqlHelperDsContextHolder.java │ │ ├── SqlHelperDsManager.java │ │ ├── SqlHelperDynamicDataSourceProxy.java │ │ └── SupportedConnectionSubspaceChange.java │ │ ├── handler │ │ ├── ColumnFilterInfoHandler.java │ │ ├── InjectColumnInfoHandler.java │ │ ├── abstractor │ │ │ ├── BinaryConditionInjectInfoHandler.java │ │ │ ├── LogicDeleteInfoHandler.java │ │ │ └── TenantInfoHandler.java │ │ └── dynamic │ │ │ ├── AbstractDynamicFindColumnFilterHandler.java │ │ │ ├── AbstractDynamicFindInjectInfoHandler.java │ │ │ ├── DynamicFindColumnFilterHandler.java │ │ │ └── DynamicFindInjectInfoHandler.java │ │ ├── helper │ │ ├── MySchemaStatVisitor.java │ │ └── SqlStatementEditor.java │ │ ├── interceptor │ │ ├── ColumnFilterCursor.java │ │ ├── SimpleProxyCursor.java │ │ └── SqlHelperPlugin.java │ │ ├── typeHandler │ │ └── ColumnFilterTypeHandler.java │ │ └── utils │ │ ├── Asserts.java │ │ └── CommonUtils.java │ └── test │ └── java │ ├── io │ └── github │ │ └── heykb │ │ └── sqlhelper │ │ └── test │ │ ├── BaseTest.java │ │ ├── BaseUtils.java │ │ ├── CompareSqlTest.java │ │ ├── DynamicDatasourceTest.java │ │ ├── SqlInjectColunmnHelperTest.java │ │ ├── SqlTest.java │ │ ├── baseTest.sql │ │ └── testSql.xml │ └── log4j.properties ├── pom.xml └── sql-demo.md /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by .ignore support plugin (hsz.mobi) 2 | ### Java template 3 | # Compiled class file 4 | *.class 5 | 6 | # Log file 7 | *.log 8 | 9 | # BlueJ files 10 | *.ctxt 11 | 12 | # Mobile Tools for Java (J2ME) 13 | .mtj.tmp/ 14 | 15 | # Package Files # 16 | *.jar 17 | *.war 18 | *.nar 19 | *.ear 20 | *.zip 21 | *.tar.gz 22 | *.rar 23 | 24 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 25 | hs_err_pid* 26 | 27 | ### Example user template template 28 | ### Example user template 29 | 30 | # IntelliJ project files 31 | .idea 32 | *.iml 33 | out 34 | gen 35 | target/ 36 | sqlHelpDemo/ 37 | .flattened-pom.xml -------------------------------------------------------------------------------- /DYNAMIC_DATASOURCE_README.md: -------------------------------------------------------------------------------- 1 | # 多数据源专篇 2 | * 动态新增数据源,数据源管理能力 3 | * 动态切换数据源 4 | * 支持连接子空间,如mysql 一个连接支持切换不同数据库,可以配置不同的逻辑数据源名称(添加时设置不同的数据源名称,相同的数据源id)但是使用同一个数据源连接池,使用过程中我们会帮你自动切换到指定子空间 5 | 6 | 7 | ## 使用多数据源功能,配置SpringSqlHelperDsManager数据源管理bean即可开始使用。 8 | xml配置 9 | ~~~xml 10 | 11 | ~~~ 12 | spring boot配置 13 | ~~~java 14 | @Bean 15 | public SqlHelperDsManager springSqlHelperDsManager(){ 16 | return new SpringSqlHelperDsManager(); 17 | } 18 | ~~~ 19 | 然后在程序中使用SqlHelperDsManager新增添加数据源 20 | ~~~java 21 | @Autowired 22 | private SqlHelperDsManager sqlHelperDsManager; 23 | ~~~ 24 | ~~~java 25 | sqlHelperDsManager.put("mysql", LogicDsMeta.builder() 26 | .datasourceId("localhost:3306") 27 | .expectedSubspaceType(ConnectionSubspaceTypeEnum.DATABASE) 28 | .createFunc(s->new PooledDataSource("com.mysql.cj.jdbc.Driver", "jdbc:mysql://localhost:3306/test", "root", "123456")).build()); 29 | ~~~ 30 | LogicDsMeta新增数据源参数解释 31 | ~~~java 32 | /** 33 | * 数据源id,当已注册数据源中存在相同id时会复用数据源,并且会触发数据源升级回调方法如果存在的话。 34 | * 使用同一个数据源id的不同逻辑数据源可以设置不同的subspace子空间。如mysql支持同一个连接切换不同数据库 35 | */ 36 | private String datasourceId; 37 | /** 38 | * 设置期望的子空间类型(不是所有的数据库都支持同一个连接进行切换) 39 | * 主要作用在于当用户期望和软件支持不匹配能快速失败及时报错 40 | */ 41 | private ConnectionSubspaceTypeEnum expectedSubspaceType; 42 | /** 43 | * 当逻辑数据源使用时将连接切换到指定的子空间。子空间的名称。仅当数据库类型支持子空间时有效 44 | * nullable 45 | */ 46 | private String subspace; 47 | /** 48 | * 当数据源id不存在时,使用此回调创建新的数据源 49 | */ 50 | private Function createFunc; 51 | ~~~ 52 | 切换数据源 53 | ~~~java 54 | SqlHelperDsContextHolder.switchTo("mysql"); 55 | 56 | // 返回上一次设置 57 | SqlHelperDsContextHolder.backToLast(); 58 | 59 | // 清空切换栈,使用主数据源 60 | SqlHelperDsContextHolder.clear(); 61 | 62 | // 切换到主数据源 63 | SqlHelperDsContextHolder.switchTo(null) 64 | 65 | // 切换到指定数据源下运行,完毕后自动切换回来 66 | SqlHelperDsContextHolder.executeOn("mysql",()->{ 67 | // 执行逻辑 68 | return xxx; 69 | }) 70 | ~~~ 71 | 72 | ## 未完待续。。(如果你有兴趣,右上角watch该项目获得最新的动态) 73 | 74 | ## 参与贡献 75 | 76 | 如果你发现问题,提交issue。 77 | 78 | 如果你解决了问题,fork项目提交pull request。 79 | 80 | ## 联系我 81 | QQ: 1259609102
82 | email: 1259609102@qq.com,bigsheller08@gmail.com -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 heykb 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Mybatis-README.md: -------------------------------------------------------------------------------- 1 | # MyBatis原生配置使用方式 2 | 3 | [![Maven central](https://maven-badges.herokuapp.com/maven-central/io.github.heykb/mybatis-sqlHelper/badge.svg)](https://maven-badges.herokuapp.com/maven-central/io.github.heykb/mybatis-sqlHelper) 4 | 5 | 如果您正在使用MyBatis,并且您的项目需要多租户、逻辑删除和数据权限、多数据源的功能建议您使用,这一定是最方便的方式,使用它对您的现有代码没有侵入,您不需要对现有代码进行任何修改。 6 | 7 | ~~~xml 8 | 9 | io.github.heykb 10 | mybatis-sqlHelper 11 | ${project.version} 12 | 13 | ~~~ 14 | ## 使用快照版本(及时bug修复版本) 15 | ~~~xml 16 | 17 | io.github.heykb 18 | mybatis-sqlHelper 19 | 3.0.0.SR2-SNAPSHOT 20 | 21 | 22 | 23 | 24 | sonatype-nexus-snapshots 25 | Sonatype Nexus Snapshots 26 | https://s01.oss.sonatype.org/content/repositories/snapshots/ 27 | 28 | true 29 | 30 | 31 | false 32 | 33 | 34 | 35 | ~~~ 36 | 37 | ### CONDITION条件注入 38 | 条件注入, 创建类实现[InjectColumnInfoHandler](src/main/java/io/github/heykb/sqlhelper/handler/InjectColumnInfoHandler.java),如: 39 | ~~~java 40 | public class MyConditionInfoHandler implements InjectColumnInfoHandler { 41 | @Override 42 | public String getColumnName() { 43 | return "tenant_id"; 44 | } 45 | @Override 46 | public String getValue() { 47 | return "sqlhelper"; 48 | } 49 | @Override 50 | public String op() { 51 | return "="; 52 | } 53 | @Override 54 | public int getInjectTypes() { 55 | return CONDITION; 56 | } 57 | @Override 58 | public boolean checkCommandType(SqlCommandType commandType) { 59 | return true; 60 | } 61 | @Override 62 | public boolean checkTableName(String tableName) { 63 | return true; 64 | } 65 | 66 | @Override 67 | public boolean checkMapperId(String mapperId) { 68 | return true; 69 | } 70 | } 71 | ~~~ 72 | 配置插件 73 | ~~~xml 74 | 75 | 76 | 78 | 79 | 80 | ~~~ 81 | 82 | 83 | ## 可用的属性 84 | 85 | | 名称 | 类型 | 默认 | 描述 | demo | 86 | | ------------------------------ | -------------------------------- | ---- | ------------------------------------------------------------------ | ------------ | 87 | | enable | boolean | true | 用于设置功能的总开关。 | true | 88 | | multi-tenant.enable | boolean | true | 用于设置多租户功能开关。 | true | 89 | | logic-delete.enable | boolean | true | 用于设置物理删除转逻辑删除功能开关。 | true | 90 | | ~~dbType~~ | String(com.alibaba.druid.DbType) | | 用于设置数据库类型的参数名称,非特殊不用配置,支持自动获取 | mysql | 91 | | InjectColumnInfoHandler | String(逗号分割的字符串数组) | | sql注入信息全限定类名数组。被用于反射生成注入信息对象 | com.xx,io.xx | 92 | | ColumnFilterInfoHandler | String(逗号分割的字符串数组) | | 数据权限中的字段过滤信息全限定类名数组。被用于反射生成注入信息对象 | com.xx,io.xx | 93 | | DynamicFindInjectInfoHandler | String | | 运行期间动态生成注入信息对象集合的全限定类名。 | com.xx | 94 | | DynamicFindColumnFilterHandler | String | | 运行期间动态生成字段过滤信息对象集合的全限定类名。 | com.xx | 95 | 96 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # MyBatis 多租户、逻辑删除、数据权限插件、多数据源-SqlHelper(持续维护,放心使用) 2 | 3 | [![Maven central](https://maven-badges.herokuapp.com/maven-central/io.github.heykb/mybatis-sqlhelper-spring-boot-starter/badge.svg)](https://maven-badges.herokuapp.com/maven-central/io.github.heykb/mybatis-sqlHelper) 4 | 5 | 如果您正在使用MyBatis,并且您的项目需要多租户、逻辑删除和数据权限、多数据源的功能建议您使用,这一定是最方便的方式,使用它对您的现有代码没有侵入,您不需要对现有代码进行任何修改。 6 | 7 | 8 | ## 特点 9 | * 可实现多租户 10 | * 可实现真实删除转逻辑删除 11 | * 可实现行级别和列级别权限控制(自动解析注入) 12 | * 简单方便开箱即用的多数据源管理和切换 13 | * 可插拔 14 | * 简单方便,无压力 15 | * 高效率(基于[阿里 druid sql解析器](https://github.com/alibaba/druid/wiki/SQL-Parser)) 16 | * 将多租户、逻辑删除与应用程序解耦,随配随用 17 | * 强大的字段自动注入能力(查询条件注入/插入语句注入/更新语句注入/查询列过滤),定制其他业务逻辑 18 | * 支持多种数据库(基于阿里 druid sql解析器) 19 | 20 | 21 | ## spring 集成 22 | 1. [MyBatis SqlHelper Spring](./mybatis-sqlhelper-spring/README.md) 23 | 2. [MyBatis SqlHelper Spring Boot](./README_SPRING_BOOT.md) 24 | 25 | ### [查看博客戳这里 👆](https://heykb.github.io) 26 | 27 | ## 使用数据权限 28 | [数据权限专篇](./README_DATA_PERMISSION.md) 29 | 30 | ## 使用多数据源 31 | [多数据源专篇](./DYNAMIC_DATASOURCE_README.md) 32 | ## 注入示例 33 | [注入示例](./sql-demo.md) 34 | 35 | ## sql自动注入 36 | ### CONDITION条件注入 37 | 1. 单一条件注入, 创建类实现[InjectColumnInfoHandler](mybatis-sqlhelper/src/main/java/io/github/heykb/sqlhelper/handler/InjectColumnInfoHandler.java),如: 38 | ~~~java 39 | @Component 40 | public class MyConditionInfoHandler implements InjectColumnInfoHandler { 41 | @Override 42 | public String getColumnName() { 43 | return "tenant_id"; 44 | } 45 | @Override 46 | public String getValue() { 47 | return "sqlhelper"; 48 | } 49 | @Override 50 | public String op() { 51 | return "="; 52 | } 53 | @Override 54 | public int getInjectTypes() { 55 | return CONDITION; 56 | } 57 | @Override 58 | public boolean checkCommandType(SqlCommandType commandType) { 59 | return true; 60 | } 61 | @Override 62 | public boolean checkTableName(String tableName) { 63 | return true; 64 | } 65 | 66 | @Override 67 | public boolean checkMapperId(String mapperId) { 68 | return true; 69 | } 70 | } 71 | ~~~ 72 | 73 | ##### 查询语句中: 74 | ~~~java 75 | select * from user s 76 | ~~~ 77 | ##### 输出: 78 | ~~~sql 79 | SELECT * FROM user s WHERE s.tenant_id = 'sqlhelper' 80 | ~~~ 81 | ##### 更新语句中: 82 | ~~~java 83 | update user set name = ? where id = ? 84 | ~~~ 85 | ##### 输出: 86 | ~~~sql 87 | update user set name = ? where id = ? and user.tenant_id = 'sqlhelper' 88 | ~~~ 89 | ##### 删除语句中: 90 | ~~~java 91 | delete from user where id = ? 92 | ~~~ 93 | ##### 输出: 94 | ~~~sql 95 | delete from user where id = ? and user.tenant_id = 'sqlhelper' 96 | ~~~ 97 | ##### 外连接语句中: 98 | ~~~java 99 | SELECT * FROM user u left JOIN card c ON u.id = c.user_id 100 | ~~~ 101 | ##### 输出: 102 | ~~~sql 103 | SELECT * 104 | FROM user u 105 | LEFT JOIN card c 106 | ON u.id = c.user_id 107 | AND c.tenant_id = sqlhelper 108 | WHERE u.tenant_id = sqlhelper 109 | ~~~ 110 | ##### 各种子查询语句中: 111 | ~~~java 112 | SELECT * FROM (select * from user where id = 2) s 113 | ~~~ 114 | ##### 输出: 115 | ~~~sql 116 | SELECT * 117 | FROM ( 118 | (SELECT * 119 | FROM user 120 | WHERE id = 2 121 | AND user.tenant_id = sqlhelper) 122 | ) s 123 | ~~~ 124 | [查看更多测试示例](./sql-demo.md)
125 | 2. 多条件组合注入继承[BinaryConditionInjectInfoHandler](mybatis-sqlhelper/src/main/java/io/github/heykb/sqlhelper/handler/abstractor/BinaryConditionInjectInfoHandler.java) 126 | ```java 127 | /** 128 | * @author heykb 129 | * 注入: a = 1 and (b > 1 or c = 'x') 130 | */ 131 | @Component 132 | public class MyBinaryConditionInjectInfoHandler extends BinaryConditionInjectInfoHandler { 133 | 134 | @Override 135 | public String op() { 136 | return "and"; 137 | } 138 | 139 | @Override 140 | public InjectColumnInfoHandler getLeftConditionInjectInfo() { 141 | return new SimpleCondition("a","=","1"); 142 | } 143 | 144 | @Override 145 | public InjectColumnInfoHandler getRightConditionInjectInfo() { 146 | return new BinaryConditionInjectInfoHandler() { 147 | @Override 148 | public InjectColumnInfoHandler getLeftConditionInjectInfo() { 149 | return new SimpleCondition("b",">","1"); 150 | } 151 | 152 | @Override 153 | public String op() { 154 | return "or"; 155 | } 156 | 157 | @Override 158 | public InjectColumnInfoHandler getRightConditionInjectInfo() { 159 | return new SimpleCondition("c","=","'x'"); 160 | } 161 | }; 162 | } 163 | 164 | static class SimpleCondition implements InjectColumnInfoHandler{ 165 | String columnName; 166 | String op; 167 | String value; 168 | 169 | public SimpleCondition(String columnName, String op, String value) { 170 | this.columnName = columnName; 171 | this.op = op; 172 | this.value = value; 173 | } 174 | 175 | @Override 176 | public String getColumnName() { 177 | return columnName; 178 | } 179 | 180 | @Override 181 | public String op() { 182 | return op; 183 | } 184 | 185 | @Override 186 | public String getValue() { 187 | return value; 188 | } 189 | 190 | @Override 191 | public int getInjectTypes() { 192 | return 0; 193 | } 194 | } 195 | } 196 | ``` 197 | 198 | 199 | ### INSERT插入列注入 如自动插入租户id列 200 | 实现[InjectColumnInfoHandler](mybatis-sqlhelper/src/main/java/io/github/heykb/sqlhelper/handler/InjectColumnInfoHandler.java),如:
201 | ~~~java 202 | @Component 203 | public class MyInsertInfoHandler implements InjectColumnInfoHandler { 204 | 205 | @Override 206 | public String getColumnName() { 207 | return "tenant_id"; 208 | } 209 | @Override 210 | public String getValue() { 211 | return "'sqlhelper'"; 212 | } 213 | 214 | @Override 215 | public int getInjectTypes() { 216 | return INSERT; 217 | } 218 | @Override 219 | public boolean checkCommandType(SqlCommandType commandType) { 220 | return true; 221 | } 222 | @Override 223 | public boolean checkTableName(String tableName) { 224 | return true; 225 | } 226 | }; 227 | 228 | ~~~ 229 | #### 输入: 230 | ~~~sql 231 | INSERT INTO user (id, name) 232 | VALUES ('0', 'heykb') 233 | ~~~ 234 | #### 输出: 235 | ~~~sql 236 | INSERT INTO user (id, name, tenant_id) 237 | VALUES ('0', 'heykb', 'sqlhelper') 238 | ~~~ 239 | #### 输入: 240 | ~~~sql 241 | INSERT INTO user (id, name) 242 | SELECT g.id, g.name 243 | FROM user_group g 244 | WHERE id = 1 245 | ~~~ 246 | #### 输出: 247 | ~~~sql 248 | INSERT INTO user (id, name, tenant_id) 249 | SELECT g.id, g.name 250 | FROM user_group g 251 | WHERE id = 1 252 | ~~~ 253 | 254 | [查看更多测试示例](./sql-demo.md)
255 | 256 | ### UPDATE更新项注入...如自动更新updated_time列 257 | ~~~java 258 | @Override 259 | public int getInjectTypes() { 260 | return UPDATE; 261 | } 262 | ~~~ 263 | 264 | ### 同时多种类型注入 265 | ~~~java 266 | @Override 267 | public int getInjectTypes() { 268 | return UPDATE|INSERT|...; 269 | } 270 | ~~~ 271 | ## [查看更多测试示例](./sql-demo.md) 272 | 273 | 274 | ## 字段隔离的多租户(数据源隔离级别参考sqlhelper多数据源配置) 275 | ### 能帮你做什么? 276 | 1. 自动为所有where 、join on添加租户过滤条件 277 | 2. 自动为insert语句添加租户列的插入 278 | 3. 多租户的实现也是利用sqlhelper的自动注入功能,相当于配置了CONDITIO与INSERT的两种注入 279 | ### 创建注入类 280 | ~~~ 281 | 创建类继承[TenantInfoHandler](mybatis-sqlhelper/src/main/java/io/github/heykb/sqlhelper/handler/abstractor/TenantInfoHandler.java),如: 282 | ~~~java 283 | @Component 284 | public class SimpleTenantInfoHandler extends TenantInfoHandler { 285 | 286 | /** 287 | * 设置代表租户字段名称 288 | * @return 289 | */ 290 | @Override 291 | public String getTenantIdColumn() { 292 | return "tenant_id"; 293 | } 294 | 295 | /** 296 | * 当前租户value获取方式。通常从线程上下文中获取用户租户信息 297 | * @return 298 | */ 299 | @Override 300 | public String getTenantId() { 301 | // SecurityContextHolder.getContext().getAuthentication() 302 | return "sqlhelper"; 303 | } 304 | 305 | } 306 | ~~~ 307 | 308 | 309 | 310 | ## 物理删除切换逻辑删除 311 | ### 能帮你做什么? 312 | 对于删除sql自动转逻辑删除,查询和更新sql添加逻辑删除条件保证不会查询到已经删除的数据,支持多表删除等复杂情况,详情可以查看 [更多测试示例](./sql-demo.md)
313 | 314 | 创建类继承[LogicDeleteInfoHandler](mybatis-sqlhelper/src/main/java/io/github/heykb/sqlhelper/handler/abstractor/LogicDeleteInfoHandler.java) 315 | ~~~java 316 | @Component 317 | public class SimpleLogicDeleteInfoHandler extends LogicDeleteInfoHandler { 318 | // 主要是为了从中提取SET is_deleted = 'Y' 319 | @Override 320 | public String getDeleteSqlDemo() { 321 | return "UPDATE xx SET is_deleted = 'Y'"; 322 | } 323 | 324 | @Override 325 | public String getNotDeletedValue() { 326 | return "'N'"; 327 | } 328 | 329 | @Override 330 | public String getColumnName() { 331 | return "is_deleted"; 332 | } 333 | 334 | @Override 335 | public boolean checkMapperId(String mapperId) { 336 | return !mapperId.contains("noPlugin"); 337 | } 338 | } 339 | ~~~ 340 | ### 3.观察日志。 341 | 物理删除语句已经被自动转换成更新语句,并且保留了所有where条件。查询和更新也自动添加了过滤条件 342 | 343 | 344 | ## 未完待续。。(如果你有兴趣,右上角watch该项目获得最新的动态) 345 | 346 | ## 参与贡献 347 | 348 | 如果你发现问题,提交issue。 349 | 350 | 如果你解决了问题,fork项目提交pull request。 351 | 352 | ## 联系我 353 | QQ群: 947964874
354 | email: 1259609102@qq.com,bigsheller08@gmail.com 355 | -------------------------------------------------------------------------------- /README_DATA_PERMISSION.md: -------------------------------------------------------------------------------- 1 | # MyBatis SqlHelper 数据权限专篇 2 | > 此功能建立在mybatis sqlhepler sql自动注入功能基础上 3 | ## 数据权限 4 | 数据权限是指对系统用户进行数据资源可见性的控制。权限控制粒度达到行和列级别。
5 | MyBatis SqlHelper实现了底层最重要的过滤逻辑,借助MyBatis SqlHelper您可以封装您自己的业务层逻辑实现数据权限管理 6 | ### 特点 7 | * 完整:支持行级别和列级别的sql注入,不仅能为查询语句控制字段还能为更新语句控制字段,支持各种多表复杂sql 8 | * 智能:所有的注入通过sql解析器智能操作,无须编码修改现有sql 9 | * 通用:适配所有MyBatis项目,包括像mybatis-plus,tk-mybatis等;支持多种数据库(基于阿里 druid sql解析器) 10 | * 方便:依托于spring boot ,代码集成方便 11 | 12 | ## MyBatis SqlHelper实现 13 | 1. 行级别的控制方式:利用[阿里 druid sql解析器](https://github.com/alibaba/druid/wiki/SQL-Parser)解析查询语句注入sql条件
14 | 2. 列级别的控制方式:对查询结果集过滤,有```修改查询sql```和```java代码层结果集过滤```两种实现方式,MyBatis SqlHelper会分情况自动选择这两个方式,通常不包含select *列这种的查询都支持修改sql的方式 15 | 16 | ## 使用范例 17 | ### 行级别控制 18 | #### 输入: 19 | ~~~sql 20 | SELECT created_by, dept_id, importand_data, data, tenant_id, id 21 | FROM test 22 | LIMIT ? OFFSET ? 23 | ~~~ 24 | #### 输出: 25 | ~~~sql 26 | SELECT created_by, dept_id, importand_data, data, tenant_id , id 27 | FROM test 28 | WHERE created_by = 'zrc' 29 | LIMIT ? OFFSET ? 30 | ~~~ 31 | ### 行控制通过slqhelper的CONDITION类型自动注入实现 32 | 1. 单条件注入:继承[ConditionInjectInfoHandler](mybatis-sqlhelper/src/main/java/io/github/heykb/sqlhelper/handler/abstractor/ConditionInjectInfoHandler.java)类,重写以下必要方法 33 | ~~~java 34 | @Component 35 | public class SimpleConditionInjectInfoHandler extends ConditionInjectInfoHandler { 36 | 37 | /** 38 | * 条件左值(实体属性名称或者表列名) 39 | * @return 40 | */ 41 | @Override 42 | public String getColumnName() { 43 | return "createdBy"; 44 | } 45 | 46 | /** 47 | * 条件右值(sql能识别的都可以包括子查询语句) 48 | * @return 49 | */ 50 | @Override 51 | public String getValue() { 52 | return "zrc"; 53 | } 54 | 55 | /** 56 | * 操作符(sql能识别的都可以) 57 | * @return 58 | */ 59 | @Override 60 | public String op(){ 61 | return "="; 62 | } 63 | 64 | /** 65 | * 设置表级别过滤逻辑 66 | * 67 | * @param tableName the table name 68 | * @return boolean 69 | */ 70 | public boolean checkTableName(String tableName){ 71 | return "example".equals(tableName); 72 | } 73 | 74 | /** 75 | * 设置mapperId方法级别过滤逻辑 76 | * 77 | * @param mapperId the mapper id 78 | * @return boolean 79 | */ 80 | public boolean checkMapperId(String mapperId){ 81 | return true; 82 | } 83 | } 84 | ~~~ 85 | 2. 多条件注入:继承[BinaryConditionInjectInfoHandler](mybatis-sqlhelper/src/main/java/io/github/heykb/sqlhelper/handler/abstractor/BinaryConditionInjectInfoHandler.java)类... 86 | 87 | 88 | ### 列级别控制 89 | #### 输入: 90 | ~~~sql 91 | SELECT created_by, dept_id, important_data, data, tenant_id, id 92 | FROM test 93 | LIMIT ? OFFSET ? 94 | ~~~ 95 | #### 输出: 96 | ~~~sql 97 | SELECT created_by, dept_id, NULL AS important_data, data, tenant_id, id 98 | FROM test 99 | LIMIT ? OFFSET ? 100 | ~~~ 101 | 102 | #### 输入: 103 | ~~~sql 104 | SELECT important_data, a, name 105 | FROM tb1 106 | UNION 107 | SELECT important_data, b, name 108 | ~~~ 109 | #### 输出: 110 | ~~~sql 111 | SELECT NULL AS important_data, a, name 112 | FROM tb1 113 | UNION 114 | SELECT NULL AS important_data, b, name 115 | FROM tb2 116 | ~~~ 117 | 118 | 119 | #### 输入: 120 | ~~~sql 121 | SELECT important_data 122 | FROM ( 123 | SELECT important_data 124 | FROM a 125 | UNION ALL 126 | SELECT important_data 127 | FROM b 128 | ) 129 | ~~~ 130 | #### 输出: 131 | ~~~sql 132 | SELECT important_data 133 | FROM ( 134 | SELECT NULL AS important_data 135 | FROM a 136 | UNION ALL 137 | SELECT NULL AS important_data 138 | FROM b 139 | ) 140 | ~~~ 141 | 142 | #### 输入: 143 | ~~~sql 144 | update test set important_data = ?,created_by=? 145 | ~~~ 146 | #### 输出: 147 | ~~~sql 148 | UPDATE test 149 | SET created_by = ? 150 | // 我们也会删除mybatis中的参数 151 | ~~~ 152 | ### 使用方式 153 | 配置: 154 | ~~~yml 155 | sqlhelper: 156 | # sql:通过注入sql过滤列,要求原sql查询不能使用select *,所有查询列必须明确写出来 157 | # result:通过修改结果集过滤列 158 | # smarter: 优先使用注入sql过滤列,注入sql不适用时使用result的方式 159 | columnFilterType: smarter #默认 160 | ~~~ 161 | 实现[ColumnFilterInfoHandler](mybatis-sqlhelper/src/main/java/io/github/heykb/sqlhelper/handler/ColumnFilterInfoHandler.java)类 162 | ~~~java 163 | @Component 164 | public class SimpleColumnFilterInfoHandler extends ColumnFilterInfoHandler { 165 | 166 | @Override 167 | public Set getFilterColumns() { 168 | return Sets.newHashSet("important_data"); 169 | } 170 | 171 | @Override 172 | public boolean checkTableName(String tableName) { 173 | return "test".equals(tableName); 174 | } 175 | } 176 | ~~~ 177 | 178 | 179 | 180 | ### 程序运行启动动态分配注入 181 | 以上方式都是程序启动就分配好的(权限)注入,不适用数据权限管理为不同用户分配不同的权限。
182 | MyBatis SqlHelper提供了[DynamicFindInjectInfoHandler](mybatis-sqlhelper/src/main/java/io/github/heykb/sqlhelper/handler/dynamic/DynamicFindInjectInfoHandler.java)和[DynamicFindColumnFilterHandler](mybatis-sqlhelper/src/main/java/io/github/heykb/sqlhelper/handler/dynamic/DynamicFindColumnFilterHandler.java),您可以实现他们并编写```根据用户权限构造handler列表返回```逻辑。 183 | ## 未完待续。。(留下你的start鼓励我完善它) 184 | ## 参与贡献 185 | fork项目提交pull request。如果您不会请学习它,它很有用 186 | ## 联系我 187 | QQ群: 947964874
188 | email: 1259609102@qq.com,bigsheller08@gmail.com 189 | 190 | 191 | 192 | 193 | -------------------------------------------------------------------------------- /README_SPRING_BOOT.md: -------------------------------------------------------------------------------- 1 | # MyBatis SqlHelper Spring Boot 2 | 3 | [![Maven central](https://maven-badges.herokuapp.com/maven-central/io.github.heykb/mybatis-sqlhelper-spring-boot-starter/badge.svg)](https://maven-badges.herokuapp.com/maven-central/io.github.heykb/mybatis-sqlhelper-spring-boot-starter) 4 | 5 | [MyBatis SqlHelper](https://github.com/heykb/mybatis-sqlhelper)的spring boot集成版本,自动配置插件、从容器中自动扫描注入信息添加到插件。 6 | 7 | ~~~xml 8 | 9 | io.github.heykb 10 | mybatis-sqlhelper-spring-boot-starter 11 | 3.0.0.SR1 12 | 13 | ~~~ 14 | 15 | ## IDEA提示 16 | ![属性提示](images/sqlhelperTip.png) 17 | 18 | ## 将创建的注入信息类生成bean注入到容器即可生效。 19 | ~~~java 20 | @Component 21 | public class InjectUpdateByHandler implements InjectColumnInfoHandler { 22 | @Override 23 | public String getColumnName() { 24 | return "updated_by"; 25 | } 26 | 27 | @Override 28 | public String getValue() { 29 | return "'zrc'"; 30 | } 31 | 32 | @Override 33 | public int getInjectTypes() { 34 | return INSERT|UPDATE; 35 | } 36 | 37 | @Override 38 | public boolean checkTableName(String tableName) { 39 | return tableName.equals("people") || tableName.equals("dept_data_test"); 40 | } 41 | } 42 | ~~~ 43 | 44 | 与直接使用[MyBatis SqlHelper](https://github.com/heykb/mybatis-sqlhelper)与[MyBatis SqlHelper Spring](https://github.com/heykb/mybatis-sqlhelper-spring)不同,逻辑删除的功能默认是关闭的。你可通过application.properties配置逻辑删除的信息。自定义LogicDeleteInfoHanlder的bean后,环境变量配置失效。 45 | 46 | ## 未完待续。。(如果你有兴趣,右上角watch该项目获得最新的动态) 47 | 48 | ## 参与贡献 49 | 50 | 如果你发现问题,提交issue。 51 | 52 | 如果你解决了问题,fork项目提交pull request。 53 | 54 | ## 联系我 55 | QQ: 1259609102
56 | email: 1259609102@qq.com,bigsheller08@gmail.com -------------------------------------------------------------------------------- /mybatis-sqlhelper-spring-boot/images/sqlhelperTip.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/heykb/mybatis-sqlhelper/7d13c29f11fe450f3a11e9d8f91841d4d8eed508/mybatis-sqlhelper-spring-boot/images/sqlhelperTip.png -------------------------------------------------------------------------------- /mybatis-sqlhelper-spring-boot/mybatis-sqlhelper-spring-boot-autoconfigure/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | mybatis-sqlhelper-spring-boot 7 | io.github.heykb 8 | ${revision} 9 | 10 | 4.0.0 11 | 12 | mybatis-sqlhelper-spring-boot-autoconfigure 13 | 14 | 15 | 16 | 17 | org.springframework.boot 18 | spring-boot-autoconfigure 19 | 20 | 21 | 22 | 23 | org.mybatis 24 | mybatis 25 | true 26 | 27 | 28 | io.github.heykb 29 | mybatis-sqlhelper-spring 30 | true 31 | 32 | 33 | org.mybatis.spring.boot 34 | mybatis-spring-boot-autoconfigure 35 | true 36 | 37 | 38 | org.projectlombok 39 | lombok 40 | provided 41 | 42 | 43 | 44 | 45 | org.springframework.boot 46 | spring-boot-configuration-processor 47 | true 48 | 49 | 50 | 51 | 52 | 53 | org.junit.jupiter 54 | junit-jupiter 55 | 5.8.1 56 | test 57 | 58 | 59 | org.springframework.boot 60 | spring-boot-test 61 | test 62 | 63 | 64 | 65 | -------------------------------------------------------------------------------- /mybatis-sqlhelper-spring-boot/mybatis-sqlhelper-spring-boot-autoconfigure/src/main/java/io/github/heykb/sqlhelper/autoconfigure/HasConflictAutoConfiguration.java: -------------------------------------------------------------------------------- 1 | package io.github.heykb.sqlhelper.autoconfigure; 2 | 3 | 4 | import org.springframework.boot.autoconfigure.AutoConfigureBefore; 5 | import org.springframework.boot.autoconfigure.ImportAutoConfiguration; 6 | import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; 7 | import org.springframework.context.annotation.Configuration; 8 | 9 | /** 10 | * @author heykb 11 | */ 12 | @Deprecated 13 | @Configuration 14 | @ConditionalOnClass(name="com.github.pagehelper.autoconfigure.PageHelperAutoConfiguration") 15 | @AutoConfigureBefore(name = "com.github.pagehelper.autoconfigure.PageHelperAutoConfiguration") 16 | @ImportAutoConfiguration(SqlHelperAutoConfiguration.class) 17 | public class HasConflictAutoConfiguration { 18 | 19 | } 20 | -------------------------------------------------------------------------------- /mybatis-sqlhelper-spring-boot/mybatis-sqlhelper-spring-boot-autoconfigure/src/main/java/io/github/heykb/sqlhelper/autoconfigure/NoConflictAutoConfiguration.java: -------------------------------------------------------------------------------- 1 | package io.github.heykb.sqlhelper.autoconfigure; 2 | 3 | import org.springframework.boot.autoconfigure.ImportAutoConfiguration; 4 | import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass; 5 | 6 | /** 7 | * @author heykb 8 | */ 9 | @Deprecated 10 | @ConditionalOnMissingClass("com.github.pagehelper.autoconfigure.PageHelperAutoConfiguration") 11 | @ImportAutoConfiguration(SqlHelperAutoConfiguration.class) 12 | public class NoConflictAutoConfiguration { 13 | 14 | 15 | } 16 | -------------------------------------------------------------------------------- /mybatis-sqlhelper-spring-boot/mybatis-sqlhelper-spring-boot-autoconfigure/src/main/java/io/github/heykb/sqlhelper/autoconfigure/SqlHelperAutoConfiguration.java: -------------------------------------------------------------------------------- 1 | package io.github.heykb.sqlhelper.autoconfigure; 2 | 3 | import io.github.heykb.sqlhelper.handler.abstractor.LogicDeleteInfoHandler; 4 | import io.github.heykb.sqlhelper.spring.PropertyLogicDeleteInfoHandler; 5 | import io.github.heykb.sqlhelper.spring.SqlHelperPluginFactoryBean; 6 | import org.springframework.boot.autoconfigure.AutoConfigureAfter; 7 | import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; 8 | import org.springframework.boot.context.properties.EnableConfigurationProperties; 9 | import org.springframework.context.annotation.Bean; 10 | import org.springframework.context.annotation.Configuration; 11 | 12 | @Configuration 13 | @EnableConfigurationProperties(SqlHelperProperties.class) 14 | 15 | public class SqlHelperAutoConfiguration { 16 | 17 | private final SqlHelperProperties properties; 18 | 19 | public SqlHelperAutoConfiguration(SqlHelperProperties properties) { 20 | this.properties = properties; 21 | } 22 | 23 | @Bean 24 | @ConditionalOnMissingBean(LogicDeleteInfoHandler.class) 25 | PropertyLogicDeleteInfoHandler logicDeleteInfoHandler(){ 26 | return properties.getLogicDelete(); 27 | } 28 | 29 | @Bean 30 | SqlHelperPluginFactoryBean sqlHelperPluginFactoryBean(){ 31 | SqlHelperPluginFactoryBean factoryBean = new SqlHelperPluginFactoryBean(); 32 | factoryBean.setProperties(properties.getProperties()); 33 | return factoryBean; 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /mybatis-sqlhelper-spring-boot/mybatis-sqlhelper-spring-boot-autoconfigure/src/main/java/io/github/heykb/sqlhelper/autoconfigure/SqlHelperLogicDeleteProperties.java: -------------------------------------------------------------------------------- 1 | package io.github.heykb.sqlhelper.autoconfigure; 2 | 3 | import io.github.heykb.sqlhelper.spring.PropertyLogicDeleteInfoHandler; 4 | 5 | 6 | public class SqlHelperLogicDeleteProperties extends PropertyLogicDeleteInfoHandler { 7 | /** 8 | * Physical delete to logical delete switch 9 | */ 10 | private boolean enable = false; 11 | 12 | public boolean isEnable() { 13 | return enable; 14 | } 15 | 16 | public void setEnable(boolean enable) { 17 | this.enable = enable; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /mybatis-sqlhelper-spring-boot/mybatis-sqlhelper-spring-boot-autoconfigure/src/main/java/io/github/heykb/sqlhelper/autoconfigure/SqlHelperMultiTenantProperties.java: -------------------------------------------------------------------------------- 1 | package io.github.heykb.sqlhelper.autoconfigure; 2 | 3 | import lombok.Data; 4 | 5 | @Data 6 | public class SqlHelperMultiTenantProperties { 7 | /** 8 | * Multi-tenant feature switch 9 | */ 10 | private boolean enable = true; 11 | } 12 | -------------------------------------------------------------------------------- /mybatis-sqlhelper-spring-boot/mybatis-sqlhelper-spring-boot-autoconfigure/src/main/java/io/github/heykb/sqlhelper/autoconfigure/SqlHelperProperties.java: -------------------------------------------------------------------------------- 1 | package io.github.heykb.sqlhelper.autoconfigure; 2 | 3 | import com.alibaba.druid.DbType; 4 | import io.github.heykb.sqlhelper.interceptor.SqlHelperPlugin; 5 | import org.springframework.boot.context.properties.ConfigurationProperties; 6 | import org.springframework.boot.context.properties.NestedConfigurationProperty; 7 | 8 | import java.util.Properties; 9 | 10 | @ConfigurationProperties(prefix = SqlHelperProperties.SQLHELPER_PREFIX) 11 | public class SqlHelperProperties { 12 | public static final String SQLHELPER_PREFIX = "sqlhelper"; 13 | /** 14 | * Master switch 15 | */ 16 | private boolean enable = true; 17 | /** 18 | * database type.Support to automatically obtain the type according to the Datasource 19 | */ 20 | @Deprecated 21 | private DbType dbType; 22 | @NestedConfigurationProperty 23 | private SqlHelperLogicDeleteProperties logicDelete = new SqlHelperLogicDeleteProperties(); 24 | @NestedConfigurationProperty 25 | private SqlHelperMultiTenantProperties multiTenant = new SqlHelperMultiTenantProperties(); 26 | 27 | 28 | private Properties properties = new Properties(); 29 | 30 | 31 | public SqlHelperProperties() { 32 | properties.setProperty(SqlHelperPlugin.enableProp,String.valueOf(enable)); 33 | properties.setProperty(SqlHelperPlugin.logicDeleteEnableProp,String.valueOf(logicDelete.isEnable())); 34 | properties.setProperty(SqlHelperPlugin.multiTenantEnableProp,String.valueOf(multiTenant.isEnable())); 35 | 36 | } 37 | 38 | public Properties getProperties() { 39 | return properties; 40 | } 41 | 42 | public boolean isEnable() { 43 | return enable; 44 | } 45 | 46 | public void setEnable(boolean enable) { 47 | this.enable = enable; 48 | properties.setProperty(SqlHelperPlugin.enableProp,String.valueOf(enable)); 49 | } 50 | 51 | @Deprecated 52 | public DbType getDbType() { 53 | return dbType; 54 | } 55 | 56 | @Deprecated 57 | public void setDbType(DbType dbType) { 58 | this.dbType = dbType; 59 | properties.setProperty(SqlHelperPlugin.dbTypeProp,dbType.name()); 60 | } 61 | 62 | 63 | public SqlHelperLogicDeleteProperties getLogicDelete() { 64 | return logicDelete; 65 | } 66 | 67 | public void setLogicDelete(SqlHelperLogicDeleteProperties logicDelete) { 68 | this.logicDelete = logicDelete; 69 | properties.setProperty(SqlHelperPlugin.logicDeleteEnableProp,String.valueOf(logicDelete.isEnable())); 70 | } 71 | 72 | public SqlHelperMultiTenantProperties getMultiTenant() { 73 | return multiTenant; 74 | } 75 | 76 | public void setMultiTenant(SqlHelperMultiTenantProperties multiTenant) { 77 | this.multiTenant = multiTenant; 78 | properties.setProperty(SqlHelperPlugin.multiTenantEnableProp,String.valueOf(multiTenant.isEnable())); 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /mybatis-sqlhelper-spring-boot/mybatis-sqlhelper-spring-boot-autoconfigure/src/main/resources/META-INF/spring.factories: -------------------------------------------------------------------------------- 1 | org.springframework.boot.autoconfigure.EnableAutoConfiguration=io.github.heykb.sqlhelper.autoconfigure.SqlHelperAutoConfiguration -------------------------------------------------------------------------------- /mybatis-sqlhelper-spring-boot/mybatis-sqlhelper-spring-boot-autoconfigure/src/test/java/io/github/heykb/sqlhelper/primary/AutoConfigurationTests.java: -------------------------------------------------------------------------------- 1 | package io.github.heykb.sqlhelper.primary; 2 | 3 | import io.github.heykb.sqlhelper.autoconfigure.SqlHelperAutoConfiguration; 4 | import io.github.heykb.sqlhelper.autoconfigure.SqlHelperProperties; 5 | import io.github.heykb.sqlhelper.handler.abstractor.LogicDeleteInfoHandler; 6 | import io.github.heykb.sqlhelper.interceptor.SqlHelperPlugin; 7 | import io.github.heykb.sqlhelper.spring.PropertyLogicDeleteInfoHandler; 8 | import org.junit.jupiter.api.AfterEach; 9 | import org.junit.jupiter.api.BeforeEach; 10 | import org.junit.jupiter.api.Test; 11 | import org.springframework.context.annotation.AnnotationConfigApplicationContext; 12 | 13 | import static org.junit.jupiter.api.Assertions.*; 14 | 15 | public class AutoConfigurationTests { 16 | 17 | private AnnotationConfigApplicationContext context; 18 | @BeforeEach 19 | void init() { 20 | this.context = new AnnotationConfigApplicationContext(); 21 | 22 | } 23 | @AfterEach 24 | void closeContext() { 25 | if (this.context != null) { 26 | this.context.close(); 27 | } 28 | } 29 | 30 | @Test 31 | void testDefaultProp(){ 32 | this.context.register(SqlHelperAutoConfiguration.class); 33 | this.context.refresh(); 34 | SqlHelperProperties sqlHelperProperties = this.context.getBean(SqlHelperProperties.class); 35 | assertEquals(sqlHelperProperties.getProperties().getProperty(SqlHelperPlugin.enableProp),"true"); 36 | assertEquals(sqlHelperProperties.getProperties().getProperty(SqlHelperPlugin.logicDeleteEnableProp),"false"); 37 | assertEquals(sqlHelperProperties.getProperties().getProperty(SqlHelperPlugin.multiTenantEnableProp),"true"); 38 | assertNull(sqlHelperProperties.getProperties().getProperty(SqlHelperPlugin.dbTypeProp)); 39 | assertNull(sqlHelperProperties.getProperties().getProperty(SqlHelperPlugin.injectColumnInfoHandlersProp)); 40 | assertNull(sqlHelperProperties.getProperties().getProperty(SqlHelperPlugin.columnFilterInfoHandlersProp)); 41 | assertNull(sqlHelperProperties.getProperties().getProperty(SqlHelperPlugin.dynamicFindColumnFilterHandlerProp)); 42 | assertNull(sqlHelperProperties.getProperties().getProperty(SqlHelperPlugin.dynamicFindInjectInfoHandlerProp)); 43 | } 44 | 45 | @Test 46 | void testPropertyLogicDeleteInfoHandler(){ 47 | this.context.register(SqlHelperAutoConfiguration.class); 48 | this.context.refresh(); 49 | LogicDeleteInfoHandler logicDeleteInfoHandler = this.context.getBean(LogicDeleteInfoHandler.class); 50 | assertTrue(logicDeleteInfoHandler instanceof PropertyLogicDeleteInfoHandler); 51 | assertEquals(logicDeleteInfoHandler.getNotDeletedValue(),"'N'"); 52 | assertEquals(logicDeleteInfoHandler.getDeleteSqlDemo(),"UPDATE a SET is_deleted = 'Y'"); 53 | assertEquals(logicDeleteInfoHandler.getColumnName(),"is_deleted"); 54 | } 55 | 56 | @Test 57 | void testSqlHelperPlugin(){ 58 | this.context.register(SqlHelperAutoConfiguration.class); 59 | this.context.refresh(); 60 | SqlHelperPlugin sqlHelperPlugin = this.context.getBean(SqlHelperPlugin.class); 61 | LogicDeleteInfoHandler logicDeleteInfoHandler = this.context.getBean(LogicDeleteInfoHandler.class); 62 | assertEquals(sqlHelperPlugin.getInjectColumnInfoHandlers().size(),1); 63 | assertEquals(logicDeleteInfoHandler,sqlHelperPlugin.getInjectColumnInfoHandlers().iterator().next()); 64 | 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /mybatis-sqlhelper-spring-boot/mybatis-sqlhelper-spring-boot-autoconfigure/src/test/resources/application.yml: -------------------------------------------------------------------------------- 1 | sqlhelper: 2 | logic-delete: 3 | column-name: is_deleted 4 | sql-demo: update xx set is_deleted = 'Y' 5 | not-deleted-value: 'N' 6 | enable: true 7 | properties: 8 | ss: xx 9 | -------------------------------------------------------------------------------- /mybatis-sqlhelper-spring-boot/mybatis-sqlhelper-spring-boot-samples/mybatis-spring-boot-dynamic-datasource/format.xml: -------------------------------------------------------------------------------- 1 | 2 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /mybatis-sqlhelper-spring-boot/mybatis-sqlhelper-spring-boot-samples/mybatis-spring-boot-dynamic-datasource/license.txt: -------------------------------------------------------------------------------- 1 | Copyright ${license.git.copyrightYears} the original author or authors. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | -------------------------------------------------------------------------------- /mybatis-sqlhelper-spring-boot/mybatis-sqlhelper-spring-boot-samples/mybatis-spring-boot-dynamic-datasource/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | 5 | mybatis-sqlhelper-spring-boot-samples 6 | io.github.heykb 7 | ${revision} 8 | 9 | mybatis-spring-boot-dynamic-datasource 10 | jar 11 | mybatis-spring-boot-dynamic-datasource 12 | 13 | 14 | 15 | io.github.heykb 16 | mybatis-sqlhelper-spring-boot-starter 17 | ${project.version} 18 | 19 | 20 | com.h2database 21 | h2 22 | runtime 23 | 24 | 25 | mysql 26 | mysql-connector-java 27 | 8.0.27 28 | runtime 29 | 30 | 31 | 32 | org.springframework.boot 33 | spring-boot-starter-test 34 | test 35 | 36 | 37 | 38 | 39 | 40 | org.springframework.boot 41 | spring-boot-maven-plugin 42 | ${spring-boot.version} 43 | 44 | 45 | 46 | repackage 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /mybatis-sqlhelper-spring-boot/mybatis-sqlhelper-spring-boot-samples/mybatis-spring-boot-dynamic-datasource/src/main/java/sample/mybatis/xml/Application.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015-2021 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package sample.mybatis.xml; 17 | 18 | 19 | import io.github.heykb.sqlhelper.dynamicdatasource.ConnectionSubspaceTypeEnum; 20 | import io.github.heykb.sqlhelper.dynamicdatasource.LogicDsMeta; 21 | import io.github.heykb.sqlhelper.dynamicdatasource.SqlHelperDsManager; 22 | import org.apache.ibatis.datasource.pooled.PooledDataSource; 23 | import org.springframework.boot.CommandLineRunner; 24 | import org.springframework.boot.SpringApplication; 25 | import org.springframework.boot.autoconfigure.SpringBootApplication; 26 | import sample.mybatis.xml.service.TestService; 27 | 28 | @SpringBootApplication 29 | public class Application implements CommandLineRunner { 30 | 31 | public static void main(String[] args) { 32 | SpringApplication.run(Application.class, args); 33 | } 34 | 35 | private final TestService testService; 36 | 37 | 38 | public Application(TestService testService, SqlHelperDsManager sqlHelperDsManager) { 39 | this.testService = testService; 40 | sqlHelperDsManager.put("mysql", LogicDsMeta.builder() 41 | .datasourceId("localhost:3306") 42 | .expectedSubspaceType(ConnectionSubspaceTypeEnum.DATABASE) 43 | .createFunc(()->new PooledDataSource("com.mysql.cj.jdbc.Driver", "jdbc:mysql://localhost:3306/test", "root", "123456")).build()); 44 | } 45 | 46 | @Override 47 | @SuppressWarnings("squid:S106") 48 | public void run(String... args) { 49 | System.out.println(this.testService.findByState("CA")); 50 | } 51 | 52 | } 53 | -------------------------------------------------------------------------------- /mybatis-sqlhelper-spring-boot/mybatis-sqlhelper-spring-boot-samples/mybatis-spring-boot-dynamic-datasource/src/main/java/sample/mybatis/xml/config/SqlHelperManagerAutoConfiguration.java: -------------------------------------------------------------------------------- 1 | package sample.mybatis.xml.config; 2 | 3 | import io.github.heykb.sqlhelper.spring.dynamicds.SpringSqlHelperDsManager; 4 | import org.springframework.context.annotation.Bean; 5 | import org.springframework.context.annotation.Configuration; 6 | 7 | @Configuration 8 | public class SqlHelperManagerAutoConfiguration { 9 | 10 | @Bean 11 | public SpringSqlHelperDsManager springSqlHelperDsManager(){ 12 | return new SpringSqlHelperDsManager(); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /mybatis-sqlhelper-spring-boot/mybatis-sqlhelper-spring-boot-samples/mybatis-spring-boot-dynamic-datasource/src/main/java/sample/mybatis/xml/domain/City.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015-2021 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package sample.mybatis.xml.domain; 17 | 18 | import java.io.Serializable; 19 | 20 | /** 21 | * @author Eddú Meléndez 22 | */ 23 | public class City implements Serializable { 24 | 25 | private static final long serialVersionUID = 1L; 26 | 27 | private Long id; 28 | 29 | private String name; 30 | 31 | private String state; 32 | 33 | private String country; 34 | 35 | public Long getId() { 36 | return this.id; 37 | } 38 | 39 | public void setId(Long id) { 40 | this.id = id; 41 | } 42 | 43 | public String getName() { 44 | return this.name; 45 | } 46 | 47 | public void setName(String name) { 48 | this.name = name; 49 | } 50 | 51 | public String getState() { 52 | return this.state; 53 | } 54 | 55 | public void setState(String state) { 56 | this.state = state; 57 | } 58 | 59 | public String getCountry() { 60 | return this.country; 61 | } 62 | 63 | public void setCountry(String country) { 64 | this.country = country; 65 | } 66 | 67 | @Override 68 | public String toString() { 69 | return getId() + "," + getName() + "," + getState() + "," + getCountry(); 70 | } 71 | 72 | } 73 | -------------------------------------------------------------------------------- /mybatis-sqlhelper-spring-boot/mybatis-sqlhelper-spring-boot-samples/mybatis-spring-boot-dynamic-datasource/src/main/java/sample/mybatis/xml/domain/Hotel.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015-2021 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | /** 17 | * @author Eduardo Macarron 18 | */ 19 | package sample.mybatis.xml.domain; 20 | 21 | import java.io.Serializable; 22 | 23 | public class Hotel implements Serializable { 24 | 25 | private static final long serialVersionUID = 1L; 26 | 27 | private Long city; 28 | 29 | private String name; 30 | 31 | private String address; 32 | 33 | private String zip; 34 | 35 | public Long getCity() { 36 | return city; 37 | } 38 | 39 | public void setCity(Long city) { 40 | this.city = city; 41 | } 42 | 43 | public String getName() { 44 | return name; 45 | } 46 | 47 | public void setName(String name) { 48 | this.name = name; 49 | } 50 | 51 | public String getAddress() { 52 | return address; 53 | } 54 | 55 | public void setAddress(String address) { 56 | this.address = address; 57 | } 58 | 59 | public String getZip() { 60 | return zip; 61 | } 62 | 63 | public void setZip(String zip) { 64 | this.zip = zip; 65 | } 66 | 67 | @Override 68 | public String toString() { 69 | return getCity() + "," + getName() + "," + getAddress() + "," + getZip(); 70 | } 71 | 72 | } -------------------------------------------------------------------------------- /mybatis-sqlhelper-spring-boot/mybatis-sqlhelper-spring-boot-samples/mybatis-spring-boot-dynamic-datasource/src/main/java/sample/mybatis/xml/mapper/CityMapper.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015-2021 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package sample.mybatis.xml.mapper; 17 | 18 | import org.apache.ibatis.annotations.Mapper; 19 | import org.apache.ibatis.annotations.Param; 20 | import org.apache.ibatis.annotations.Select; 21 | import sample.mybatis.xml.domain.City; 22 | 23 | /** 24 | * @author Eddú Meléndez 25 | */ 26 | @Mapper 27 | public interface CityMapper { 28 | 29 | @Select("select id, name, state, country from city where state = #{state}") 30 | City findByState(@Param("state") String state); 31 | 32 | @Select("select id, name, state, country from city where state = #{state}") 33 | City findByState2(@Param("state") String state); 34 | 35 | 36 | } 37 | -------------------------------------------------------------------------------- /mybatis-sqlhelper-spring-boot/mybatis-sqlhelper-spring-boot-samples/mybatis-spring-boot-dynamic-datasource/src/main/java/sample/mybatis/xml/mapper/HotelMapper.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015-2021 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package sample.mybatis.xml.mapper; 17 | 18 | import org.apache.ibatis.annotations.Mapper; 19 | import sample.mybatis.xml.domain.Hotel; 20 | 21 | /** 22 | * @author Eduardo Macarron 23 | */ 24 | @Mapper 25 | public interface HotelMapper { 26 | 27 | Hotel selectByCityId(int cityId); 28 | 29 | } 30 | -------------------------------------------------------------------------------- /mybatis-sqlhelper-spring-boot/mybatis-sqlhelper-spring-boot-samples/mybatis-spring-boot-dynamic-datasource/src/main/java/sample/mybatis/xml/service/AnotherTestService.java: -------------------------------------------------------------------------------- 1 | package sample.mybatis.xml.service; 2 | 3 | import sample.mybatis.xml.domain.City; 4 | 5 | public interface AnotherTestService { 6 | City findByState(String state); 7 | } 8 | -------------------------------------------------------------------------------- /mybatis-sqlhelper-spring-boot/mybatis-sqlhelper-spring-boot-samples/mybatis-spring-boot-dynamic-datasource/src/main/java/sample/mybatis/xml/service/TestService.java: -------------------------------------------------------------------------------- 1 | package sample.mybatis.xml.service; 2 | 3 | import sample.mybatis.xml.domain.City; 4 | 5 | public interface TestService { 6 | City findByState(String state); 7 | } 8 | -------------------------------------------------------------------------------- /mybatis-sqlhelper-spring-boot/mybatis-sqlhelper-spring-boot-samples/mybatis-spring-boot-dynamic-datasource/src/main/java/sample/mybatis/xml/service/impl/AnotherTestServiceImpl.java: -------------------------------------------------------------------------------- 1 | package sample.mybatis.xml.service.impl; 2 | 3 | import org.springframework.beans.factory.annotation.Autowired; 4 | import org.springframework.stereotype.Service; 5 | import org.springframework.transaction.annotation.Propagation; 6 | import org.springframework.transaction.annotation.Transactional; 7 | import sample.mybatis.xml.domain.City; 8 | import sample.mybatis.xml.mapper.CityMapper; 9 | import sample.mybatis.xml.service.AnotherTestService; 10 | 11 | @Service 12 | public class AnotherTestServiceImpl implements AnotherTestService { 13 | 14 | @Autowired 15 | private CityMapper cityMapper; 16 | 17 | @Override 18 | @Transactional(propagation = Propagation.SUPPORTS) 19 | public City findByState(String state) { 20 | City city = cityMapper.findByState2(state); 21 | return city; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /mybatis-sqlhelper-spring-boot/mybatis-sqlhelper-spring-boot-samples/mybatis-spring-boot-dynamic-datasource/src/main/java/sample/mybatis/xml/service/impl/TestServiceImpl.java: -------------------------------------------------------------------------------- 1 | package sample.mybatis.xml.service.impl; 2 | 3 | import io.github.heykb.sqlhelper.dynamicdatasource.SqlHelperDsContextHolder; 4 | import org.springframework.beans.factory.annotation.Autowired; 5 | import org.springframework.stereotype.Service; 6 | import org.springframework.transaction.annotation.Transactional; 7 | import sample.mybatis.xml.domain.City; 8 | import sample.mybatis.xml.mapper.CityMapper; 9 | import sample.mybatis.xml.service.AnotherTestService; 10 | import sample.mybatis.xml.service.TestService; 11 | 12 | @Service 13 | public class TestServiceImpl implements TestService { 14 | 15 | @Autowired 16 | private CityMapper cityMapper; 17 | 18 | @Autowired 19 | private AnotherTestService anotherTestService; 20 | 21 | @Override 22 | @Transactional 23 | public City findByState(String state) { 24 | City city = cityMapper.findByState(state); 25 | SqlHelperDsContextHolder.switchTo("mysql"); 26 | city = anotherTestService.findByState(state); 27 | SqlHelperDsContextHolder.clear(); 28 | return city; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /mybatis-sqlhelper-spring-boot/mybatis-sqlhelper-spring-boot-samples/mybatis-spring-boot-dynamic-datasource/src/main/java/sample/mybatis/xml/sqlhelper/SimpleTenantInfoHandler.java: -------------------------------------------------------------------------------- 1 | package sample.mybatis.xml.sqlhelper; 2 | 3 | 4 | import io.github.heykb.sqlhelper.handler.abstractor.TenantInfoHandler; 5 | 6 | /** 7 | * @author heykb 8 | */ 9 | //@Component 10 | public class SimpleTenantInfoHandler extends TenantInfoHandler { 11 | public static final String TENANT_ID = "1"; 12 | @Override 13 | public String getTenantIdColumn() { 14 | return "tenant_id"; 15 | } 16 | @Override 17 | public String getTenantId() { 18 | // 可以从 19 | // SecurityContextHolder.getContext().getAuthentication() 20 | return "'"+TENANT_ID+"'"; 21 | } 22 | @Override 23 | public boolean checkTableName(String tableName) { 24 | return "city".equals(tableName); 25 | } 26 | 27 | @Override 28 | public boolean checkMapperId(String mapperId) { 29 | return !mapperId.contains("noPluginSelect"); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /mybatis-sqlhelper-spring-boot/mybatis-sqlhelper-spring-boot-samples/mybatis-spring-boot-dynamic-datasource/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | mybatis.config-location=classpath:mybatis-config.xml 2 | logging.level.root=WARN 3 | logging.level.sample.mybatis.xml.mapper=TRACE 4 | -------------------------------------------------------------------------------- /mybatis-sqlhelper-spring-boot/mybatis-sqlhelper-spring-boot-samples/mybatis-spring-boot-dynamic-datasource/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | sqlhelper: 2 | logic-delete: 3 | enable: true 4 | column-name: is_deleted 5 | not-deleted-value: "'N'" 6 | deleteSqlDemo: update xx set is_deleted = 'Y' 7 | ignoreMapperIds: '**.noPlugin*' 8 | ignoreTables: xx,xxx 9 | -------------------------------------------------------------------------------- /mybatis-sqlhelper-spring-boot/mybatis-sqlhelper-spring-boot-samples/mybatis-spring-boot-dynamic-datasource/src/main/resources/data.sql: -------------------------------------------------------------------------------- 1 | 2 | insert into city (name, state, country) values ('San Francisco', 'CA', 'US'); 3 | insert into hotel(city, name, address, zip) values (1, 'Conrad Treasury Place', 'William & George Streets', '4001') 4 | -------------------------------------------------------------------------------- /mybatis-sqlhelper-spring-boot/mybatis-sqlhelper-spring-boot-samples/mybatis-spring-boot-dynamic-datasource/src/main/resources/mybatis-config.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /mybatis-sqlhelper-spring-boot/mybatis-sqlhelper-spring-boot-samples/mybatis-spring-boot-dynamic-datasource/src/main/resources/sample/mybatis/xml/mapper/CityMapper.xml: -------------------------------------------------------------------------------- 1 | 2 | 19 | 22 | 23 | 26 | 27 | -------------------------------------------------------------------------------- /mybatis-sqlhelper-spring-boot/mybatis-sqlhelper-spring-boot-samples/mybatis-spring-boot-dynamic-datasource/src/main/resources/sample/mybatis/xml/mapper/HotelMapper.xml: -------------------------------------------------------------------------------- 1 | 2 | 19 | 22 | 23 | 26 | 27 | -------------------------------------------------------------------------------- /mybatis-sqlhelper-spring-boot/mybatis-sqlhelper-spring-boot-samples/mybatis-spring-boot-dynamic-datasource/src/main/resources/schema.sql: -------------------------------------------------------------------------------- 1 | 2 | drop table if exists city; 3 | drop table if exists hotel; 4 | 5 | create table city (id int primary key auto_increment, name varchar, state varchar, country varchar,tenant_id varchar); 6 | create table hotel (city int, name varchar, address varchar, zip varchar,is_deleted char(1)); 7 | -------------------------------------------------------------------------------- /mybatis-sqlhelper-spring-boot/mybatis-sqlhelper-spring-boot-samples/mybatis-spring-boot-sample-xml/format.xml: -------------------------------------------------------------------------------- 1 | 2 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /mybatis-sqlhelper-spring-boot/mybatis-sqlhelper-spring-boot-samples/mybatis-spring-boot-sample-xml/license.txt: -------------------------------------------------------------------------------- 1 | Copyright ${license.git.copyrightYears} the original author or authors. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | -------------------------------------------------------------------------------- /mybatis-sqlhelper-spring-boot/mybatis-sqlhelper-spring-boot-samples/mybatis-spring-boot-sample-xml/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | 5 | mybatis-sqlhelper-spring-boot-samples 6 | io.github.heykb 7 | ${revision} 8 | 9 | mybatis-spring-boot-sample-xml 10 | jar 11 | mybatis-spring-boot-sample-xml 12 | 13 | 14 | 15 | io.github.heykb 16 | mybatis-sqlhelper-spring-boot-starter 17 | ${project.version} 18 | 19 | 20 | com.h2database 21 | h2 22 | runtime 23 | 24 | 25 | 26 | org.springframework.boot 27 | spring-boot-starter-test 28 | test 29 | 30 | 31 | 32 | 33 | 34 | org.springframework.boot 35 | spring-boot-maven-plugin 36 | ${spring-boot.version} 37 | 38 | 39 | 40 | repackage 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /mybatis-sqlhelper-spring-boot/mybatis-sqlhelper-spring-boot-samples/mybatis-spring-boot-sample-xml/src/main/java/sample/mybatis/xml/SampleXmlApplication.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015-2021 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package sample.mybatis.xml; 17 | 18 | import org.springframework.boot.CommandLineRunner; 19 | import org.springframework.boot.SpringApplication; 20 | import org.springframework.boot.autoconfigure.SpringBootApplication; 21 | import sample.mybatis.xml.mapper.HotelMapper; 22 | import sample.mybatis.xml.service.TestService; 23 | 24 | @SpringBootApplication 25 | public class SampleXmlApplication implements CommandLineRunner { 26 | 27 | public static void main(String[] args) { 28 | SpringApplication.run(SampleXmlApplication.class, args); 29 | } 30 | 31 | private final TestService testService; 32 | 33 | 34 | public SampleXmlApplication(TestService testService, HotelMapper hotelMapper) { 35 | this.testService = testService; 36 | } 37 | 38 | @Override 39 | @SuppressWarnings("squid:S106") 40 | public void run(String... args) { 41 | System.out.println(this.testService.findByState("CA")); 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /mybatis-sqlhelper-spring-boot/mybatis-sqlhelper-spring-boot-samples/mybatis-spring-boot-sample-xml/src/main/java/sample/mybatis/xml/domain/City.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015-2021 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package sample.mybatis.xml.domain; 17 | 18 | import java.io.Serializable; 19 | 20 | /** 21 | * @author Eddú Meléndez 22 | */ 23 | public class City implements Serializable { 24 | 25 | private static final long serialVersionUID = 1L; 26 | 27 | private Long id; 28 | 29 | private String name; 30 | 31 | private String state; 32 | 33 | private String country; 34 | 35 | public Long getId() { 36 | return this.id; 37 | } 38 | 39 | public void setId(Long id) { 40 | this.id = id; 41 | } 42 | 43 | public String getName() { 44 | return this.name; 45 | } 46 | 47 | public void setName(String name) { 48 | this.name = name; 49 | } 50 | 51 | public String getState() { 52 | return this.state; 53 | } 54 | 55 | public void setState(String state) { 56 | this.state = state; 57 | } 58 | 59 | public String getCountry() { 60 | return this.country; 61 | } 62 | 63 | public void setCountry(String country) { 64 | this.country = country; 65 | } 66 | 67 | @Override 68 | public String toString() { 69 | return getId() + "," + getName() + "," + getState() + "," + getCountry(); 70 | } 71 | 72 | } 73 | -------------------------------------------------------------------------------- /mybatis-sqlhelper-spring-boot/mybatis-sqlhelper-spring-boot-samples/mybatis-spring-boot-sample-xml/src/main/java/sample/mybatis/xml/domain/Hotel.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015-2021 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | /** 17 | * @author Eduardo Macarron 18 | */ 19 | package sample.mybatis.xml.domain; 20 | 21 | import java.io.Serializable; 22 | 23 | public class Hotel implements Serializable { 24 | 25 | private static final long serialVersionUID = 1L; 26 | 27 | private Long city; 28 | 29 | private String name; 30 | 31 | private String address; 32 | 33 | private String zip; 34 | 35 | public Long getCity() { 36 | return city; 37 | } 38 | 39 | public void setCity(Long city) { 40 | this.city = city; 41 | } 42 | 43 | public String getName() { 44 | return name; 45 | } 46 | 47 | public void setName(String name) { 48 | this.name = name; 49 | } 50 | 51 | public String getAddress() { 52 | return address; 53 | } 54 | 55 | public void setAddress(String address) { 56 | this.address = address; 57 | } 58 | 59 | public String getZip() { 60 | return zip; 61 | } 62 | 63 | public void setZip(String zip) { 64 | this.zip = zip; 65 | } 66 | 67 | @Override 68 | public String toString() { 69 | return getCity() + "," + getName() + "," + getAddress() + "," + getZip(); 70 | } 71 | 72 | } -------------------------------------------------------------------------------- /mybatis-sqlhelper-spring-boot/mybatis-sqlhelper-spring-boot-samples/mybatis-spring-boot-sample-xml/src/main/java/sample/mybatis/xml/mapper/CityMapper.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015-2021 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package sample.mybatis.xml.mapper; 17 | 18 | import org.apache.ibatis.annotations.Mapper; 19 | import org.apache.ibatis.annotations.Param; 20 | import org.apache.ibatis.annotations.Select; 21 | import sample.mybatis.xml.domain.City; 22 | 23 | /** 24 | * @author Eddú Meléndez 25 | */ 26 | @Mapper 27 | public interface CityMapper { 28 | 29 | @Select("select id, name, state, country from city where state = #{state}") 30 | City findByState(@Param("state") String state); 31 | 32 | } 33 | -------------------------------------------------------------------------------- /mybatis-sqlhelper-spring-boot/mybatis-sqlhelper-spring-boot-samples/mybatis-spring-boot-sample-xml/src/main/java/sample/mybatis/xml/mapper/HotelMapper.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015-2021 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package sample.mybatis.xml.mapper; 17 | 18 | import org.apache.ibatis.annotations.Mapper; 19 | import sample.mybatis.xml.domain.Hotel; 20 | 21 | /** 22 | * @author Eduardo Macarron 23 | */ 24 | @Mapper 25 | public interface HotelMapper { 26 | 27 | Hotel selectByCityId(int cityId); 28 | 29 | } 30 | -------------------------------------------------------------------------------- /mybatis-sqlhelper-spring-boot/mybatis-sqlhelper-spring-boot-samples/mybatis-spring-boot-sample-xml/src/main/java/sample/mybatis/xml/service/TestService.java: -------------------------------------------------------------------------------- 1 | package sample.mybatis.xml.service; 2 | 3 | import sample.mybatis.xml.domain.City; 4 | 5 | public interface TestService { 6 | City findByState(String state); 7 | } 8 | -------------------------------------------------------------------------------- /mybatis-sqlhelper-spring-boot/mybatis-sqlhelper-spring-boot-samples/mybatis-spring-boot-sample-xml/src/main/java/sample/mybatis/xml/service/impl/TestServiceImpl.java: -------------------------------------------------------------------------------- 1 | package sample.mybatis.xml.service.impl; 2 | 3 | import org.springframework.beans.factory.annotation.Autowired; 4 | import org.springframework.stereotype.Service; 5 | import org.springframework.transaction.annotation.Transactional; 6 | import sample.mybatis.xml.domain.City; 7 | import sample.mybatis.xml.mapper.CityMapper; 8 | import sample.mybatis.xml.service.TestService; 9 | 10 | @Service 11 | public class TestServiceImpl implements TestService { 12 | 13 | @Autowired 14 | private CityMapper cityMapper; 15 | 16 | @Override 17 | @Transactional 18 | public City findByState(String state) { 19 | return cityMapper.findByState(state); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /mybatis-sqlhelper-spring-boot/mybatis-sqlhelper-spring-boot-samples/mybatis-spring-boot-sample-xml/src/main/java/sample/mybatis/xml/sqlhelper/SimpleTenantInfoHandler.java: -------------------------------------------------------------------------------- 1 | package sample.mybatis.xml.sqlhelper; 2 | 3 | 4 | import io.github.heykb.sqlhelper.handler.abstractor.TenantInfoHandler; 5 | import org.springframework.stereotype.Component; 6 | 7 | /** 8 | * @author heykb 9 | */ 10 | @Component 11 | public class SimpleTenantInfoHandler extends TenantInfoHandler { 12 | public static final String TENANT_ID = "1"; 13 | @Override 14 | public String getTenantIdColumn() { 15 | return "tenant_id"; 16 | } 17 | @Override 18 | public String getTenantId() { 19 | // 可以从 20 | // SecurityContextHolder.getContext().getAuthentication() 21 | return "'"+TENANT_ID+"'"; 22 | } 23 | @Override 24 | public boolean checkTableName(String tableName) { 25 | return "city".equals(tableName); 26 | } 27 | 28 | @Override 29 | public boolean checkMapperId(String mapperId) { 30 | return !mapperId.contains("noPluginSelect"); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /mybatis-sqlhelper-spring-boot/mybatis-sqlhelper-spring-boot-samples/mybatis-spring-boot-sample-xml/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | mybatis.config-location=classpath:mybatis-config.xml 2 | logging.level.root=WARN 3 | logging.level.sample.mybatis.xml.mapper=TRACE 4 | sqlhelper.enable=true 5 | sqlhelper.multi-tenant.enable=true -------------------------------------------------------------------------------- /mybatis-sqlhelper-spring-boot/mybatis-sqlhelper-spring-boot-samples/mybatis-spring-boot-sample-xml/src/main/resources/data.sql: -------------------------------------------------------------------------------- 1 | 2 | insert into city (name, state, country) values ('San Francisco', 'CA', 'US'); 3 | insert into hotel(city, name, address, zip) values (1, 'Conrad Treasury Place', 'William & George Streets', '4001') 4 | -------------------------------------------------------------------------------- /mybatis-sqlhelper-spring-boot/mybatis-sqlhelper-spring-boot-samples/mybatis-spring-boot-sample-xml/src/main/resources/mybatis-config.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /mybatis-sqlhelper-spring-boot/mybatis-sqlhelper-spring-boot-samples/mybatis-spring-boot-sample-xml/src/main/resources/sample/mybatis/xml/mapper/CityMapper.xml: -------------------------------------------------------------------------------- 1 | 2 | 19 | 22 | 23 | 26 | 27 | -------------------------------------------------------------------------------- /mybatis-sqlhelper-spring-boot/mybatis-sqlhelper-spring-boot-samples/mybatis-spring-boot-sample-xml/src/main/resources/sample/mybatis/xml/mapper/HotelMapper.xml: -------------------------------------------------------------------------------- 1 | 2 | 19 | 22 | 23 | 26 | 27 | -------------------------------------------------------------------------------- /mybatis-sqlhelper-spring-boot/mybatis-sqlhelper-spring-boot-samples/mybatis-spring-boot-sample-xml/src/main/resources/schema.sql: -------------------------------------------------------------------------------- 1 | 2 | drop table if exists city; 3 | drop table if exists hotel; 4 | 5 | create table city (id int primary key auto_increment, name varchar, state varchar, country varchar,tenant_id varchar); 6 | create table hotel (city int, name varchar, address varchar, zip varchar,is_deleted char(1)); 7 | -------------------------------------------------------------------------------- /mybatis-sqlhelper-spring-boot/mybatis-sqlhelper-spring-boot-samples/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | mybatis-sqlhelper-spring-boot 7 | io.github.heykb 8 | ${revision} 9 | 10 | 4.0.0 11 | 12 | mybatis-spring-boot-sample-xml 13 | mybatis-spring-boot-dynamic-datasource 14 | 15 | 16 | mybatis-sqlhelper-spring-boot-samples 17 | pom 18 | 19 | true 20 | 21 | 22 | 23 | 24 | 25 | maven-deploy-plugin 26 | 3.0.0-M1 27 | 28 | true 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /mybatis-sqlhelper-spring-boot/mybatis-sqlhelper-spring-boot-starter/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | mybatis-sqlhelper-spring-boot 7 | io.github.heykb 8 | ${revision} 9 | 10 | 4.0.0 11 | mybatis-sqlhelper-spring-boot-starter 12 | mybatis-sqlhelper-spring-boot-starter 13 | 14 | 15 | org.springframework.boot 16 | spring-boot-starter 17 | 18 | 19 | org.mybatis.spring.boot 20 | mybatis-spring-boot-starter 21 | 22 | 23 | io.github.heykb 24 | mybatis-sqlhelper-spring-boot-autoconfigure 25 | 26 | 27 | io.github.heykb 28 | mybatis-sqlhelper-spring 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /mybatis-sqlhelper-spring-boot/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | io.github.heykb 7 | mybatis-sqlhelper-parent 8 | ${revision} 9 | 10 | 4.0.0 11 | 12 | mybatis-sqlhelper-spring-boot 13 | pom 14 | 15 | mybatis-sqlhelper-spring-boot-starter 16 | mybatis-sqlhelper-spring-boot-autoconfigure 17 | mybatis-sqlhelper-spring-boot-samples 18 | 19 | 20 | MyBatis SqlHelper Spring Boot 21 | MyBatis 多租户、逻辑删除、数据权限插件-SqlHelper Spring Boot版本 22 | 23 | 24 | 3.5.7 25 | 2.1.1 26 | 2.1.1.RELEASE 27 | 28 | 29 | 30 | 31 | org.mybatis 32 | mybatis 33 | ${mybatis.version} 34 | 35 | 36 | org.mybatis.spring.boot 37 | mybatis-spring-boot-autoconfigure 38 | ${mybatis-spring-boot.version} 39 | 40 | 41 | io.github.heykb 42 | mybatis-sqlhelper-spring-boot-autoconfigure 43 | ${project.version} 44 | 45 | 46 | io.github.heykb 47 | mybatis-sqlhelper-spring 48 | ${project.version} 49 | 50 | 51 | org.mybatis.spring.boot 52 | mybatis-spring-boot-starter 53 | ${mybatis-spring-boot.version} 54 | 55 | 56 | org.springframework.boot 57 | spring-boot-dependencies 58 | ${spring-boot.version} 59 | pom 60 | import 61 | 62 | 63 | 64 | 65 | -------------------------------------------------------------------------------- /mybatis-sqlhelper-spring/README.md: -------------------------------------------------------------------------------- 1 | # MyBatis SqlHelper Spring 2 | 3 | [![Maven central](https://maven-badges.herokuapp.com/maven-central/io.github.heykb/mybatis-sqlHelper-spring/badge.svg)](https://maven-badges.herokuapp.com/maven-central/io.github.heykb/mybatis-sqlHelper-spring) 4 | 5 | [MyBatis SqlHelper](https://github.com/heykb/mybatis-sqlhelper)的spring集成版本,友好集成了Spring依赖注入的能力,各种注入信息类可通过自动扫描bean添加到插件中。 6 | 7 | ~~~xml 8 | 9 | io.github.heykb 10 | mybatis-sqlHelper-spring 11 | 3.0.0.SR1 12 | 13 | ~~~ 14 | 15 | ## 在applicationContext.xml中如下配置 16 | 17 | 注意使用[SqlHelperPluginFactoryBean](src/main/java/io/github/heykb/sqlhelper/spring/SqlHelperPluginFactoryBean.java)工厂类创建bean而不是SqlHelperPlugin,[SqlHelperPluginFactoryBean](src/main/java/io/github/heykb/sqlhelper/spring/SqlHelperPluginFactoryBean.java)提供了自动扫描注入信息类的bean的能力,通过其properties属性可以像原始方式一样配置各项参数。SqlHelperPlugin的详细参数参见[MyBatis SqlHelper](https://github.com/heykb/mybatis-sqlhelper)。 18 | ~~~xml 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | true 33 | 34 | 35 | 36 | 37 | 38 | ~~~ 39 | ## 使用自动注入等功能,配置相关bean即可。 40 | ~~~xml 41 | 42 | ~~~ 43 | 44 | ## 提供[PropertyLogicDeleteInfoHandler](src/main/java/io/github/heykb/sqlhelper/spring/PropertyLogicDeleteInfoHandler.java)更方便配置逻辑删除 45 | ~~~xml 46 | 47 | 48 | is_deleted 49 | 50 | 51 | 'N' 52 | 53 | 54 | update xx set is_deleted = 'Y' 55 | 56 | 57 | 58 | **.noPlugin* 59 | 60 | 61 | 62 | 63 | people 64 | 65 | 66 | 67 | ~~~ 68 | 69 | ## 使用多数据源 70 | [多数据源专篇](../DYNAMIC_DATASOURCE_README.md) 71 | ## 未完待续。。(如果你有兴趣,右上角watch该项目获得最新的动态) 72 | 73 | ## 参与贡献 74 | 75 | 如果你发现问题,提交issue。 76 | 77 | 如果你解决了问题,fork项目提交pull request。 78 | 79 | ## 联系我 80 | QQ: 1259609102
81 | email: 1259609102@qq.com,bigsheller08@gmail.com -------------------------------------------------------------------------------- /mybatis-sqlhelper-spring/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | io.github.heykb 7 | mybatis-sqlhelper-parent 8 | ${revision} 9 | 10 | 4.0.0 11 | 12 | mybatis-sqlhelper-spring 13 | 14 | MyBatis SqlHelper Spring 15 | MyBatis 多租户、逻辑删除、数据权限插件-SqlHelper Spring版本 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | io.github.heykb 24 | mybatis-sqlhelper 25 | ${project.version} 26 | 27 | 28 | 29 | 30 | org.springframework 31 | spring-beans 32 | 5.3.13 33 | provided 34 | 35 | 36 | org.springframework 37 | spring-core 38 | 5.3.13 39 | provided 40 | 41 | 42 | org.springframework 43 | spring-context 44 | 5.3.13 45 | provided 46 | 47 | 48 | org.projectlombok 49 | lombok 50 | 1.18.20 51 | provided 52 | 53 | 54 | 55 | 56 | org.testcontainers 57 | junit-jupiter 58 | 1.16.2 59 | test 60 | 61 | 62 | org.postgresql 63 | postgresql 64 | 42.3.1 65 | test 66 | 67 | 68 | org.testcontainers 69 | junit-jupiter 70 | 1.16.2 71 | test 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | org.junit.jupiter 81 | junit-jupiter-engine 82 | 5.8.1 83 | test 84 | 85 | 86 | org.junit.jupiter 87 | junit-jupiter-params 88 | 5.8.1 89 | test 90 | 91 | 92 | org.springframework 93 | spring-jdbc 94 | 5.3.13 95 | test 96 | 97 | 98 | org.springframework 99 | spring-test 100 | 5.3.13 101 | test 102 | 103 | 104 | org.mybatis 105 | mybatis-spring 106 | 2.0.5 107 | test 108 | 109 | 110 | org.mybatis 111 | mybatis 112 | 3.5.7 113 | provided 114 | 115 | 116 | org.hsqldb 117 | hsqldb 118 | 2.5.2 119 | test 120 | 121 | 122 | org.slf4j 123 | slf4j-log4j12 124 | 1.7.32 125 | test 126 | 127 | 128 | org.mockito 129 | mockito-core 130 | 4.0.0 131 | test 132 | 133 | 134 | 135 | 136 | -------------------------------------------------------------------------------- /mybatis-sqlhelper-spring/src/main/java/io/github/heykb/sqlhelper/spring/PropertyLogicDeleteInfoHandler.java: -------------------------------------------------------------------------------- 1 | package io.github.heykb.sqlhelper.spring; 2 | 3 | import io.github.heykb.sqlhelper.handler.abstractor.LogicDeleteInfoHandler; 4 | import io.github.heykb.sqlhelper.utils.CommonUtils; 5 | import org.springframework.util.AntPathMatcher; 6 | 7 | import java.util.List; 8 | 9 | public class PropertyLogicDeleteInfoHandler extends LogicDeleteInfoHandler { 10 | static final AntPathMatcher antPathMatcher = new AntPathMatcher("."); 11 | private String deleteSqlDemo = "UPDATE a SET is_deleted = 'Y'"; 12 | private String columnName = "is_deleted"; 13 | private String notDeletedValue = "'N'"; 14 | 15 | private List mapperIds; 16 | 17 | private List tables; 18 | 19 | private List ignoreMapperIds; 20 | 21 | private List ignoreTables; 22 | 23 | 24 | /** 25 | * Gets ignore mapper ids. 26 | * 27 | * @return the ignore mapper ids 28 | */ 29 | public List getIgnoreMapperIds() { 30 | return ignoreMapperIds; 31 | } 32 | 33 | /** 34 | * Sets ignore mapper ids. 35 | * 36 | * @param ignoreMapperIds the ignore mapper ids 37 | */ 38 | public void setIgnoreMapperIds(List ignoreMapperIds) { 39 | this.ignoreMapperIds = ignoreMapperIds; 40 | } 41 | 42 | /** 43 | * Gets ignore tables. 44 | * 45 | * @return the ignore tables 46 | */ 47 | public List getIgnoreTables() { 48 | return ignoreTables; 49 | } 50 | 51 | /** 52 | * Sets ignore tables. 53 | * 54 | * @param ignoreTables the ignore tables 55 | */ 56 | public void setIgnoreTables(List ignoreTables) { 57 | this.ignoreTables = ignoreTables; 58 | } 59 | 60 | public List getMapperIds() { 61 | return mapperIds; 62 | } 63 | 64 | public void setMapperIds(List mapperIds) { 65 | this.mapperIds = mapperIds; 66 | } 67 | 68 | public List getTables() { 69 | return tables; 70 | } 71 | 72 | public void setTables(List tables) { 73 | this.tables = tables; 74 | } 75 | 76 | @Override 77 | public String getDeleteSqlDemo() { 78 | return this.deleteSqlDemo; 79 | } 80 | 81 | @Override 82 | public String getNotDeletedValue() { 83 | return notDeletedValue; 84 | } 85 | 86 | /** 87 | * Sets not deleted value. 88 | * 89 | * @param notDeletedValue the not deleted value 90 | */ 91 | public void setNotDeletedValue(String notDeletedValue) { 92 | this.notDeletedValue = notDeletedValue; 93 | } 94 | 95 | @Override 96 | public String getColumnName() { 97 | return this.columnName; 98 | } 99 | 100 | public void setDeleteSqlDemo(String deleteSqlDemo) { 101 | this.deleteSqlDemo = deleteSqlDemo; 102 | } 103 | 104 | /** 105 | * Sets column name. 106 | * 107 | * @param columnName the column name 108 | */ 109 | public void setColumnName(String columnName) { 110 | this.columnName = columnName; 111 | } 112 | 113 | @Override 114 | public boolean checkTableName(String tableName) { 115 | if(tables!=null && !wildcardMatchAny(this.tables,tableName)){ 116 | return false; 117 | } 118 | if(ignoreTables!=null && wildcardMatchAny(ignoreTables,tableName)){ 119 | return false; 120 | } 121 | return true; 122 | } 123 | 124 | 125 | @Override 126 | public boolean checkMapperId(String mapperId) { 127 | if(mapperIds!=null && !pathMatchAny(this.mapperIds,mapperId)){ 128 | return false; 129 | } 130 | if(ignoreMapperIds!=null && pathMatchAny(ignoreMapperIds,mapperId)){ 131 | return false; 132 | } 133 | return true; 134 | } 135 | 136 | boolean wildcardMatchAny(List patterns,String target){ 137 | for(String pattern:patterns){ 138 | if(CommonUtils.wildcardMatch(pattern,target)){ 139 | return true; 140 | } 141 | } 142 | return false; 143 | } 144 | 145 | boolean pathMatchAny(List patterns,String target){ 146 | for(String pattern:patterns){ 147 | if(antPathMatcher.match(pattern,target)){ 148 | return true; 149 | } 150 | } 151 | return false; 152 | } 153 | 154 | } 155 | 156 | -------------------------------------------------------------------------------- /mybatis-sqlhelper-spring/src/main/java/io/github/heykb/sqlhelper/spring/SqlHelperPluginFactoryBean.java: -------------------------------------------------------------------------------- 1 | package io.github.heykb.sqlhelper.spring; 2 | 3 | import io.github.heykb.sqlhelper.handler.ColumnFilterInfoHandler; 4 | import io.github.heykb.sqlhelper.handler.InjectColumnInfoHandler; 5 | import io.github.heykb.sqlhelper.handler.dynamic.DynamicFindColumnFilterHandler; 6 | import io.github.heykb.sqlhelper.handler.dynamic.DynamicFindInjectInfoHandler; 7 | import io.github.heykb.sqlhelper.interceptor.SqlHelperPlugin; 8 | import lombok.Setter; 9 | import org.springframework.beans.factory.FactoryBean; 10 | import org.springframework.beans.factory.annotation.Autowired; 11 | 12 | import java.util.List; 13 | import java.util.Properties; 14 | 15 | 16 | public class SqlHelperPluginFactoryBean implements FactoryBean { 17 | @Autowired(required = false) 18 | private List injectColumnInfoHandlers; 19 | @Autowired(required = false) 20 | private List columnFilterInfoHandlers; 21 | @Autowired(required = false) 22 | private DynamicFindColumnFilterHandler dynamicFindColumnFilterHandler; 23 | @Autowired(required = false) 24 | private DynamicFindInjectInfoHandler dynamicFindInjectInfoHandler; 25 | 26 | @Setter 27 | private Properties properties; 28 | 29 | @Override 30 | public SqlHelperPlugin getObject() throws Exception { 31 | SqlHelperPlugin re = new SqlHelperPlugin(); 32 | re.setDynamicFindColumnFilterHandler(dynamicFindColumnFilterHandler); 33 | re.setDynamicFindInjectInfoHandler(dynamicFindInjectInfoHandler); 34 | re.setInjectColumnInfoHandlers(injectColumnInfoHandlers); 35 | re.setColumnFilterInfoHandlers(columnFilterInfoHandlers); 36 | re.setProperties(properties); 37 | return re; 38 | } 39 | 40 | 41 | @Override 42 | public Class getObjectType() { 43 | return SqlHelperPlugin.class; 44 | } 45 | 46 | @Override 47 | public boolean isSingleton() { 48 | return true; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /mybatis-sqlhelper-spring/src/main/java/io/github/heykb/sqlhelper/spring/dynamicds/SpringSqlHelperDsManager.java: -------------------------------------------------------------------------------- 1 | package io.github.heykb.sqlhelper.spring.dynamicds; 2 | 3 | import io.github.heykb.sqlhelper.dynamicdatasource.DefaultSqlHelperDsManager; 4 | import io.github.heykb.sqlhelper.dynamicdatasource.LogicDsMeta; 5 | import io.github.heykb.sqlhelper.dynamicdatasource.SqlHelperDsManager; 6 | import org.springframework.beans.BeansException; 7 | import org.springframework.beans.factory.config.BeanPostProcessor; 8 | 9 | import javax.sql.DataSource; 10 | import java.util.List; 11 | 12 | public class SpringSqlHelperDsManager implements BeanPostProcessor, SqlHelperDsManager { 13 | private SqlHelperDsManager sqlHelperDsManager; 14 | @Override 15 | public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { 16 | if(bean instanceof DataSource){ 17 | if(bean instanceof SqlHelperDynamicDataSourceProxy){ 18 | this.sqlHelperDsManager = ((SqlHelperDynamicDataSourceProxy)bean).getSqlHelperDsManager(); 19 | return bean; 20 | }else{ 21 | this.sqlHelperDsManager = new DefaultSqlHelperDsManager((DataSource) bean); 22 | io.github.heykb.sqlhelper.dynamicdatasource.SqlHelperDynamicDataSourceProxy dataSource = new SqlHelperDynamicDataSourceProxy(this.sqlHelperDsManager); 23 | return dataSource; 24 | } 25 | } 26 | return bean; 27 | } 28 | 29 | @Override 30 | public void put(String logicName, LogicDsMeta dsMeta) { 31 | sqlHelperDsManager.put(logicName,dsMeta); 32 | } 33 | 34 | @Override 35 | public DataSource remove(String logicName) { 36 | return sqlHelperDsManager.remove(logicName); 37 | } 38 | 39 | @Override 40 | public boolean contains(String logicName) { 41 | return sqlHelperDsManager.contains(logicName); 42 | } 43 | 44 | @Override 45 | public boolean containsId(String dsId) { 46 | return sqlHelperDsManager.contains(dsId); 47 | } 48 | 49 | @Override 50 | public DataSource getByName(String logicName) { 51 | return sqlHelperDsManager.getByName(logicName); 52 | } 53 | 54 | @Override 55 | public DataSource getById(String dsId) { 56 | return sqlHelperDsManager.getById(dsId); 57 | } 58 | 59 | @Override 60 | public LogicDsMeta getLogicDsMeta(String switchedDsName) { 61 | return sqlHelperDsManager.getLogicDsMeta(switchedDsName); 62 | } 63 | 64 | @Override 65 | public List all() { 66 | return sqlHelperDsManager.all(); 67 | } 68 | 69 | @Override 70 | public List allDatasourceIds() { 71 | return sqlHelperDsManager.allDatasourceIds(); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /mybatis-sqlhelper-spring/src/main/java/io/github/heykb/sqlhelper/spring/dynamicds/SqlHelperDynamicDataSourceProxy.java: -------------------------------------------------------------------------------- 1 | package io.github.heykb.sqlhelper.spring.dynamicds; 2 | 3 | import io.github.heykb.sqlhelper.dynamicdatasource.SqlHelperDsManager; 4 | import org.springframework.core.InfrastructureProxy; 5 | 6 | import javax.sql.DataSource; 7 | import java.util.function.Function; 8 | 9 | class SqlHelperDynamicDataSourceProxy extends io.github.heykb.sqlhelper.dynamicdatasource.SqlHelperDynamicDataSourceProxy implements InfrastructureProxy { 10 | 11 | 12 | public SqlHelperDynamicDataSourceProxy(SqlHelperDsManager sqlHelperDsManager) { 13 | super(sqlHelperDsManager); 14 | } 15 | 16 | /** 17 | * 适应spring事务,做到数据源切换后再开启的事务为一个新的事务,保证正常切换。 18 | * @See org.springframework.transaction.support.TransactionSynchronizationUtils#unwrapResourceIfNecessary() 19 | * @return 20 | */ 21 | @Override 22 | public Object getWrappedObject() { 23 | return getDatasource(); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /mybatis-sqlhelper-spring/src/test/java/io/github/heykb/sqlhelper/spring/test/BaseDataUtils.java: -------------------------------------------------------------------------------- 1 | package io.github.heykb.sqlhelper.spring.test; 2 | 3 | import org.apache.ibatis.io.Resources; 4 | import org.apache.ibatis.jdbc.ScriptRunner; 5 | 6 | import javax.sql.DataSource; 7 | import java.io.IOException; 8 | import java.io.Reader; 9 | import java.sql.Connection; 10 | import java.sql.SQLException; 11 | 12 | public class BaseDataUtils { 13 | 14 | public static void runScript(DataSource ds, String resource) throws IOException, SQLException { 15 | try (Connection connection = ds.getConnection()) { 16 | ScriptRunner runner = new ScriptRunner(connection); 17 | runner.setAutoCommit(true); 18 | runner.setStopOnError(false); 19 | runner.setLogWriter(null); 20 | runner.setErrorLogWriter(null); 21 | runScript(runner, resource); 22 | } 23 | } 24 | public static void runScript(ScriptRunner runner, String resource) throws IOException, SQLException { 25 | try (Reader reader = Resources.getResourceAsReader(resource)) { 26 | runner.runScript(reader); 27 | } 28 | } 29 | 30 | 31 | } 32 | -------------------------------------------------------------------------------- /mybatis-sqlhelper-spring/src/test/java/io/github/heykb/sqlhelper/spring/test/PluginTests.java: -------------------------------------------------------------------------------- 1 | package io.github.heykb.sqlhelper.spring.test; 2 | 3 | import io.github.heykb.sqlhelper.config.SqlHelperException; 4 | import io.github.heykb.sqlhelper.dynamicdatasource.SqlHelperDsManager; 5 | import io.github.heykb.sqlhelper.spring.test.dao.EmployeeMapper; 6 | import io.github.heykb.sqlhelper.spring.test.dao.PeopleMapper; 7 | import io.github.heykb.sqlhelper.spring.test.domain.People; 8 | import org.apache.ibatis.session.SqlSession; 9 | import org.apache.ibatis.session.SqlSessionFactory; 10 | import org.junit.jupiter.api.Assertions; 11 | import org.junit.jupiter.api.DisplayName; 12 | import org.junit.jupiter.api.Test; 13 | import org.junit.jupiter.api.extension.ExtendWith; 14 | import org.slf4j.Logger; 15 | import org.slf4j.LoggerFactory; 16 | import org.springframework.beans.factory.annotation.Autowired; 17 | import org.springframework.test.context.junit.jupiter.SpringExtension; 18 | import org.springframework.test.context.junit.jupiter.SpringJUnitConfig; 19 | 20 | import java.util.List; 21 | 22 | @ExtendWith(SpringExtension.class) 23 | @SpringJUnitConfig(locations = "classpath:io/github/heykb/sqlhelper/spring/test/applicationContext.xml") 24 | public class PluginTests { 25 | private Logger LOG = LoggerFactory.getLogger(this.getClass()); 26 | @Autowired 27 | private SqlSessionFactory sqlSessionFactory; 28 | 29 | @Autowired(required = false) 30 | private SqlHelperDsManager sqlHelperDsManager; 31 | 32 | 33 | @Test 34 | @DisplayName("简单select查询") 35 | void selectTest(){ 36 | try (SqlSession sqlSession = sqlSessionFactory.openSession();) { 37 | PeopleMapper demoMapper = sqlSession.getMapper(PeopleMapper.class); 38 | List list = demoMapper.selectList(People.builder().name("tom").build()); 39 | Assertions.assertEquals(list.size(),1); 40 | } 41 | } 42 | @Test 43 | @DisplayName("转逻辑删除测试") 44 | void logicDeleteTest(){ 45 | try (SqlSession sqlSession = sqlSessionFactory.openSession();) { 46 | EmployeeMapper demoMapper = sqlSession.getMapper(EmployeeMapper.class); 47 | int count1 = demoMapper.count(); 48 | demoMapper.deleteById(1); 49 | int count2 = demoMapper.count(); 50 | Assertions.assertEquals(count1,count2+1); 51 | int count3 = demoMapper.noPluginCount(); 52 | Assertions.assertEquals(count1,count3); 53 | } 54 | } 55 | 56 | @Test 57 | @DisplayName("多数据源注入测试") 58 | void multiDatasourceTest(){ 59 | Assertions.assertThrows(SqlHelperException.class,()->sqlHelperDsManager.remove("xxx")); 60 | } 61 | 62 | } 63 | -------------------------------------------------------------------------------- /mybatis-sqlhelper-spring/src/test/java/io/github/heykb/sqlhelper/spring/test/applicationContext.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | true 27 | true 28 | 29 | 30 | 31 | 32 | 33 | 34 | is_deleted 35 | 36 | 37 | 'N' 38 | 39 | 40 | update xx set is_deleted = 'Y' where id = 'xx' 41 | 42 | 43 | 44 | **.noPlugin* 45 | 46 | 47 | 48 | 49 | people 50 | 51 | 52 | 53 | 54 | ?* 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /mybatis-sqlhelper-spring/src/test/java/io/github/heykb/sqlhelper/spring/test/dao/EmployeeMapper.java: -------------------------------------------------------------------------------- 1 | package io.github.heykb.sqlhelper.spring.test.dao; 2 | 3 | 4 | import org.apache.ibatis.annotations.Delete; 5 | import org.apache.ibatis.annotations.Mapper; 6 | import org.apache.ibatis.annotations.Param; 7 | import org.apache.ibatis.annotations.Select; 8 | 9 | @Mapper 10 | public interface EmployeeMapper { 11 | 12 | @Select("select count(*) from employees") 13 | int count(); 14 | 15 | @Select("select count(*) from employees") 16 | int noPluginCount(); 17 | 18 | @Delete("delete from employees where id = #{id}") 19 | int deleteById(@Param("id")Integer id); 20 | 21 | 22 | } 23 | -------------------------------------------------------------------------------- /mybatis-sqlhelper-spring/src/test/java/io/github/heykb/sqlhelper/spring/test/dao/EmployeeMapper.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /mybatis-sqlhelper-spring/src/test/java/io/github/heykb/sqlhelper/spring/test/dao/PeopleMapper.java: -------------------------------------------------------------------------------- 1 | package io.github.heykb.sqlhelper.spring.test.dao; 2 | 3 | import io.github.heykb.sqlhelper.spring.test.domain.People; 4 | import org.apache.ibatis.annotations.Mapper; 5 | import org.apache.ibatis.annotations.Param; 6 | 7 | import java.util.List; 8 | 9 | /** 10 | * @author kb 11 | */ 12 | @Mapper 13 | public interface PeopleMapper { 14 | /** 15 | * [新增] 16 | * @author kb 17 | **/ 18 | int insert(People people); 19 | /** 20 | * [刪除] 21 | * @author kb 22 | **/ 23 | int delete(@Param("id") String id); 24 | 25 | /** 26 | * [更新] 27 | * @author kb 28 | **/ 29 | int update(People people); 30 | 31 | /** 32 | * [查询] 根据主键 id 查询 33 | * @author kb 34 | **/ 35 | People select(@Param("id") String id); 36 | /** 37 | * [查询] 分页查询 38 | * @author kb 39 | **/ 40 | List selectList(People people); 41 | 42 | 43 | People leftJoinSelect(@Param("id") String id); 44 | 45 | 46 | List noPluginSelect(People people); 47 | 48 | /** 49 | * [查询] 批量插入 50 | * @author kb 51 | **/ 52 | int batchInsert(List peoples); 53 | 54 | /** 55 | * [查询] 批量删除 56 | * @author kb 57 | **/ 58 | int batchDelete(List ids); 59 | } 60 | -------------------------------------------------------------------------------- /mybatis-sqlhelper-spring/src/test/java/io/github/heykb/sqlhelper/spring/test/dao/PeopleMapper.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | name, 17 | age, 18 | email, 19 | dept_id, 20 | id 21 | 22 | INSERT INTO people 23 | 24 | name, 25 | age, 26 | email, 27 | dept_id, 28 | id, 29 | 30 | 31 | #{name}, 32 | #{age}, 33 | #{email}, 34 | #{deptId}, 35 | #{id}, 36 | 37 | 38 | 39 | DELETE FROM people 40 | WHERE id = #{id} 41 | 42 | 43 | 44 | UPDATE people 45 | 46 | name = #{name}, 47 | age = #{age}, 48 | email = #{email}, 49 | dept_id = #{deptId}, 50 | 51 | WHERE id = #{id} 52 | 53 | 54 | 55 | 60 | 61 | 66 | 67 | 78 | 79 | 90 | 91 | 92 | INSERT INTO people() VALUES 93 | 94 | ( #{item.name}, #{item.age}, #{item.email}, #{item.deptId}, #{item.id} 95 | 96 | 97 | 98 | DELETE FROM people 99 | WHERE id IN 100 | 101 | #{id} 102 | 103 | 104 | 105 | -------------------------------------------------------------------------------- /mybatis-sqlhelper-spring/src/test/java/io/github/heykb/sqlhelper/spring/test/db/createDb.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE people ( 2 | name varchar(255) , 3 | age int, 4 | email varchar(255) , 5 | id varchar(255) NOT NULL, 6 | tenant_id varchar(255), 7 | dept_id varchar(255), 8 | created_time timestamp(6), 9 | created_by varchar(255) , 10 | updated_time timestamp(6), 11 | updated_by varchar(255), 12 | CONSTRAINT people_pkey PRIMARY KEY (id) 13 | ) 14 | ; 15 | -- COMMENT ON COLUMN people.created_time IS '创建时间'; 16 | -- COMMENT ON COLUMN people.created_by IS '创建人'; 17 | -- COMMENT ON COLUMN people.updated_time IS '更新时间'; 18 | -- COMMENT ON COLUMN people.updated_by IS '更新人'; 19 | 20 | CREATE TABLE department ( 21 | dept_id varchar(255) NOT NULL, 22 | dept_name varchar(255) , 23 | tenant_id varchar(255), 24 | created_time timestamp(6) , 25 | created_by varchar(255) , 26 | updated_time timestamp(6), 27 | updated_by varchar(255) , 28 | CONSTRAINT dept_pkey PRIMARY KEY (dept_id) 29 | ) 30 | ; 31 | 32 | 33 | -- COMMENT ON COLUMN department.created_time IS '创建时间'; 34 | -- 35 | -- COMMENT ON COLUMN department.created_by IS '创建人'; 36 | -- 37 | -- COMMENT ON COLUMN department.updated_time IS '更新时间'; 38 | -- 39 | -- COMMENT ON COLUMN department.updated_by IS '更新人'; 40 | 41 | -- ---------------------------- 42 | -- Records of people 43 | -- ---------------------------- 44 | INSERT INTO people VALUES ('tom', NULL, NULL, '1', '1', NULL, '2021-11-12 12:18:14.235029', 'heykb', NULL, 'heykb'); 45 | INSERT INTO people VALUES ('tom', NULL, NULL, '2', '2', NULL, '2021-11-12 12:18:14.235029', 'heykb', NULL, 'heykb'); 46 | 47 | -------------------------------------------------------------------------------- /mybatis-sqlhelper-spring/src/test/java/io/github/heykb/sqlhelper/spring/test/db/logicDeleteTest.sql: -------------------------------------------------------------------------------- 1 | create table employees ( 2 | id integer not null, 3 | is_deleted char(1) default 'N' not null 4 | ); 5 | insert into employees(id) VALUES (1); 6 | insert into employees(id) VALUES (2); -------------------------------------------------------------------------------- /mybatis-sqlhelper-spring/src/test/java/io/github/heykb/sqlhelper/spring/test/domain/Employee.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2010-2021 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package io.github.heykb.sqlhelper.spring.test.domain; 17 | 18 | import lombok.AllArgsConstructor; 19 | import lombok.Builder; 20 | import lombok.Data; 21 | import lombok.NoArgsConstructor; 22 | 23 | @Data 24 | @AllArgsConstructor 25 | @NoArgsConstructor 26 | @Builder 27 | public class Employee { 28 | private int id; 29 | } 30 | -------------------------------------------------------------------------------- /mybatis-sqlhelper-spring/src/test/java/io/github/heykb/sqlhelper/spring/test/domain/People.java: -------------------------------------------------------------------------------- 1 | package io.github.heykb.sqlhelper.spring.test.domain; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Builder; 5 | import lombok.Data; 6 | import lombok.NoArgsConstructor; 7 | 8 | /** 9 | * 10 | * @author kb 11 | */ 12 | @Data 13 | @Builder 14 | @AllArgsConstructor 15 | @NoArgsConstructor 16 | public class People { 17 | private String name; 18 | private Integer age; 19 | private String email; 20 | private String deptId; 21 | private String id; 22 | private String createdBy; 23 | private String updatedBy; 24 | private String tenantId; 25 | } 26 | -------------------------------------------------------------------------------- /mybatis-sqlhelper-spring/src/test/java/io/github/heykb/sqlhelper/spring/test/handlers/SimpleColumnFilterHandler.java: -------------------------------------------------------------------------------- 1 | package io.github.heykb.sqlhelper.spring.test.handlers; 2 | 3 | import io.github.heykb.sqlhelper.handler.ColumnFilterInfoHandler; 4 | import org.apache.commons.compress.utils.Sets; 5 | 6 | import java.util.Set; 7 | 8 | /** 9 | * @author heykb 10 | */ 11 | public class SimpleColumnFilterHandler implements ColumnFilterInfoHandler { 12 | 13 | @Override 14 | public Set getFilterColumns() { 15 | return Sets.newHashSet("name"); 16 | } 17 | 18 | @Override 19 | public boolean checkMapperId(String mapperId) { 20 | return true; 21 | } 22 | 23 | @Override 24 | public boolean checkTableName(String tableName) { 25 | return true; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /mybatis-sqlhelper-spring/src/test/java/io/github/heykb/sqlhelper/spring/test/handlers/SimpleTenantInfoHandler.java: -------------------------------------------------------------------------------- 1 | package io.github.heykb.sqlhelper.spring.test.handlers; 2 | 3 | 4 | import io.github.heykb.sqlhelper.handler.abstractor.TenantInfoHandler; 5 | 6 | /** 7 | * @author heykb 8 | */ 9 | public class SimpleTenantInfoHandler extends TenantInfoHandler { 10 | public static final String TENANT_ID = "1"; 11 | @Override 12 | public String getTenantIdColumn() { 13 | return "tenant_id"; 14 | } 15 | @Override 16 | public String getTenantId() { 17 | // 可以从 18 | // SecurityContextHolder.getContext().getAuthentication() 19 | return "'"+TENANT_ID+"'"; 20 | } 21 | @Override 22 | public boolean checkTableName(String tableName) { 23 | return "people".equals(tableName)||"department".equals(tableName); 24 | } 25 | 26 | @Override 27 | public boolean checkMapperId(String mapperId) { 28 | return !mapperId.contains("noPluginSelect"); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /mybatis-sqlhelper-spring/src/test/resources/log4j.properties: -------------------------------------------------------------------------------- 1 | log4j.rootLogger=DEBUG,Console 2 | log4j.appender.Console=org.apache.log4j.ConsoleAppender 3 | log4j.appender.Console.Target=System.out 4 | log4j.appender.Console.layout=org.apache.log4j.PatternLayout 5 | log4j.appender.Console.layout.ConversionPattern=[%d{yy/MM/dd HH:mm:ss:SSS}]-%l:%m%n -------------------------------------------------------------------------------- /mybatis-sqlhelper/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | io.github.heykb 5 | mybatis-sqlhelper-parent 6 | ${revision} 7 | 8 | 9 | 4.0.0 10 | 11 | mybatis-sqlhelper 12 | MyBatis SqlHelper 13 | MyBatis 多租户、逻辑删除、数据权限插件-SqlHelper 14 | 15 | 16 | UTF-8 17 | 1.8 18 | 1.8 19 | 1.2.11 20 | 21.0 21 | 3.2.2 22 | 23 | 24 | 25 | 26 | com.alibaba 27 | druid 28 | ${druid.version} 29 | 30 | 31 | com.google.guava 32 | guava 33 | ${guava.version} 34 | 35 | 36 | commons-collections 37 | commons-collections 38 | ${commons-collections.version} 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | com.alibaba 47 | druid 48 | 49 | 50 | com.google.guava 51 | guava 52 | 53 | 54 | commons-collections 55 | commons-collections 56 | 57 | 58 | 59 | org.mybatis 60 | mybatis 61 | 3.5.6 62 | provided 63 | 64 | 65 | org.projectlombok 66 | lombok 67 | 1.18.20 68 | provided 69 | 70 | 71 | 72 | 73 | 74 | org.hsqldb 75 | hsqldb 76 | 2.5.2 77 | test 78 | 79 | 80 | mysql 81 | mysql-connector-java 82 | 8.0.27 83 | test 84 | 85 | 86 | com.microsoft.sqlserver 87 | sqljdbc4 88 | 4.0 89 | test 90 | 91 | 92 | com.h2database 93 | h2 94 | 1.4.200 95 | test 96 | 97 | 98 | 99 | org.postgresql 100 | postgresql 101 | 42.3.1 102 | test 103 | 104 | 105 | org.testcontainers 106 | junit-jupiter 107 | 1.16.2 108 | test 109 | 110 | 111 | org.junit.jupiter 112 | junit-jupiter-engine 113 | 5.8.1 114 | test 115 | 116 | 117 | org.junit.jupiter 118 | junit-jupiter-params 119 | 5.8.1 120 | test 121 | 122 | 123 | com.github.pagehelper 124 | pagehelper 125 | 5.2.0 126 | test 127 | 128 | 129 | log4j 130 | log4j 131 | 1.2.17 132 | test 133 | 134 | 135 | org.slf4j 136 | slf4j-log4j12 137 | 1.7.21 138 | test 139 | 140 | 141 | org.slf4j 142 | slf4j-api 143 | 1.7.21 144 | test 145 | 146 | 147 | com.alibaba 148 | fastjson 149 | 1.2.79 150 | test 151 | 152 | 153 | 154 | 155 | -------------------------------------------------------------------------------- /mybatis-sqlhelper/src/main/java/io/github/heykb/sqlhelper/config/SqlHelperAutoDbType.java: -------------------------------------------------------------------------------- 1 | package io.github.heykb.sqlhelper.config; 2 | 3 | import com.alibaba.druid.DbType; 4 | import io.github.heykb.sqlhelper.utils.CommonUtils; 5 | 6 | import javax.sql.DataSource; 7 | import java.net.URI; 8 | import java.sql.Connection; 9 | import java.sql.SQLException; 10 | 11 | /** 12 | * @author heykb 13 | */ 14 | public class SqlHelperAutoDbType { 15 | public static DbType getDbType(DataSource dataSource){ 16 | String url = getUrl(dataSource); 17 | if (CommonUtils.isEmpty(url)) { 18 | throw new SqlHelperException("无法自动获取jdbcUrl,请通过配置指定数据库类型!"); 19 | } 20 | DbType dbType = fromJdbcUrl(url); 21 | if (dbType == null) { 22 | throw new SqlHelperException("无法从" + url + "自动获取数据库类型com.alibaba.druid.DbType,请通过配置指定数据库类型!"); 23 | } 24 | return dbType; 25 | } 26 | 27 | public static DbType getDbType(Connection connection) { 28 | try { 29 | DbType dbType = fromJdbcUrl(connection.getMetaData().getURL()); 30 | return dbType; 31 | } catch (SQLException e) { 32 | throw new SqlHelperException("自动获取DbType失败"); 33 | } 34 | } 35 | 36 | public static DbType fromJdbcUrl(String jdbcUrl) { 37 | final String url = jdbcUrl.toLowerCase(); 38 | for (DbType dbType : DbType.values()) { 39 | if (url.contains(dbType.name().toLowerCase())) { 40 | return dbType; 41 | } 42 | } 43 | return null; 44 | } 45 | 46 | /** 47 | * 获取url 48 | * 49 | * @param dataSource 50 | * @return 51 | */ 52 | static private String getUrl(DataSource dataSource) { 53 | Connection conn = null; 54 | try { 55 | conn = dataSource.getConnection(); 56 | return conn.getMetaData().getURL(); 57 | } catch (SQLException e) { 58 | throw new SqlHelperException(e); 59 | } finally { 60 | if (conn != null) { 61 | try { 62 | conn.close(); 63 | } catch (SQLException e) { 64 | //ignore 65 | } 66 | } 67 | } 68 | } 69 | 70 | static void test(String url) { 71 | String cleanURI = url.substring(5); 72 | URI uri = URI.create(cleanURI); 73 | System.out.println(url); 74 | System.out.println(uri.getScheme()); 75 | System.out.println(uri.getHost()); 76 | System.out.println(uri.getPort()); 77 | System.out.println(uri.getPath()); 78 | System.out.println("*****************"); 79 | } 80 | 81 | public static void main(String[] args) { 82 | test("jdbc:oracle:thin:@localhost:1521:orclpdb1"); 83 | test("jdbc:mysql://localhost/database2"); 84 | test("jdbc:postgresql://localhost/database2"); 85 | test("jdbc:sqlserver://localhost;instance=SQLEXPRESS;databaseName=database2"); 86 | test("jdbc:mariadb://localhost/database2"); 87 | test("jdbc:db2://localhost/database2"); 88 | test("jdbc:sap://localhost/database2"); 89 | test("jdbc:informix-sqli://localhost:9088/sysuser:INFORMIXSERVER=hpjp"); 90 | test("jdbc:hsqldb:mem:database2"); 91 | test("jdbc:h2:mem:database2"); 92 | test("jdbc:derby:target/tmp/derby/hpjp;databaseName=database2;create=true"); 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /mybatis-sqlhelper/src/main/java/io/github/heykb/sqlhelper/config/SqlHelperException.java: -------------------------------------------------------------------------------- 1 | package io.github.heykb.sqlhelper.config; 2 | 3 | /** 4 | * @author heykb 5 | */ 6 | public class SqlHelperException extends RuntimeException { 7 | public SqlHelperException(Throwable cause) { 8 | super(cause); 9 | } 10 | 11 | public SqlHelperException(String message, Throwable cause) { 12 | super(message, cause); 13 | } 14 | 15 | 16 | public SqlHelperException(String message){ 17 | super(message); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /mybatis-sqlhelper/src/main/java/io/github/heykb/sqlhelper/dynamicdatasource/ConnectionSubspaceTypeEnum.java: -------------------------------------------------------------------------------- 1 | package io.github.heykb.sqlhelper.dynamicdatasource; 2 | 3 | 4 | /** 5 | * The enum Connection subspace type. 6 | */ 7 | public enum ConnectionSubspaceTypeEnum { 8 | /** 9 | * Not support connection subspace type. 10 | */ 11 | NOT_SUPPORT, 12 | /** 13 | * Database connection subspace type. 14 | */ 15 | DATABASE, 16 | /** 17 | * Schema connection subspace type. 18 | */ 19 | SCHEMA; 20 | 21 | } -------------------------------------------------------------------------------- /mybatis-sqlhelper/src/main/java/io/github/heykb/sqlhelper/dynamicdatasource/DefaultSqlHelperDsManager.java: -------------------------------------------------------------------------------- 1 | package io.github.heykb.sqlhelper.dynamicdatasource; 2 | 3 | import io.github.heykb.sqlhelper.config.SqlHelperException; 4 | import io.github.heykb.sqlhelper.utils.Asserts; 5 | import org.apache.ibatis.logging.Log; 6 | import org.apache.ibatis.logging.LogFactory; 7 | 8 | import javax.sql.DataSource; 9 | import java.sql.Connection; 10 | import java.sql.SQLException; 11 | import java.util.*; 12 | import java.util.concurrent.ConcurrentHashMap; 13 | import java.util.concurrent.locks.ReentrantLock; 14 | import java.util.function.Function; 15 | 16 | /** 17 | * The type Sqlhelper ds manager. 18 | */ 19 | public class DefaultSqlHelperDsManager implements SqlHelperDsManager { 20 | public static final String PRIMARY_DATASOURCE_ID = "SQLHELP_PRIMARY_DS"; 21 | private static final Log log = LogFactory.getLog(DefaultSqlHelperDsManager.class); 22 | private final Map logicDsName2DsMeta = new HashMap<>(); 23 | private final Map dsId2Datasource = new HashMap<>(); 24 | private final Map> dsId2LogicDsName = new HashMap<>(); 25 | protected ReentrantLock lock = new ReentrantLock(true); 26 | /** 27 | * 当新增加的数据源存在共用情况时,可通过该方法注入升级数据源的逻辑,如可以升级数据源的连接池大小 28 | */ 29 | private Function dsUpgradeCallback; 30 | 31 | /** 32 | * Instantiates a new Sqlhelper ds manager. 33 | * 34 | * @param primaryDs the primary ds 35 | * @param dsUpgradeCallback the ds upgrade callback 36 | */ 37 | public DefaultSqlHelperDsManager(DataSource primaryDs, Function dsUpgradeCallback) { 38 | put(null,LogicDsMeta.builder().datasourceId(PRIMARY_DATASOURCE_ID).createFunc(()->primaryDs).build()); 39 | this.dsUpgradeCallback = dsUpgradeCallback; 40 | } 41 | 42 | /** 43 | * Instantiates a new Sqlhelper ds manager. 44 | * 45 | * @param primaryDs the primary ds 46 | */ 47 | public DefaultSqlHelperDsManager(DataSource primaryDs) { 48 | this(primaryDs, null); 49 | } 50 | 51 | @Override 52 | public LogicDsMeta getLogicDsMeta(String switchedDsName) { 53 | return logicDsName2DsMeta.get(switchedDsName); 54 | } 55 | 56 | @Override 57 | public List all() { 58 | return new ArrayList<>(logicDsName2DsMeta.keySet()); 59 | } 60 | 61 | @Override 62 | public List allDatasourceIds() { 63 | return new ArrayList<>(dsId2Datasource.keySet()); 64 | } 65 | 66 | /** 67 | * Put. 68 | * 69 | * @param logicName the logic name 70 | * @param dsMeta the ds meta 71 | */ 72 | @Override 73 | synchronized public void put(String logicName, LogicDsMeta dsMeta) { 74 | lock.lock(); 75 | try{ 76 | log.warn("添加逻辑数据源" + logicName); 77 | if (logicDsName2DsMeta.containsKey(logicName)) { 78 | throw new SqlHelperException("数据源名称已存在"); 79 | } 80 | DataSource newDatasource = null; 81 | if (dsId2Datasource.get(dsMeta.getDatasourceId()) == null) { 82 | if (dsMeta.getCreateFunc() == null) { 83 | throw new SqlHelperException("缺少createFunc数据源创建方法,无法初始化" + dsMeta.getDatasourceId() + "数据源"); 84 | } 85 | log.warn("为逻辑数据源 " + logicName + " 初始化新的数据源 " + dsMeta.getDatasourceId()); 86 | try{ 87 | newDatasource = dsMeta.getCreateFunc().call(); 88 | }catch (Exception e){ 89 | new SqlHelperException("初始化"+logicName+"数据源失败",e); 90 | } 91 | } else { 92 | log.warn("逻辑数据源" + logicName + "复用已存在的数据源ID:" + dsMeta.getDatasourceId()); 93 | // if(dsMeta.getSubspace()==null){ 94 | // throw new SqlHelperException("复用已有数据源,subspace不能为null"); 95 | // } 96 | newDatasource = upgradeDatasourceByDatasourceId(dsMeta.getDatasourceId()); 97 | } 98 | dsId2Datasource.put(dsMeta.getDatasourceId(), newDatasource); 99 | // 保存逻辑数据源信息 100 | logicDsName2DsMeta.put(logicName, dsMeta); 101 | List logicNames = dsId2LogicDsName.getOrDefault(dsMeta.getDatasourceId(),new ArrayList<>()); 102 | if(logicNames.size()==1){ 103 | // 复用数据源 但是原引用逻辑数据源没有设置子空间 自动为其设置 104 | LogicDsMeta logicDsMeta = logicDsName2DsMeta.get(logicNames.get(0)); 105 | if(logicDsMeta.getSubspace() == null){ 106 | try(Connection connection = newDatasource.getConnection()){ 107 | log.warn("逻辑数据源"+logicDsMeta.getSubspace()+"与其他数据源共享但是未设置subspace,尝试从连接中自动获取"); 108 | logicDsMeta.setSubspace(SupportedConnectionSubspaceChange.getCurrentSubspaceIfSupport(connection,dsMeta.getExpectedSubspaceType())); 109 | log.warn("从连接中获取成功,设置subspace为"+logicDsMeta.getSubspace()); 110 | } catch (SQLException e) { 111 | throw new SqlHelperException(e); 112 | } 113 | } 114 | } 115 | logicNames.add(logicName); 116 | dsId2LogicDsName.put(dsMeta.getDatasourceId(),logicNames); 117 | }finally { 118 | lock.unlock(); 119 | } 120 | 121 | } 122 | 123 | 124 | @Override 125 | public DataSource remove(String logicName) { 126 | lock.lock(); 127 | try{ 128 | LogicDsMeta logicDsMeta = logicDsName2DsMeta.remove(logicName); 129 | if(logicDsMeta == null){ 130 | throw new SqlHelperException(logicName+"数据源名称不存在"); 131 | } 132 | if(!PRIMARY_DATASOURCE_ID.equals(logicDsMeta.getDatasourceId()) && dsId2Datasource.get(logicDsMeta.getDatasourceId())!=null ){ 133 | for(LogicDsMeta item:logicDsName2DsMeta.values()){ 134 | if(logicDsMeta.getDatasourceId().equals(item.getDatasourceId())){ 135 | // 存在其他引用,不删除数据源id 136 | return null; 137 | } 138 | } 139 | return dsId2Datasource.remove(logicDsMeta.getDatasourceId()); 140 | } 141 | return null; 142 | }finally { 143 | lock.unlock(); 144 | } 145 | } 146 | 147 | @Override 148 | public boolean contains(String logicName) { 149 | return logicDsName2DsMeta.containsKey(logicName); 150 | } 151 | 152 | @Override 153 | public boolean containsId(String dsId) { 154 | return dsId2Datasource.containsKey(dsId); 155 | } 156 | 157 | @Override 158 | public DataSource getByName(String logicName) { 159 | if(logicName == null){ 160 | return getById(PRIMARY_DATASOURCE_ID); 161 | } 162 | LogicDsMeta logicDsMeta = logicDsName2DsMeta.get(logicName); 163 | if(logicDsMeta!=null){ 164 | return getById(logicDsMeta.getDatasourceId()); 165 | } 166 | return null; 167 | } 168 | 169 | @Override 170 | public DataSource getById(String dsId) { 171 | return dsId2Datasource.get(dsId); 172 | } 173 | 174 | 175 | /** 176 | * Gets by logic name. 177 | * 178 | * @param logicName the logic name 179 | * @return the by logic name 180 | */ 181 | public LogicDsMeta getByLogicName(String logicName) { 182 | LogicDsMeta re = logicDsName2DsMeta.get(logicName); 183 | if (re == null) { 184 | throw new SqlHelperException("逻辑数据源" + logicName + "不存在"); 185 | } 186 | return re; 187 | } 188 | 189 | /** 190 | * Upgrade datasource by datasource id data source. 191 | * 192 | * @param datasourceId the datasource id 193 | * @return the data source 194 | */ 195 | DataSource upgradeDatasourceByDatasourceId(String datasourceId) { 196 | DataSource dataSource = getById(datasourceId); 197 | if (dsUpgradeCallback != null) { 198 | log.warn("升级数据源 " + datasourceId); 199 | return dsId2Datasource.put(datasourceId, dsUpgradeCallback.apply(dataSource)); 200 | } 201 | return dataSource; 202 | } 203 | } 204 | -------------------------------------------------------------------------------- /mybatis-sqlhelper/src/main/java/io/github/heykb/sqlhelper/dynamicdatasource/LogicDsMeta.java: -------------------------------------------------------------------------------- 1 | package io.github.heykb.sqlhelper.dynamicdatasource; 2 | 3 | import io.github.heykb.sqlhelper.utils.Asserts; 4 | import lombok.Data; 5 | 6 | import javax.sql.DataSource; 7 | import java.util.concurrent.Callable; 8 | 9 | /** 10 | * The type Logic ds meta. 11 | */ 12 | @Data 13 | public class LogicDsMeta { 14 | 15 | /** 16 | * 数据源id,当已注册数据源中存在相同id时会复用数据源,并且会触发数据源升级回调方法如果存在的话。 17 | * 使用同一个数据源id的不同逻辑数据源可以设置不同的subspace子空间。如mysql支持同一个连接切换不同数据库 18 | */ 19 | private String datasourceId; 20 | /** 21 | * 设置期望的子空间类型(不是所有的数据库都支持同一个连接进行切换) 22 | * 主要作用在于当用户期望和软件支持不匹配能快速失败及时报错 23 | */ 24 | private ConnectionSubspaceTypeEnum expectedSubspaceType; 25 | /** 26 | * 当逻辑数据源使用时将连接切换到指定的子空间。子空间的名称。仅当数据库类型支持子空间时有效 27 | * nullable 28 | */ 29 | private String subspace; 30 | private String table_prefix; 31 | /** 32 | * 当数据源id不存在时,使用此回调创建新的数据源 33 | */ 34 | private Callable createFunc; 35 | 36 | 37 | private LogicDsMeta() { 38 | } 39 | 40 | /** 41 | * Builder logic ds meta builder. 42 | * 43 | * @return the logic ds meta builder 44 | */ 45 | public static final LogicDsMetaBuilder builder() { 46 | return new LogicDsMetaBuilder(); 47 | } 48 | 49 | /** 50 | * The type Logic ds meta builder. 51 | */ 52 | public static final class LogicDsMetaBuilder { 53 | private String datasourceId; 54 | private ConnectionSubspaceTypeEnum expectedSubspaceType; 55 | private String subspace; 56 | private String table_prefix; 57 | 58 | private Callable createFunc; 59 | 60 | private LogicDsMetaBuilder() { 61 | } 62 | 63 | /** 64 | * A logic ds meta logic ds meta builder. 65 | * 66 | * @return the logic ds meta builder 67 | */ 68 | public static LogicDsMetaBuilder aLogicDsMeta() { 69 | return new LogicDsMetaBuilder(); 70 | } 71 | 72 | /** 73 | * 数据源id,当已注册数据源中存在相同id时会复用数据源,并且会触发数据源升级回调方法如果存在的话。 74 | * 使用同一个数据源id的不同逻辑数据源可以设置不同的subspace子空间。如mysql支持同一个连接使用过程中切换数据库 75 | * 76 | * @param datasourceId the datasource id 77 | * @return the logic ds meta builder 78 | */ 79 | public LogicDsMetaBuilder datasourceId(String datasourceId) { 80 | this.datasourceId = datasourceId; 81 | return this; 82 | } 83 | 84 | /** 85 | * 设置期望的子空间类型(不是所有的数据库都支持同一个连接进行切换) 86 | * 主要作用在于当用户期望和软件支持不匹配能快速失败及时报错 87 | * 88 | * @param expectedSubspaceType the expected subspace type 89 | * @return the logic ds meta builder 90 | */ 91 | public LogicDsMetaBuilder expectedSubspaceType(ConnectionSubspaceTypeEnum expectedSubspaceType) { 92 | this.expectedSubspaceType = expectedSubspaceType; 93 | return this; 94 | } 95 | 96 | /** 97 | * 当逻辑数据源使用时将连接切换到指定的子空间。子空间的名称。仅当数据库类型支持子空间时有效 98 | * nullable 99 | * @param subspace the subspace 100 | * @return the logic ds meta builder 101 | */ 102 | public LogicDsMetaBuilder subspace(String subspace) { 103 | this.subspace = subspace; 104 | return this; 105 | } 106 | 107 | /** 108 | * Table prefix logic ds meta builder. 109 | * 110 | * @param table_prefix the table prefix 111 | * @return the logic ds meta builder 112 | */ 113 | public LogicDsMetaBuilder table_prefix(String table_prefix) { 114 | this.table_prefix = table_prefix; 115 | return this; 116 | } 117 | 118 | /** 119 | * 当数据源id不存在时,使用此回调创建新的数据源 120 | * 121 | * @param createFunc the create func 122 | * @return the logic ds meta builder 123 | */ 124 | public LogicDsMetaBuilder createFunc(Callable createFunc) { 125 | this.createFunc = createFunc; 126 | return this; 127 | } 128 | 129 | /** 130 | * Build logic ds meta. 131 | * 132 | * @return the logic ds meta 133 | */ 134 | public LogicDsMeta build() { 135 | Asserts.notNull(datasourceId, "datasourceId"); 136 | LogicDsMeta logicDsMeta = new LogicDsMeta(); 137 | logicDsMeta.setDatasourceId(datasourceId); 138 | logicDsMeta.setExpectedSubspaceType(expectedSubspaceType); 139 | logicDsMeta.setSubspace(subspace); 140 | logicDsMeta.setTable_prefix(table_prefix); 141 | logicDsMeta.setCreateFunc(createFunc); 142 | return logicDsMeta; 143 | } 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /mybatis-sqlhelper/src/main/java/io/github/heykb/sqlhelper/dynamicdatasource/PrimaryDatasource.java: -------------------------------------------------------------------------------- 1 | package io.github.heykb.sqlhelper.dynamicdatasource; 2 | 3 | import io.github.heykb.sqlhelper.config.SqlHelperException; 4 | 5 | import javax.sql.DataSource; 6 | import java.sql.Connection; 7 | import java.sql.SQLException; 8 | 9 | /** 10 | * The type Primary datasource. 11 | */ 12 | public class PrimaryDatasource extends SimpleProxyDatasource { 13 | private String primaryDsInitialSubspace; 14 | 15 | /** 16 | * Instantiates a new Primary datasource. 17 | * 18 | * @param dataSource the data source 19 | */ 20 | public PrimaryDatasource(DataSource dataSource) { 21 | super(dataSource); 22 | init(); 23 | } 24 | 25 | private void init() { 26 | try (Connection connection = super.getConnection()) { 27 | this.primaryDsInitialSubspace = SupportedConnectionSubspaceChange.getCurrentSubspaceIfSupport(connection, null); 28 | } catch (SQLException e) { 29 | throw new SqlHelperException(e); 30 | } 31 | } 32 | 33 | @Override 34 | public Connection getConnection() throws SQLException { 35 | Connection connection = super.getConnection(); 36 | SupportedConnectionSubspaceChange.changeSubspaceIfSupport(connection, primaryDsInitialSubspace,null); 37 | return connection; 38 | } 39 | 40 | @Override 41 | public Connection getConnection(String username, String password) throws SQLException { 42 | Connection connection = super.getConnection(username, password); 43 | SupportedConnectionSubspaceChange.changeSubspaceIfSupport(connection, primaryDsInitialSubspace,null); 44 | return connection; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /mybatis-sqlhelper/src/main/java/io/github/heykb/sqlhelper/dynamicdatasource/SimpleProxyDatasource.java: -------------------------------------------------------------------------------- 1 | package io.github.heykb.sqlhelper.dynamicdatasource; 2 | 3 | import javax.sql.DataSource; 4 | import java.io.PrintWriter; 5 | import java.sql.Connection; 6 | import java.sql.SQLException; 7 | import java.sql.SQLFeatureNotSupportedException; 8 | import java.util.logging.Logger; 9 | 10 | /** 11 | * The type Simple proxy datasource. 12 | */ 13 | public class SimpleProxyDatasource implements DataSource { 14 | /** 15 | * The Data source. 16 | */ 17 | protected DataSource dataSource; 18 | 19 | /** 20 | * Instantiates a new Simple proxy datasource. 21 | * 22 | * @param dataSource the data source 23 | */ 24 | public SimpleProxyDatasource(DataSource dataSource) { 25 | this.dataSource = dataSource; 26 | } 27 | 28 | @Override 29 | public Connection getConnection() throws SQLException { 30 | return dataSource.getConnection(); 31 | } 32 | 33 | @Override 34 | public Connection getConnection(String username, String password) throws SQLException { 35 | return dataSource.getConnection(username, password); 36 | } 37 | 38 | @Override 39 | public T unwrap(Class iface) throws SQLException { 40 | return dataSource.unwrap(iface); 41 | } 42 | 43 | @Override 44 | public boolean isWrapperFor(Class iface) throws SQLException { 45 | return dataSource.isWrapperFor(iface); 46 | } 47 | 48 | @Override 49 | public PrintWriter getLogWriter() throws SQLException { 50 | return dataSource.getLogWriter(); 51 | } 52 | 53 | @Override 54 | public void setLogWriter(PrintWriter out) throws SQLException { 55 | dataSource.setLogWriter(out); 56 | } 57 | 58 | @Override 59 | public int getLoginTimeout() throws SQLException { 60 | return dataSource.getLoginTimeout(); 61 | } 62 | 63 | @Override 64 | public void setLoginTimeout(int seconds) throws SQLException { 65 | dataSource.setLoginTimeout(seconds); 66 | } 67 | 68 | @Override 69 | public Logger getParentLogger() throws SQLFeatureNotSupportedException { 70 | return dataSource.getParentLogger(); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /mybatis-sqlhelper/src/main/java/io/github/heykb/sqlhelper/dynamicdatasource/SqlHelperDsContextHolder.java: -------------------------------------------------------------------------------- 1 | package io.github.heykb.sqlhelper.dynamicdatasource; 2 | 3 | 4 | import org.apache.ibatis.logging.Log; 5 | import org.apache.ibatis.logging.LogFactory; 6 | 7 | import java.util.Stack; 8 | import java.util.concurrent.Callable; 9 | 10 | /** 11 | * The type Sql helper ds context holder. 12 | */ 13 | public class SqlHelperDsContextHolder { 14 | private static final Log log = LogFactory.getLog(SqlHelperDsContextHolder.class); 15 | private static final ThreadLocal> CONTEXTHOLDER = new ThreadLocal<>(); 16 | 17 | /** 18 | * 切换数据源 19 | * 20 | * @param logicName 数据源名称,null代表默认数据源 21 | */ 22 | public static void switchTo(String logicName) { 23 | Stack switchStack = CONTEXTHOLDER.get(); 24 | if(switchStack == null){ 25 | switchStack = new Stack<>(); 26 | CONTEXTHOLDER.set(switchStack); 27 | } 28 | switchStack.push(logicName); 29 | } 30 | 31 | /** 32 | * 退出当前数据源,会返回到上一次设置的值 33 | */ 34 | public static void backToLast(){ 35 | Stack switchStack = CONTEXTHOLDER.get(); 36 | if(switchStack!=null && !switchStack.empty()){ 37 | switchStack.pop(); 38 | } 39 | } 40 | 41 | /** 42 | * Clear. 43 | */ 44 | public static void clear() { 45 | CONTEXTHOLDER.remove(); 46 | } 47 | 48 | 49 | /** 50 | * Get string. 51 | * 52 | * @return the string 53 | */ 54 | public static String get() { 55 | Stack switchStack = CONTEXTHOLDER.get(); 56 | if(switchStack!=null && !switchStack.empty()){ 57 | return switchStack.peek(); 58 | } 59 | return null; 60 | } 61 | 62 | public static R executeOn(String datasourceName, Callable callable) { 63 | switchTo(datasourceName); 64 | try { 65 | R re = callable.call(); 66 | return re; 67 | } catch (Exception e) { 68 | throw new RuntimeException(e); 69 | }finally { 70 | backToLast(); 71 | } 72 | } 73 | 74 | 75 | } 76 | -------------------------------------------------------------------------------- /mybatis-sqlhelper/src/main/java/io/github/heykb/sqlhelper/dynamicdatasource/SqlHelperDsManager.java: -------------------------------------------------------------------------------- 1 | package io.github.heykb.sqlhelper.dynamicdatasource; 2 | 3 | 4 | import io.github.heykb.sqlhelper.config.SqlHelperException; 5 | 6 | import javax.sql.DataSource; 7 | import java.util.List; 8 | 9 | public interface SqlHelperDsManager { 10 | 11 | void put(String logicName, LogicDsMeta dsMeta); 12 | 13 | /** 14 | * 移除某个逻辑数据源 15 | * @param logicName 16 | * @throws SqlHelperException 当logicName不存在 17 | * @return 当对应的数据源id没有被其他逻辑数据源引用时,从管理中删除的Datasource对象。否则返回null 18 | */ 19 | DataSource remove(String logicName); 20 | 21 | boolean contains(String logicName); 22 | 23 | boolean containsId(String dsId); 24 | 25 | DataSource getByName(String logicName); 26 | 27 | DataSource getById(String dsId); 28 | 29 | 30 | LogicDsMeta getLogicDsMeta(String switchedDsName); 31 | 32 | 33 | List all(); 34 | 35 | List allDatasourceIds(); 36 | } 37 | -------------------------------------------------------------------------------- /mybatis-sqlhelper/src/main/java/io/github/heykb/sqlhelper/dynamicdatasource/SqlHelperDynamicDataSourceProxy.java: -------------------------------------------------------------------------------- 1 | package io.github.heykb.sqlhelper.dynamicdatasource; 2 | 3 | import io.github.heykb.sqlhelper.config.SqlHelperException; 4 | import org.apache.ibatis.logging.Log; 5 | import org.apache.ibatis.logging.LogFactory; 6 | 7 | import javax.sql.DataSource; 8 | import java.io.PrintWriter; 9 | import java.sql.Connection; 10 | import java.sql.SQLException; 11 | import java.sql.SQLFeatureNotSupportedException; 12 | import java.util.function.Function; 13 | import java.util.logging.Logger; 14 | 15 | /** 16 | * The type Sql helper dynamic data source proxy. 17 | */ 18 | public class SqlHelperDynamicDataSourceProxy implements DataSource { 19 | private static final Log log = LogFactory.getLog(SqlHelperDynamicDataSourceProxy.class); 20 | private SqlHelperDsManager sqlHelperDsManager; 21 | 22 | public SqlHelperDynamicDataSourceProxy(SqlHelperDsManager sqlHelperDsManager) { 23 | this.sqlHelperDsManager = sqlHelperDsManager; 24 | } 25 | 26 | @Override 27 | public Connection getConnection() throws SQLException { 28 | String switchedDsName = SqlHelperDsContextHolder.get(); 29 | log.warn(Thread.currentThread().getName() + "线程使用"+(switchedDsName==null?"主":switchedDsName)+"数据源"); 30 | DataSource dataSource = getDatasource(); 31 | LogicDsMeta logicDsMeta = sqlHelperDsManager.getLogicDsMeta(switchedDsName); 32 | Connection connection = dataSource.getConnection(); 33 | if(logicDsMeta.getSubspace() != null){ 34 | SupportedConnectionSubspaceChange.changeSubspaceIfSupport(connection, logicDsMeta.getSubspace(),logicDsMeta.getExpectedSubspaceType()); 35 | } 36 | return connection; 37 | } 38 | 39 | 40 | @Override 41 | public Connection getConnection(String username, String password) throws SQLException { 42 | String switchedDsName = SqlHelperDsContextHolder.get(); 43 | DataSource dataSource = getDatasource(); 44 | LogicDsMeta logicDsMeta = sqlHelperDsManager.getLogicDsMeta(switchedDsName); 45 | Connection connection = dataSource.getConnection(username, password); 46 | if (logicDsMeta.getSubspace() != null) { 47 | SupportedConnectionSubspaceChange.changeSubspaceIfSupport(connection, logicDsMeta.getSubspace(), logicDsMeta.getExpectedSubspaceType()); 48 | } 49 | return connection; 50 | } 51 | 52 | /** 53 | * Gets datasource. 54 | * 55 | * @return the datasource 56 | */ 57 | protected DataSource getDatasource() { 58 | String switchedDsName = SqlHelperDsContextHolder.get(); 59 | DataSource dataSource = sqlHelperDsManager.getByName(switchedDsName); 60 | if(dataSource == null){ 61 | throw new SqlHelperException("逻辑数据源不存在:"+switchedDsName); 62 | } 63 | return dataSource; 64 | } 65 | 66 | @Override 67 | public T unwrap(Class iface) throws SQLException { 68 | return getDatasource().unwrap(iface); 69 | } 70 | 71 | @Override 72 | public boolean isWrapperFor(Class iface) throws SQLException { 73 | return getDatasource().isWrapperFor(iface); 74 | } 75 | 76 | @Override 77 | public PrintWriter getLogWriter() throws SQLException { 78 | return getDatasource().getLogWriter(); 79 | } 80 | 81 | @Override 82 | public void setLogWriter(PrintWriter out) throws SQLException { 83 | getDatasource().setLogWriter(out); 84 | } 85 | 86 | @Override 87 | public void setLoginTimeout(int seconds) throws SQLException { 88 | getDatasource().setLoginTimeout(seconds); 89 | } 90 | 91 | @Override 92 | public int getLoginTimeout() throws SQLException { 93 | return getDatasource().getLoginTimeout(); 94 | } 95 | 96 | @Override 97 | public Logger getParentLogger() throws SQLFeatureNotSupportedException { 98 | return getDatasource().getParentLogger(); 99 | } 100 | 101 | public SqlHelperDsManager getSqlHelperDsManager() { 102 | return sqlHelperDsManager; 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /mybatis-sqlhelper/src/main/java/io/github/heykb/sqlhelper/dynamicdatasource/SupportedConnectionSubspaceChange.java: -------------------------------------------------------------------------------- 1 | package io.github.heykb.sqlhelper.dynamicdatasource; 2 | 3 | import com.alibaba.druid.DbType; 4 | import io.github.heykb.sqlhelper.config.SqlHelperAutoDbType; 5 | import io.github.heykb.sqlhelper.config.SqlHelperException; 6 | import org.apache.ibatis.logging.Log; 7 | import org.apache.ibatis.logging.LogFactory; 8 | 9 | import java.sql.Connection; 10 | import java.sql.SQLException; 11 | import java.sql.Statement; 12 | import java.sql.Wrapper; 13 | import java.util.HashMap; 14 | import java.util.Map; 15 | 16 | /** 17 | * The type Supported connection subspace change. 18 | */ 19 | public class SupportedConnectionSubspaceChange { 20 | 21 | private static final Log log = LogFactory.getLog(SupportedConnectionSubspaceChange.class); 22 | 23 | private static final Map DB_TO_NAMESPACE_TYPE = new HashMap<>(3); 24 | 25 | static { 26 | DB_TO_NAMESPACE_TYPE.put(DbType.postgresql, ConnectionSubspaceTypeEnum.SCHEMA); 27 | DB_TO_NAMESPACE_TYPE.put(DbType.mysql, ConnectionSubspaceTypeEnum.DATABASE); 28 | DB_TO_NAMESPACE_TYPE.put(DbType.sqlserver, ConnectionSubspaceTypeEnum.DATABASE); 29 | } 30 | 31 | /** 32 | * Change subspace if supprt connection subspace type. 33 | * 34 | * @param connection the connection 35 | * @param subspace the subspace 36 | * @return the connection subspace type 37 | * @throws SQLException the sql exception 38 | */ 39 | public static ConnectionSubspaceTypeEnum changeSubspaceIfSupport(Connection connection, String subspace, ConnectionSubspaceTypeEnum expectedType) throws SQLException { 40 | if (subspace == null) { 41 | return null; 42 | } 43 | log.warn(Thread.currentThread().getName() + "线程连接subspace切换到" + subspace); 44 | connection = unwrapConnection(connection); 45 | ConnectionSubspaceTypeEnum re = getSupportedSubspaceType(connection, expectedType); 46 | switch (re) { 47 | case SCHEMA: 48 | connection.setSchema(subspace); 49 | break; 50 | case DATABASE: 51 | connection.setCatalog(subspace); 52 | break; 53 | } 54 | log.warn("连接subspace类型为" + re.name()); 55 | return re; 56 | } 57 | 58 | /** 59 | * Gets current subspace if support change. 60 | * 61 | * @param connection the connection 62 | * @return the current subspace if support change 63 | * @throws SQLException the sql exception 64 | */ 65 | public static String getCurrentSubspaceIfSupport(Connection connection, ConnectionSubspaceTypeEnum expectedType) throws SQLException { 66 | String subspace = null; 67 | ConnectionSubspaceTypeEnum subspaceType = getSupportedSubspaceType(connection, expectedType); 68 | connection = unwrapConnection(connection); 69 | // 创建语句 70 | switch (subspaceType) { 71 | case SCHEMA: 72 | return connection.getSchema(); 73 | case DATABASE: 74 | return connection.getCatalog(); 75 | } 76 | return subspace; 77 | } 78 | 79 | public static ConnectionSubspaceTypeEnum getSupportedSubspaceType(Connection connection, ConnectionSubspaceTypeEnum expectedType) throws SQLException { 80 | DbType dbType = SqlHelperAutoDbType.fromJdbcUrl(connection.getMetaData().getURL()); 81 | ConnectionSubspaceTypeEnum subspaceType = DB_TO_NAMESPACE_TYPE.get(dbType); 82 | if (subspaceType == null) { 83 | subspaceType = ConnectionSubspaceTypeEnum.NOT_SUPPORT; 84 | } 85 | if (expectedType!=null && expectedType != subspaceType) { 86 | throw new SqlHelperException(dbType.name() + "数据库连接支持的subspace类型为" + subspaceType + "。但是LogicDsMeta配置中期望的类型是" + expectedType.name()); 87 | } 88 | return subspaceType; 89 | } 90 | 91 | public static ConnectionSubspaceTypeEnum getSupportedSubspaceType(DbType dbType) { 92 | return DB_TO_NAMESPACE_TYPE.get(dbType); 93 | } 94 | 95 | 96 | private static Connection unwrapConnection(Connection connection) throws SQLException { 97 | while (true){ 98 | if(connection instanceof Wrapper){ 99 | Connection unwrapConn = connection.unwrap(Connection.class); 100 | if(unwrapConn!=null && unwrapConn!=connection){ 101 | connection = unwrapConn; 102 | continue; 103 | } 104 | } 105 | return connection; 106 | } 107 | } 108 | 109 | } 110 | -------------------------------------------------------------------------------- /mybatis-sqlhelper/src/main/java/io/github/heykb/sqlhelper/handler/ColumnFilterInfoHandler.java: -------------------------------------------------------------------------------- 1 | package io.github.heykb.sqlhelper.handler; 2 | 3 | import org.apache.ibatis.mapping.SqlCommandType; 4 | 5 | import java.util.Set; 6 | 7 | /** 8 | * The interface Column filter info handler. 9 | * 10 | * @author heykb 11 | */ 12 | public interface ColumnFilterInfoHandler { 13 | /** 14 | * Filter columns set. 15 | * 16 | * @return the set 17 | */ 18 | Set getFilterColumns(); 19 | 20 | 21 | /** 22 | * 设置mapperId方法级别过滤逻辑 23 | * 24 | * @param mapperId the mapper id 25 | * @return boolean boolean 26 | */ 27 | default boolean checkMapperId(String mapperId) { 28 | return true; 29 | } 30 | 31 | boolean checkTableName(String tableName); 32 | 33 | default boolean checkCommandType(SqlCommandType commandType){ 34 | return SqlCommandType.SELECT == commandType || SqlCommandType.UPDATE == commandType; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /mybatis-sqlhelper/src/main/java/io/github/heykb/sqlhelper/handler/InjectColumnInfoHandler.java: -------------------------------------------------------------------------------- 1 | package io.github.heykb.sqlhelper.handler; 2 | 3 | import com.alibaba.druid.DbType; 4 | import com.alibaba.druid.sql.SQLUtils; 5 | import com.alibaba.druid.sql.ast.SQLExpr; 6 | import io.github.heykb.sqlhelper.utils.CommonUtils; 7 | import org.apache.ibatis.mapping.SqlCommandType; 8 | 9 | import java.util.HashSet; 10 | import java.util.Map; 11 | import java.util.Set; 12 | 13 | /** 14 | * 代表一条自动注入配置,如配置字段名称、字段值、注入类型等 15 | * 16 | * @author heykb 17 | */ 18 | public interface InjectColumnInfoHandler { 19 | 20 | /** 21 | * 注入到条件查询中 22 | */ 23 | int CONDITION = 1; 24 | /** 25 | * 注入插入内容中 26 | */ 27 | int INSERT = 1<<1; 28 | /** 29 | * 注入到更新内容中 30 | */ 31 | int UPDATE = 1<<2; 32 | 33 | /** 34 | * 注入到查询内容中 35 | */ 36 | int SELECT_ITEM=1<<3; 37 | 38 | /** 39 | * 设置注入字段名称 40 | * 41 | * @return column name 42 | */ 43 | String getColumnName(); 44 | 45 | /** 46 | * 设置注入字段的值,可以是值、方法甚至是子查询语句 47 | * 48 | * @return value 49 | */ 50 | String getValue(); 51 | 52 | 53 | /** 54 | * 设置注入类型 CONDITION|INSERT|UPDATE 55 | * 56 | * @return inject types 57 | */ 58 | int getInjectTypes(); 59 | 60 | /** 61 | * Op string. 62 | * 63 | * @return the string 64 | */ 65 | default String op(){ 66 | return "="; 67 | } 68 | 69 | /** 70 | * 当update和insert注入时已存在该字段时,true覆盖,false跳过。condition注入不做判断直接新增条件 71 | * 72 | * @return boolean 73 | */ 74 | default boolean isExistOverride() { 75 | return true; 76 | } 77 | 78 | 79 | /** 80 | * 设置表级别过滤逻辑 81 | * 82 | * @param tableName the table name 83 | * @return boolean 84 | */ 85 | default boolean checkTableName(String tableName){ 86 | return true; 87 | } 88 | 89 | /** 90 | * 设置mapperId方法级别过滤逻辑 91 | * 92 | * @param mapperId the mapper id 93 | * @return boolean 94 | */ 95 | default boolean checkMapperId(String mapperId){ 96 | return true; 97 | } 98 | 99 | 100 | /** 101 | * 设置sql命令类型过滤逻辑 102 | * @param commandType 103 | * @return 104 | */ 105 | default boolean checkCommandType(SqlCommandType commandType){ 106 | Set commandTypes = new HashSet<>(); 107 | if ((getInjectTypes() & CONDITION) > 0) { 108 | return true; 109 | } 110 | if ((getInjectTypes() & INSERT) > 0) { 111 | commandTypes.add(SqlCommandType.INSERT); 112 | } 113 | if ((getInjectTypes() & UPDATE) > 0) { 114 | commandTypes.add(SqlCommandType.UPDATE); 115 | } 116 | return commandTypes.contains(commandType); 117 | } 118 | 119 | 120 | /** 121 | * To sql expr sql expr. 122 | * 123 | * @param dbType the db type 124 | * @return the sql expr 125 | */ 126 | default SQLExpr toSQLExpr(DbType dbType){ 127 | SQLExpr sqlExpr = SQLUtils.toSQLExpr((String) getValue(),dbType); 128 | return sqlExpr; 129 | } 130 | 131 | 132 | default SQLExpr toConditionSQLExpr(String tableAlias, DbType dbType, Map columnAliasMap, boolean isMapUnderscoreToCamelCase){ 133 | if((getInjectTypes() & CONDITION)==0){ 134 | throw new UnsupportedOperationException("只有CONDITION注入支持该方法"); 135 | } 136 | String columnName = CommonUtils.adaptePropertyName(getColumnName(), columnAliasMap, isMapUnderscoreToCamelCase); 137 | String aliasFieldName = CommonUtils.isEmpty(tableAlias) ? columnName : tableAlias + "." + columnName; 138 | StringBuilder conditionSql = new StringBuilder(aliasFieldName); 139 | conditionSql.append(" ").append(op()).append(" ").append(getValue()); 140 | return SQLUtils.toSQLExpr(conditionSql.toString()); 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /mybatis-sqlhelper/src/main/java/io/github/heykb/sqlhelper/handler/abstractor/BinaryConditionInjectInfoHandler.java: -------------------------------------------------------------------------------- 1 | package io.github.heykb.sqlhelper.handler.abstractor; 2 | 3 | import com.alibaba.druid.DbType; 4 | import com.alibaba.druid.sql.ast.SQLExpr; 5 | import com.alibaba.druid.sql.ast.expr.SQLBinaryOpExpr; 6 | import io.github.heykb.sqlhelper.handler.InjectColumnInfoHandler; 7 | import io.github.heykb.sqlhelper.utils.CommonUtils; 8 | 9 | import java.util.Map; 10 | 11 | /** 12 | * 复杂条件注入 13 | * 14 | * @author heykb 15 | */ 16 | public abstract class BinaryConditionInjectInfoHandler implements InjectColumnInfoHandler { 17 | 18 | 19 | @Override 20 | public int getInjectTypes() { 21 | return CONDITION; 22 | } 23 | /** 24 | * Gets left condition inject info. 25 | * 26 | * @return the left condition inject info 27 | */ 28 | abstract public InjectColumnInfoHandler getLeftConditionInjectInfo(); 29 | 30 | /** 31 | * Gets right condition inject info. 32 | * 33 | * @return the right condition inject info 34 | */ 35 | abstract public InjectColumnInfoHandler getRightConditionInjectInfo(); 36 | 37 | @Override 38 | public String op() { 39 | return "and"; 40 | } 41 | 42 | @Override 43 | public SQLExpr toConditionSQLExpr(String tableAlias, DbType dbType, Map columnAliasMap, boolean isMapUnderscoreToCamelCase) { 44 | SQLExpr left = null; 45 | SQLExpr right = null; 46 | if(getLeftConditionInjectInfo()!=null){ 47 | left = getLeftConditionInjectInfo().toConditionSQLExpr(tableAlias, dbType, columnAliasMap, isMapUnderscoreToCamelCase); 48 | } 49 | if(getRightConditionInjectInfo()!=null){ 50 | right = getRightConditionInjectInfo().toConditionSQLExpr(tableAlias, dbType, columnAliasMap, isMapUnderscoreToCamelCase); 51 | } 52 | if(left != null && right !=null){ 53 | return new SQLBinaryOpExpr(left, right, CommonUtils.convert(op())); 54 | }else if(left == null){ 55 | return right; 56 | }else if(right == null){ 57 | return left; 58 | } 59 | return null; 60 | } 61 | 62 | 63 | 64 | @Override 65 | public String getColumnName() { 66 | return null; 67 | } 68 | 69 | @Override 70 | public String getValue() { 71 | return null; 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /mybatis-sqlhelper/src/main/java/io/github/heykb/sqlhelper/handler/abstractor/LogicDeleteInfoHandler.java: -------------------------------------------------------------------------------- 1 | package io.github.heykb.sqlhelper.handler.abstractor; 2 | 3 | import io.github.heykb.sqlhelper.handler.InjectColumnInfoHandler; 4 | 5 | /** 6 | * 实现InjectColumnInfoHandler自动注入配置接口,代表逻辑删除自动注入配置 7 | * 配置逻辑删除的字段名称、字段值、以及逻辑删除语法样例(其中表名和where条件会在运行中被替换) 8 | * 使用时继承该类,并注入为一个bean 9 | * 10 | * @author heykb sqlDemo: update xx set status = false where id = 'xx' columnName: status notDeleteValue: true 11 | */ 12 | public abstract class LogicDeleteInfoHandler implements InjectColumnInfoHandler { 13 | /** 14 | * 设置逻辑删除实例:update xx set status = false 15 | * (其中表名和where条件会在运行中被替换) 16 | * 17 | * @return sql demo 18 | */ 19 | public abstract String getDeleteSqlDemo(); 20 | 21 | /** 22 | * 设置正常未被删除时,逻辑删除字段的value 23 | * 24 | * @return not deleted value 25 | */ 26 | public abstract String getNotDeletedValue(); 27 | 28 | @Override 29 | public String getValue() { 30 | return getNotDeletedValue(); 31 | } 32 | 33 | /** 34 | * 为所有子查询添加逻辑删除条件 35 | * @return 36 | */ 37 | @Override 38 | public int getInjectTypes() { 39 | return CONDITION; 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /mybatis-sqlhelper/src/main/java/io/github/heykb/sqlhelper/handler/abstractor/TenantInfoHandler.java: -------------------------------------------------------------------------------- 1 | package io.github.heykb.sqlhelper.handler.abstractor; 2 | 3 | 4 | import io.github.heykb.sqlhelper.handler.InjectColumnInfoHandler; 5 | 6 | /** 7 | * 实现InjectColumnInfoHandler自动注入配置接口,代表多租户自动注入配置 8 | * 配置多租户的字段名称、租户value获取方式 9 | * 使用时继承该类,并注入为一个bean 10 | * 11 | * @author heykb 12 | */ 13 | public abstract class TenantInfoHandler implements InjectColumnInfoHandler { 14 | /** 15 | * 设置代表租户字段名称 16 | * 17 | * @return tenant id column 18 | */ 19 | public abstract String getTenantIdColumn(); 20 | 21 | /** 22 | * 当前租户value获取方式 23 | * 24 | * @return tenant id 25 | */ 26 | public abstract String getTenantId(); 27 | 28 | @Override 29 | public String getColumnName() { 30 | return getTenantIdColumn(); 31 | } 32 | 33 | @Override 34 | public String getValue() { 35 | return getTenantId(); 36 | } 37 | 38 | @Override 39 | public boolean isExistOverride() { 40 | return false; 41 | } 42 | 43 | /** 44 | * 为所有子查询添加租户条件 为所有插入语句注入租户id 45 | * @return 46 | */ 47 | @Override 48 | public int getInjectTypes() { 49 | return CONDITION|INSERT; 50 | } 51 | 52 | } 53 | -------------------------------------------------------------------------------- /mybatis-sqlhelper/src/main/java/io/github/heykb/sqlhelper/handler/dynamic/AbstractDynamicFindColumnFilterHandler.java: -------------------------------------------------------------------------------- 1 | package io.github.heykb.sqlhelper.handler.dynamic; 2 | 3 | import io.github.heykb.sqlhelper.handler.ColumnFilterInfoHandler; 4 | 5 | import java.util.List; 6 | 7 | /** 8 | * The type Default dynamic find column filter handler. 9 | * 10 | * @param 数据权限信息 11 | * @author heykb 12 | */ 13 | public abstract class AbstractDynamicFindColumnFilterHandler implements DynamicFindColumnFilterHandler{ 14 | /** 15 | * 编写将获取到的数据权限信息解析成handlers逻辑 16 | * 17 | * @param o the o 18 | * @return the list 19 | */ 20 | abstract public List parse(T o); 21 | 22 | /** 23 | * 编写从上下文获取用户的数据权限信息逻辑 24 | * 25 | * @return the permission info 26 | */ 27 | abstract public T getPermissionInfo(); 28 | 29 | @Override 30 | public List findColumnFilterHandlers() { 31 | return parse(getPermissionInfo()); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /mybatis-sqlhelper/src/main/java/io/github/heykb/sqlhelper/handler/dynamic/AbstractDynamicFindInjectInfoHandler.java: -------------------------------------------------------------------------------- 1 | package io.github.heykb.sqlhelper.handler.dynamic; 2 | 3 | import io.github.heykb.sqlhelper.handler.InjectColumnInfoHandler; 4 | 5 | import java.util.List; 6 | 7 | /** 8 | * The type Default dynamic find column filter handler. 9 | * 10 | * @param the type parameter 11 | * @author heykb 12 | */ 13 | public abstract class AbstractDynamicFindInjectInfoHandler implements DynamicFindInjectInfoHandler{ 14 | 15 | /** 16 | * 编写将获取到的数据权限信息解析成handlers逻辑 17 | * 18 | * @param o the o 19 | * @return the list 20 | */ 21 | abstract public List parse(T o); 22 | 23 | /** 24 | * 编写从上下文获取用户的数据权限信息逻辑 25 | * 26 | * @return the permission info 27 | */ 28 | abstract public T getPermissionInfo(); 29 | 30 | @Override 31 | public List findInjectInfoHandlers() { 32 | return parse(getPermissionInfo()); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /mybatis-sqlhelper/src/main/java/io/github/heykb/sqlhelper/handler/dynamic/DynamicFindColumnFilterHandler.java: -------------------------------------------------------------------------------- 1 | package io.github.heykb.sqlhelper.handler.dynamic; 2 | 3 | import io.github.heykb.sqlhelper.handler.ColumnFilterInfoHandler; 4 | 5 | import java.util.List; 6 | 7 | /** 8 | * @author heykb 9 | */ 10 | public interface DynamicFindColumnFilterHandler { 11 | List findColumnFilterHandlers(); 12 | default boolean checkMapperIds(String mapperId){ 13 | return true; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /mybatis-sqlhelper/src/main/java/io/github/heykb/sqlhelper/handler/dynamic/DynamicFindInjectInfoHandler.java: -------------------------------------------------------------------------------- 1 | package io.github.heykb.sqlhelper.handler.dynamic; 2 | 3 | import io.github.heykb.sqlhelper.handler.InjectColumnInfoHandler; 4 | 5 | import java.util.List; 6 | 7 | /** 8 | * @author heykb 9 | */ 10 | public interface DynamicFindInjectInfoHandler { 11 | List findInjectInfoHandlers(); 12 | 13 | default boolean checkMapperIds(String mapperId){ 14 | return true; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /mybatis-sqlhelper/src/main/java/io/github/heykb/sqlhelper/helper/MySchemaStatVisitor.java: -------------------------------------------------------------------------------- 1 | package io.github.heykb.sqlhelper.helper; 2 | 3 | import com.alibaba.druid.sql.ast.statement.SQLExprTableSource; 4 | import com.alibaba.druid.sql.visitor.SchemaStatVisitor; 5 | 6 | import java.util.HashMap; 7 | import java.util.Map; 8 | 9 | public class MySchemaStatVisitor extends SchemaStatVisitor { 10 | private Map aliasTableMap = new HashMap<>(); 11 | @Override 12 | public void endVisit(SQLExprTableSource x) { 13 | super.endVisit(x); 14 | String alias = x.computeAlias(); 15 | if(alias!=null){ 16 | aliasTableMap.put(alias,x.getTableName()); 17 | } 18 | 19 | } 20 | public Map getAliasTableMap() { 21 | return aliasTableMap; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /mybatis-sqlhelper/src/main/java/io/github/heykb/sqlhelper/interceptor/ColumnFilterCursor.java: -------------------------------------------------------------------------------- 1 | package io.github.heykb.sqlhelper.interceptor; 2 | 3 | import io.github.heykb.sqlhelper.utils.CommonUtils; 4 | import org.apache.ibatis.cursor.Cursor; 5 | 6 | import java.util.*; 7 | import java.util.function.Consumer; 8 | 9 | public class ColumnFilterCursor extends SimpleProxyCursor { 10 | 11 | private Set ignoreColumns; 12 | private boolean isMapUnderscoreToCamelCase; 13 | 14 | 15 | public ColumnFilterCursor(Cursor target, Set ignoreColumns, boolean isMapUnderscoreToCamelCase) { 16 | super(target); 17 | this.ignoreColumns = ignoreColumns; 18 | this.isMapUnderscoreToCamelCase = isMapUnderscoreToCamelCase; 19 | } 20 | 21 | @Override 22 | public Iterator iterator() { 23 | return new ColumnFilterIterator(super.iterator()); 24 | } 25 | 26 | @Override 27 | public void forEach(Consumer action) { 28 | Objects.requireNonNull(action); 29 | for (T t : this) { 30 | action.accept(t); 31 | } 32 | } 33 | 34 | @Override 35 | public Spliterator spliterator() { 36 | return Spliterators.spliteratorUnknownSize(iterator(), 0); 37 | } 38 | 39 | 40 | class ColumnFilterIterator implements Iterator { 41 | private Iterator target; 42 | 43 | public ColumnFilterIterator(Iterator target) { 44 | this.target = target; 45 | } 46 | 47 | @Override 48 | public Object next() { 49 | Object re = target.next(); 50 | CommonUtils.filterColumns(re, ignoreColumns, isMapUnderscoreToCamelCase); 51 | return re; 52 | } 53 | 54 | public Iterator getTarget() { 55 | return target; 56 | } 57 | 58 | @Override 59 | public boolean hasNext() { 60 | return target.hasNext(); 61 | } 62 | 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /mybatis-sqlhelper/src/main/java/io/github/heykb/sqlhelper/interceptor/SimpleProxyCursor.java: -------------------------------------------------------------------------------- 1 | package io.github.heykb.sqlhelper.interceptor; 2 | 3 | 4 | import org.apache.ibatis.cursor.Cursor; 5 | 6 | import java.io.IOException; 7 | import java.util.Iterator; 8 | 9 | public class SimpleProxyCursor implements Cursor { 10 | 11 | private Cursor target; 12 | 13 | public SimpleProxyCursor(Cursor target) { 14 | this.target = target; 15 | } 16 | 17 | public Cursor getTarget() { 18 | return target; 19 | } 20 | 21 | @Override 22 | public boolean isOpen() { 23 | return target.isOpen(); 24 | } 25 | 26 | @Override 27 | public boolean isConsumed() { 28 | return target.isConsumed(); 29 | } 30 | 31 | @Override 32 | public int getCurrentIndex() { 33 | return target.getCurrentIndex(); 34 | } 35 | 36 | @Override 37 | public void close() throws IOException { 38 | target.close(); 39 | } 40 | 41 | @Override 42 | public Iterator iterator() { 43 | return target.iterator(); 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /mybatis-sqlhelper/src/main/java/io/github/heykb/sqlhelper/typeHandler/ColumnFilterTypeHandler.java: -------------------------------------------------------------------------------- 1 | package io.github.heykb.sqlhelper.typeHandler; 2 | 3 | import org.apache.ibatis.type.JdbcType; 4 | import org.apache.ibatis.type.TypeHandler; 5 | 6 | import java.sql.CallableStatement; 7 | import java.sql.PreparedStatement; 8 | import java.sql.ResultSet; 9 | import java.sql.SQLException; 10 | import java.util.Collections; 11 | import java.util.List; 12 | 13 | /** 14 | * @author heykb 15 | */ 16 | public class ColumnFilterTypeHandler implements TypeHandler { 17 | 18 | private TypeHandler typeHandler; 19 | private List removeIndex; 20 | 21 | public ColumnFilterTypeHandler(TypeHandler typeHandler, List removeIndex) { 22 | this.typeHandler = typeHandler; 23 | this.removeIndex = removeIndex; 24 | Collections.sort(removeIndex,Integer::compareTo); 25 | } 26 | 27 | /* 28 | 1 2 3 4 5 6 29 | 1 3 4 6 30 | 2 5 31 | */ 32 | @Override 33 | public void setParameter(PreparedStatement ps, int i, Object parameter, JdbcType jdbcType) throws SQLException { 34 | int j = 0; 35 | for(Integer item:removeIndex){ 36 | if(i < item){ 37 | break; 38 | }else if(i==item){ 39 | return; 40 | }else if(i > item){ 41 | ++j; 42 | } 43 | } 44 | typeHandler.setParameter(ps,i-j,parameter,jdbcType); 45 | } 46 | 47 | @Override 48 | public Object getResult(ResultSet rs, String columnName) throws SQLException { 49 | return typeHandler.getResult(rs,columnName); 50 | } 51 | 52 | @Override 53 | public Object getResult(ResultSet rs, int columnIndex) throws SQLException { 54 | return typeHandler.getResult(rs,columnIndex); 55 | } 56 | 57 | @Override 58 | public Object getResult(CallableStatement cs, int columnIndex) throws SQLException { 59 | return typeHandler.getResult(cs,columnIndex); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /mybatis-sqlhelper/src/main/java/io/github/heykb/sqlhelper/utils/Asserts.java: -------------------------------------------------------------------------------- 1 | package io.github.heykb.sqlhelper.utils; 2 | 3 | public class Asserts { 4 | public Asserts() { 5 | } 6 | 7 | public static void check(boolean expression, String message) { 8 | if (!expression) { 9 | throw new IllegalArgumentException(message); 10 | } 11 | } 12 | 13 | public static void check(boolean expression, String message, Object... args) { 14 | if (!expression) { 15 | throw new IllegalArgumentException(String.format(message, args)); 16 | } 17 | } 18 | 19 | public static void check(boolean expression, String message, Object arg) { 20 | if (!expression) { 21 | throw new IllegalArgumentException(String.format(message, arg)); 22 | } 23 | } 24 | 25 | public static void notNull(Object object, String name) { 26 | if (object == null) { 27 | throw new IllegalArgumentException(name + " is null"); 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /mybatis-sqlhelper/src/main/java/io/github/heykb/sqlhelper/utils/CommonUtils.java: -------------------------------------------------------------------------------- 1 | package io.github.heykb.sqlhelper.utils; 2 | 3 | import com.alibaba.druid.sql.ast.expr.SQLBinaryOperator; 4 | import com.google.common.base.CaseFormat; 5 | import org.apache.commons.collections.CollectionUtils; 6 | 7 | import java.lang.reflect.Array; 8 | import java.lang.reflect.Field; 9 | import java.util.*; 10 | 11 | /** 12 | * The type Common utils. 13 | * 14 | * @author heykb 15 | */ 16 | public class CommonUtils { 17 | /** 18 | * Convert sql binary operator. 19 | * 20 | * @param op the op 21 | * @return the sql binary operator 22 | */ 23 | public static SQLBinaryOperator convert(String op){ 24 | for (SQLBinaryOperator item:SQLBinaryOperator.values()){ 25 | if(item.getName().equalsIgnoreCase(op.trim())){ 26 | return item; 27 | } 28 | } 29 | throw new IllegalArgumentException(String.format("暂不支持%s操作",op)); 30 | } 31 | 32 | /** 33 | * Is primitive or wrap boolean. 34 | * 35 | * @param clazz the clazz 36 | * @return the boolean 37 | */ 38 | public static boolean isPrimitiveOrWrap(Class clazz){ 39 | try{ 40 | return clazz.isPrimitive() || ((Class)clazz.getField("TYPE").get(null)).isPrimitive(); 41 | }catch (Exception e){ 42 | return false; 43 | } 44 | 45 | } 46 | 47 | /** 48 | * Adapte property name string. 49 | * 50 | * @param originName the origin name 51 | * @param columnAliasMap the column alias map 52 | * @param isMapUnderscoreToCamelCase the is map underscore to camel case 53 | * @return the string 54 | */ 55 | public static String adaptePropertyName(String originName, Map columnAliasMap, boolean isMapUnderscoreToCamelCase) { 56 | String re = originName; 57 | if (originName != null) { 58 | if (columnAliasMap != null && columnAliasMap.containsKey(re)) { 59 | re = columnAliasMap.get(re); 60 | }else if(isMapUnderscoreToCamelCase){ 61 | if(Character.isUpperCase(re.charAt(0))){ 62 | re = CaseFormat.UPPER_CAMEL.to(CaseFormat.UPPER_UNDERSCORE,re); 63 | }else{ 64 | re = CaseFormat.LOWER_CAMEL.to(CaseFormat.LOWER_UNDERSCORE,re); 65 | } 66 | } 67 | } 68 | return re; 69 | } 70 | 71 | /** 72 | * Gets instance by class name. 73 | * 74 | * @param classNames the class names 75 | * @return the instance by class name 76 | * @throws ClassNotFoundException the class not found exception 77 | * @throws IllegalAccessException the illegal access exception 78 | * @throws InstantiationException the instantiation exception 79 | */ 80 | public static List getInstanceByClassName(String[] classNames) throws ClassNotFoundException, IllegalAccessException, InstantiationException { 81 | List re = new ArrayList(); 82 | if(classNames == null || classNames.length==0){ 83 | return re; 84 | } 85 | for(String item:classNames){ 86 | item = item.trim(); 87 | Object obj = Class.forName(item).newInstance(); 88 | re.add(obj); 89 | } 90 | return re; 91 | } 92 | 93 | 94 | /** 95 | * Filter columns. 96 | * 97 | * @param o the o 98 | * @param ignoreColumns the ignore columns 99 | * @param isMapUnderscoreToCamelCase the is map underscore to camel case 100 | */ 101 | public static void filterColumns(Object o, Set ignoreColumns, boolean isMapUnderscoreToCamelCase) { 102 | if (o == null || CollectionUtils.isEmpty(ignoreColumns)) { 103 | return; 104 | } 105 | if (CommonUtils.isPrimitiveOrWrap(o.getClass())) { 106 | return; 107 | } else if (o instanceof String) { 108 | return; 109 | } else if (o.getClass().isArray()) { 110 | int length = Array.getLength(o); 111 | for (int i = 0; i < length; i++) { 112 | filterColumns(Array.get(o, i), ignoreColumns, isMapUnderscoreToCamelCase); 113 | } 114 | } else if (Collection.class.isAssignableFrom(o.getClass())) { 115 | for (Object item : (Collection) o) { 116 | filterColumns(item, ignoreColumns, isMapUnderscoreToCamelCase); 117 | } 118 | } else if (Map.class.isAssignableFrom(o.getClass())) { 119 | // List removeKeys = new ArrayList<>(); 120 | Map map = (Map) o; 121 | Iterator> iterator = map.entrySet().iterator(); 122 | while(iterator.hasNext()){ 123 | if(ignoreColumns.contains(iterator.next().getKey().toLowerCase())){ 124 | iterator.remove(); 125 | } 126 | } 127 | // for (String key : map.keySet()) { 128 | // for (String column : ignoreColumns) { 129 | // if (ignoreColumns.contains(column)) { 130 | // removeKeys.add(key); 131 | // } 132 | // } 133 | // } 134 | // for (String key : removeKeys) { 135 | // map.remove(key); 136 | // } 137 | } else { 138 | Class clazz = o.getClass(); 139 | Field[] fields = clazz.getDeclaredFields(); 140 | for (Field field : fields) { 141 | field.setAccessible(true); 142 | for (String column : ignoreColumns) { 143 | boolean founded = false; 144 | String name = field.getName(); 145 | if (name.equals(column)) { 146 | founded = true; 147 | } else if (isMapUnderscoreToCamelCase && name.equalsIgnoreCase(column.replace("_", ""))) { 148 | founded = true; 149 | } 150 | if (founded) { 151 | try { 152 | field.set(o, null); 153 | } catch (IllegalAccessException e) { 154 | } 155 | } 156 | } 157 | } 158 | } 159 | } 160 | 161 | 162 | /** 163 | * Is empty boolean. 164 | * 165 | * @param str the str 166 | * @return the boolean 167 | */ 168 | public static boolean isEmpty(String str) { 169 | return (str == null || str.length() == 0); 170 | } 171 | 172 | 173 | /** 174 | * 字符串通配符匹配:支持? * 175 | * 176 | * @param pattern the pattern 通配符 177 | * @param str the str 被匹配字符串 178 | * @return the boolean 179 | */ 180 | public static boolean wildcardMatch(String pattern,String str){ 181 | int m = pattern.length(); 182 | int n = str.length(); 183 | boolean[][] dp = new boolean[m+1][n+1]; 184 | dp[0][0] = true; 185 | for(int i=0;ii,j;不使用*消耗掉ch->i,j+1 ;使用*消耗掉ch,但是*是二次使用ch->i+1,j 198 | dp[i+1][j+1] = dp[i][j+1] || dp[i][j] || dp[i+1][j]; 199 | }else{ 200 | if(p=='?' || ch==p){ 201 | dp[i+1][j+1] = dp[i][j]; 202 | }else{ 203 | dp[i+1][j+1] = false; 204 | } 205 | } 206 | } 207 | } 208 | return dp[m][n]; 209 | } 210 | 211 | 212 | } 213 | -------------------------------------------------------------------------------- /mybatis-sqlhelper/src/test/java/io/github/heykb/sqlhelper/test/BaseUtils.java: -------------------------------------------------------------------------------- 1 | package io.github.heykb.sqlhelper.test; 2 | 3 | import com.alibaba.druid.DbType; 4 | import io.github.heykb.sqlhelper.handler.ColumnFilterInfoHandler; 5 | import io.github.heykb.sqlhelper.handler.InjectColumnInfoHandler; 6 | import io.github.heykb.sqlhelper.handler.abstractor.LogicDeleteInfoHandler; 7 | import io.github.heykb.sqlhelper.interceptor.SqlHelperPlugin; 8 | import org.apache.ibatis.io.Resources; 9 | import org.apache.ibatis.jdbc.ScriptRunner; 10 | import org.apache.ibatis.mapping.Environment; 11 | import org.apache.ibatis.session.Configuration; 12 | import org.apache.ibatis.session.SqlSessionFactory; 13 | import org.apache.ibatis.session.SqlSessionFactoryBuilder; 14 | import org.apache.ibatis.transaction.jdbc.JdbcTransactionFactory; 15 | import org.w3c.dom.Document; 16 | import org.w3c.dom.Node; 17 | import org.w3c.dom.NodeList; 18 | import org.xml.sax.InputSource; 19 | 20 | import javax.sql.DataSource; 21 | import javax.xml.parsers.DocumentBuilder; 22 | import javax.xml.parsers.DocumentBuilderFactory; 23 | import javax.xml.xpath.XPath; 24 | import javax.xml.xpath.XPathConstants; 25 | import javax.xml.xpath.XPathFactory; 26 | import java.io.IOException; 27 | import java.io.Reader; 28 | import java.sql.Connection; 29 | import java.sql.SQLException; 30 | import java.util.ArrayList; 31 | import java.util.List; 32 | 33 | public class BaseUtils { 34 | 35 | public static List parse(String resource) throws Exception { 36 | List re = new ArrayList<>(); 37 | try(Reader reader = Resources.getResourceAsReader(resource)){ 38 | DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); 39 | DocumentBuilder builder = factory.newDocumentBuilder(); 40 | Document document = builder.parse(new InputSource(reader)); 41 | XPathFactory xPathfactory = XPathFactory.newInstance(); 42 | XPath xpath = xPathfactory.newXPath(); 43 | NodeList nodes = (NodeList)xpath.evaluate("injectTest|logicDeleteTest",xpath.evaluate("/tests",document, XPathConstants.NODE), XPathConstants.NODESET); 44 | for (int i = 0; i < nodes.getLength(); i++) { 45 | Node node = nodes.item(i); 46 | re.add(parseNode(node)); 47 | } 48 | } 49 | return re; 50 | } 51 | 52 | static SqlTest parseNode(Node node) throws Exception { 53 | XPathFactory xPathfactory = XPathFactory.newInstance(); 54 | XPath xpath = xPathfactory.newXPath(); 55 | Node origin = (Node) xpath.evaluate("origin",node,XPathConstants.NODE); 56 | Node target = (Node) xpath.evaluate("target",node,XPathConstants.NODE); 57 | String name = node.getNodeName(); 58 | 59 | if("injectTest".equals(name)){ 60 | return new SqlTest() { 61 | @Override 62 | public String origin() { 63 | return origin.getTextContent(); 64 | } 65 | 66 | 67 | @Override 68 | public String target() { 69 | return target.getTextContent(); 70 | } 71 | 72 | @Override 73 | public String name() { 74 | return node.getAttributes().getNamedItem("name").getNodeValue(); 75 | } 76 | 77 | @Override 78 | public DbType db() { 79 | return DbType.of(node.getAttributes().getNamedItem("db").getNodeValue()); 80 | } 81 | 82 | @Override 83 | public InjectColumnInfoHandler injectColumnInfoHandler() { 84 | return new InjectColumnInfoHandler() { 85 | @Override 86 | public String getColumnName() { 87 | return node.getAttributes().getNamedItem("column").getNodeValue(); 88 | } 89 | 90 | @Override 91 | public String getValue() { 92 | return node.getAttributes().getNamedItem("value").getNodeValue(); 93 | } 94 | 95 | @Override 96 | public String op() { 97 | Node op = node.getAttributes().getNamedItem("op"); 98 | return op!=null? op.getNodeValue():"="; 99 | } 100 | 101 | @Override 102 | public int getInjectTypes() { 103 | int re = 0; 104 | String type = node.getAttributes().getNamedItem("type").getNodeValue().toLowerCase(); 105 | if(type.contains("condition") || type.contains("logicDelete")){ 106 | re|=CONDITION; 107 | } 108 | if(type.contains("insert")){ 109 | re|=INSERT; 110 | } 111 | if(type.contains("update")){ 112 | re|=UPDATE; 113 | } 114 | 115 | return re; 116 | } 117 | }; 118 | } 119 | }; 120 | }else if("logicDeleteTest".equals(name)){ 121 | return new SqlTest(){ 122 | 123 | @Override 124 | public String origin() { 125 | return origin.getTextContent(); 126 | } 127 | 128 | @Override 129 | public String target() { 130 | return target.getTextContent(); 131 | } 132 | 133 | @Override 134 | public String name() { 135 | return node.getAttributes().getNamedItem("name").getNodeValue(); 136 | } 137 | 138 | @Override 139 | public DbType db() { 140 | return DbType.of(node.getAttributes().getNamedItem("db").getNodeValue()); 141 | } 142 | 143 | @Override 144 | public InjectColumnInfoHandler injectColumnInfoHandler() { 145 | return new LogicDeleteInfoHandler() { 146 | @Override 147 | public String getDeleteSqlDemo() { 148 | 149 | return node.getAttributes().getNamedItem("deleteSqlDemo").getNodeValue(); 150 | } 151 | 152 | @Override 153 | public String getNotDeletedValue() { 154 | return node.getAttributes().getNamedItem("notDeletedValue").getNodeValue(); 155 | } 156 | 157 | @Override 158 | public String getColumnName() { 159 | return node.getAttributes().getNamedItem("column").getNodeValue(); 160 | } 161 | }; 162 | } 163 | }; 164 | } 165 | return null; 166 | } 167 | 168 | 169 | public static SqlSessionFactory generateSqlSessionFactory(DataSource dataSource, String dbScriptResource, Class MapperType, List injectColumnInfoHandlers, List columnFilterInfoHandlers) throws Exception { 170 | BaseUtils.runScript(dataSource,dbScriptResource); 171 | Environment environment = new Environment("test", new JdbcTransactionFactory(), dataSource); 172 | Configuration configuration = new Configuration(environment); 173 | configuration.addMapper(MapperType); 174 | SqlHelperPlugin sqlHelperPlugin = new SqlHelperPlugin(); 175 | sqlHelperPlugin.setInjectColumnInfoHandlers(injectColumnInfoHandlers); 176 | sqlHelperPlugin.setColumnFilterInfoHandlers(columnFilterInfoHandlers); 177 | configuration.addInterceptor(sqlHelperPlugin); 178 | return new SqlSessionFactoryBuilder().build(configuration); 179 | } 180 | 181 | 182 | public static void runScript(DataSource ds, String resource) throws IOException, SQLException { 183 | try (Connection connection = ds.getConnection()) { 184 | ScriptRunner runner = new ScriptRunner(connection); 185 | runner.setAutoCommit(true); 186 | runner.setStopOnError(true); 187 | runner.setLogWriter(null); 188 | runner.setErrorLogWriter(null); 189 | runScript(runner, resource); 190 | } 191 | } 192 | public static void runScript(ScriptRunner runner, String resource) throws IOException, SQLException { 193 | try (Reader reader = Resources.getResourceAsReader(resource)) { 194 | runner.runScript(reader); 195 | } 196 | } 197 | 198 | } 199 | -------------------------------------------------------------------------------- /mybatis-sqlhelper/src/test/java/io/github/heykb/sqlhelper/test/CompareSqlTest.java: -------------------------------------------------------------------------------- 1 | package io.github.heykb.sqlhelper.test; 2 | 3 | import com.alibaba.druid.sql.SQLUtils; 4 | import io.github.heykb.sqlhelper.handler.InjectColumnInfoHandler; 5 | import io.github.heykb.sqlhelper.helper.SqlStatementEditor; 6 | import org.junit.After; 7 | import org.junit.jupiter.api.AfterAll; 8 | import org.junit.jupiter.api.Assertions; 9 | import org.junit.jupiter.api.BeforeAll; 10 | import org.junit.jupiter.api.Named; 11 | import org.junit.jupiter.params.ParameterizedTest; 12 | import org.junit.jupiter.params.provider.Arguments; 13 | import org.junit.jupiter.params.provider.MethodSource; 14 | 15 | import java.io.FileWriter; 16 | import java.io.IOException; 17 | import java.util.Arrays; 18 | import java.util.stream.Stream; 19 | 20 | public class CompareSqlTest { 21 | 22 | static FileWriter fileWriter; 23 | 24 | static Stream parse() throws Exception { 25 | return BaseUtils.parse("io/github/heykb/sqlhelper/test/testSql.xml").stream() 26 | .map(item-> Arguments.of(Named.of(item.name(),item))); 27 | } 28 | 29 | @BeforeAll 30 | static void start() throws IOException { 31 | fileWriter = new FileWriter("../sql-demo.md"); 32 | } 33 | 34 | @AfterAll 35 | static void end() throws IOException { 36 | fileWriter.close(); 37 | } 38 | 39 | @ParameterizedTest 40 | @MethodSource("parse") 41 | void test(SqlTest sqlTest) throws IOException { 42 | InjectColumnInfoHandler injectColumnInfoHandler = sqlTest.injectColumnInfoHandler(); 43 | SqlStatementEditor sqlStatementEditor = 44 | new SqlStatementEditor.Builder(sqlTest.origin(), sqlTest.db()) 45 | .injectColumnInfoHandlers(Arrays.asList(injectColumnInfoHandler)) 46 | .build(); 47 | SqlStatementEditor.Result result = sqlStatementEditor.processing(); 48 | System.out.println(sqlTest.target().trim()); 49 | StringBuilder sb = new StringBuilder(); 50 | sb.append("## ").append(sqlTest.name()).append("\n```sql\n"); 51 | String title = String.format("-- [%s] [columnName=%s] [op=\"%s\"] [value=%s]",sqlTest.db(),injectColumnInfoHandler.getColumnName(),injectColumnInfoHandler.op(),injectColumnInfoHandler.getValue()); 52 | sb.append(title).append("\n"); 53 | sb.append(SQLUtils.parseSingleStatement(sqlTest.origin(),sqlTest.db()).toString()).append("\n"); 54 | if(result == null){ 55 | System.out.println("不会修改"); 56 | Assertions.assertTrue(sqlTest.target().trim().equals("null")); 57 | }else{ 58 | String resultStr = "\n-- ⇊\n\n"+result.getSql(); 59 | sb.append(resultStr).append("\n"); 60 | String exceptResult = SQLUtils.parseSingleStatement(sqlTest.target(),sqlTest.db()).toString(); 61 | System.out.println(result.getSql()); 62 | Assertions.assertEquals(result.getSql(),exceptResult); 63 | } 64 | sb.append("```\n"); 65 | fileWriter.write(sb.toString()); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /mybatis-sqlhelper/src/test/java/io/github/heykb/sqlhelper/test/DynamicDatasourceTest.java: -------------------------------------------------------------------------------- 1 | package io.github.heykb.sqlhelper.test; 2 | 3 | import io.github.heykb.sqlhelper.dynamicdatasource.*; 4 | import org.apache.ibatis.datasource.unpooled.UnpooledDataSource; 5 | import org.apache.ibatis.session.SqlSession; 6 | import org.apache.ibatis.session.SqlSessionFactory; 7 | import org.junit.jupiter.api.BeforeAll; 8 | import org.junit.jupiter.api.DisplayName; 9 | import org.junit.jupiter.api.Test; 10 | 11 | import javax.sql.DataSource; 12 | import java.util.List; 13 | import java.util.Map; 14 | 15 | @DisplayName("动态数据源") 16 | public class DynamicDatasourceTest { 17 | 18 | static SqlSessionFactory sqlSessionFactory; 19 | static DefaultSqlHelperDsManager sqlHelperDsManager; 20 | @BeforeAll 21 | static void setUp() throws Exception { 22 | DataSource dataSource = new UnpooledDataSource("org.hsqldb.jdbcDriver","jdbc:hsqldb:mem:automapping","sa",null); 23 | SqlHelperDsManager sqlHelperDsManager = new DefaultSqlHelperDsManager(dataSource); 24 | SqlHelperDynamicDataSourceProxy dataSourceProxy = new SqlHelperDynamicDataSourceProxy(sqlHelperDsManager); 25 | sqlHelperDsManager.put("ds2", LogicDsMeta.builder() 26 | .expectedSubspaceType(ConnectionSubspaceTypeEnum.NOT_SUPPORT) 27 | .datasourceId("ds2").createFunc(()->{ 28 | return new UnpooledDataSource("org.hsqldb.jdbcDriver","jdbc:hsqldb:mem:automapping2","sa",null); 29 | }).build()); 30 | sqlSessionFactory = BaseUtils.generateSqlSessionFactory(dataSourceProxy, "io/github/heykb/sqlhelper/test/baseTest.sql", 31 | BaseTest.TestMapper.class, null,null); 32 | BaseUtils.runScript(sqlHelperDsManager.getById("ds2"),"io/github/heykb/sqlhelper/test/baseTest.sql"); 33 | } 34 | 35 | @Test 36 | void cacheTest() throws Exception { 37 | try(SqlSession sqlSession =sqlSessionFactory.openSession()){ 38 | BaseTest.TestMapper testMapper = sqlSession.getMapper(BaseTest.TestMapper.class); 39 | List result = testMapper.selectByName("tom"); 40 | 41 | } 42 | SqlHelperDsContextHolder.switchTo("ds2"); 43 | try(SqlSession sqlSession =sqlSessionFactory.openSession()){ 44 | BaseTest.TestMapper testMapper = sqlSession.getMapper(BaseTest.TestMapper.class); 45 | List result = testMapper.selectByName("tom"); 46 | // Assertions.assertTrue(result.size()>0); 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /mybatis-sqlhelper/src/test/java/io/github/heykb/sqlhelper/test/SqlTest.java: -------------------------------------------------------------------------------- 1 | package io.github.heykb.sqlhelper.test; 2 | 3 | import com.alibaba.druid.DbType; 4 | import io.github.heykb.sqlhelper.handler.InjectColumnInfoHandler; 5 | 6 | public interface SqlTest { 7 | String name(); 8 | DbType db(); 9 | String origin(); 10 | String target(); 11 | InjectColumnInfoHandler injectColumnInfoHandler(); 12 | } 13 | -------------------------------------------------------------------------------- /mybatis-sqlhelper/src/test/java/io/github/heykb/sqlhelper/test/baseTest.sql: -------------------------------------------------------------------------------- 1 | DROP TABLE IF EXISTS people; 2 | CREATE TABLE people ( 3 | name varchar(255) , 4 | age int, 5 | email varchar(255) , 6 | id varchar(255) NOT NULL, 7 | tenant_id varchar(255), 8 | dept_id varchar(255), 9 | created_time timestamp(6), 10 | created_by varchar(255) , 11 | updated_time timestamp(6), 12 | updated_by varchar(255), 13 | is_deleted char(1) default 'N' not null, 14 | CONSTRAINT people_pkey PRIMARY KEY (id) 15 | ) 16 | ; 17 | 18 | 19 | INSERT INTO people VALUES ('tony', 10, 'tony@qq.com', '1', 'tenant_1', 'dept1', '2021-11-12 12:18:14.235029', 'admin', NULL, 'admin','N'); 20 | INSERT INTO people VALUES ('tom', 20, 'tom@qq.com', '2', 'tenant_2', 'dept2', '2021-11-12 12:18:14.235029', 'admin', NULL, 'admin','N'); 21 | 22 | -------------------------------------------------------------------------------- /mybatis-sqlhelper/src/test/java/log4j.properties: -------------------------------------------------------------------------------- 1 | 2 | log4j.rootLogger=DEBUG,Console 3 | log4j.appender.Console=org.apache.log4j.ConsoleAppender 4 | log4j.appender.Console.Target=System.out 5 | log4j.appender.Console.layout=org.apache.log4j.PatternLayout 6 | log4j.appender.Console.layout.ConversionPattern=[%d{yy/MM/dd HH:mm:ss:SSS}]-%l:%m%n -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | io.github.heykb 8 | mybatis-sqlhelper-parent 9 | 10 | ${revision} 11 | pom 12 | 13 | MyBatis SqlHelper Parent pom 14 | MyBatis 多租户、逻辑删除、数据权限插件-SqlHelper Parent Pom 15 | https://github.com/heykb/mybatis-sqlhelper 16 | 17 | 18 | MIT License 19 | http://www.opensource.org/licenses/mit-license.php 20 | 21 | 22 | 23 | 24 | zrc 25 | 1259609102@qq.com 26 | 27 | 28 | 29 | scm:git:git@github.com:heykb/mybatis-sqlhelper.git 30 | scm:git:git@github.com:heykb/mybatis-sqlhelper.git 31 | git@github.com:heykb/mybatis-sqlhelper.git 32 | 33 | 34 | 35 | mybatis-sqlhelper 36 | mybatis-sqlhelper-spring 37 | mybatis-sqlhelper-spring-boot 38 | 39 | 40 | 41 | 3.0.0.SR2 42 | UTF-8 43 | UTF-8 44 | 1.8 45 | 1.8 46 | 1.2.4 47 | 21.0 48 | 3.2.2 49 | 50 | 51 | 52 | release 53 | 54 | 55 | 56 | org.apache.maven.plugins 57 | maven-source-plugin 58 | 2.2.1 59 | 60 | 61 | attach-sources 62 | 63 | jar-no-fork 64 | 65 | 66 | 67 | 68 | 69 | org.apache.maven.plugins 70 | maven-javadoc-plugin 71 | 2.9.1 72 | 73 | 74 | attach-javadocs 75 | 76 | jar 77 | 78 | 79 | 80 | 81 | 82 | org.apache.maven.plugins 83 | maven-gpg-plugin 84 | 1.5 85 | 86 | 87 | sign-artifacts 88 | verify 89 | 90 | sign 91 | 92 | 93 | 94 | 95 | 96 | org.sonatype.plugins 97 | nexus-staging-maven-plugin 98 | 1.6.7 99 | true 100 | 101 | ossrh 102 | https://s01.oss.sonatype.org/ 103 | true 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | org.codehaus.mojo 115 | 116 | 117 | flatten-maven-plugin 118 | 1.1.0 119 | 120 | true 121 | resolveCiFriendliesOnly 122 | 123 | 124 | 125 | flatten 126 | process-resources 127 | 128 | flatten 129 | 130 | 131 | 132 | flatten.clean 133 | clean 134 | 135 | clean 136 | 137 | 138 | 139 | 140 | 141 | maven-surefire-plugin 142 | 2.22.2 143 | 144 | -Dfile.encoding=UTF-8 145 | 146 | 147 | 148 | 149 | 150 | 151 | src/test/java 152 | 153 | **/*.java 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | ossrh 162 | https://s01.oss.sonatype.org/content/repositories/snapshots 163 | 164 | 165 | ossrh 166 | https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/ 167 | 168 | 169 | 170 | 171 | --------------------------------------------------------------------------------