├── .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 | [](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 | [](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 | [](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 | 
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 | [](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 super T> 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