├── .gitignore
├── LICENSE
├── README.md
├── pom.xml
├── qrcode_for_gh.jpg
└── src
└── main
└── java
└── com
├── MPP.java
├── baomidou
└── mybatisplus
│ └── core
│ └── metadata
│ └── ResultMapHelper.java
└── github
└── jeffreyning
└── mybatisplus
├── anno
├── AutoMap.java
├── InsertFill.java
├── MppMultiId.java
└── UpdateFill.java
├── base
├── DeleteByMultiIdMethod.java
├── MppBaseMapper.java
├── MppSqlInjector.java
├── SelectByMultiIdMethod.java
└── UpdateByMultiIdMethod.java
├── conf
├── EnableAutoFill.java
├── EnableKeyGen.java
├── EnableMPP.java
└── PlusConfig.java
├── handler
├── DataAutoFill.java
└── MppKeyGenerator.java
├── ognl
└── NhOgnlClassResolver.java
├── scan
├── ResultMapUtil.java
└── ScanUtil.java
├── service
├── IMppService.java
└── MppServiceImpl.java
└── util
├── CheckId.java
├── ColNameUtil.java
├── LambdaUtil.java
├── ParseFun.java
├── PlusACUtils.java
└── ReadValueUtil.java
/.gitignore:
--------------------------------------------------------------------------------
1 | .idea
2 | .project
3 | .settings
4 | .classpath
5 | .factorypath
6 | .apt_generated
7 | target
8 | out
9 |
10 | *.iml
11 |
12 | # Compiled class file
13 | *.class
14 |
15 | # Log file
16 | *.log
17 |
18 | # BlueJ files
19 | *.ctxt
20 |
21 | # Mobile Tools for Java (J2ME)
22 | .mtj.tmp/
23 |
24 | # Package Files #
25 | *.jar
26 | *.war
27 | *.nar
28 | *.ear
29 | *.zip
30 | *.tar.gz
31 | *.rar
32 |
33 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
34 | hs_err_pid*
35 | /.apt_generated/
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "[]"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright [yyyy] [name of copyright owner]
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # mybatisplus-plus
2 | mybatisplus-plus对mybatisplus的一些功能补充
3 |
4 | **根据多个字段联合主键增删改查**
5 | 原生mybatisplus只支持一个主键,mpp支持多个字段联合主键(复合主键)增删改查,mapper需要继承MppBaseMapper
6 | 实体类中联合主键的字段需要用@MppMultiId注解修饰
7 | 如果需要在service使用多主键相关操作包括saveOrUpdateByMultiId和批量操作updateBatchByMultiId和saveOrUpdateBatchByMultiId,可以直接继承IMppService接口
8 |
9 | **优化分页插件实现在不分页时进行排序操作**
10 | 原生mybatisplus分页与排序是绑定的,mpp优化了分页插件,使用MppPaginationInterceptor插件
11 | 在不分页的情况下支持排序操作
12 | page参数size设置为-1可实现不分页取全量数据,同时设置OrderItem可以实现排序
13 |
14 | **自动填充优化功能 & 自动扫描Entity类构建ResultMap功能**
15 | 原生mybatisplus只能做%s+1和now两种填充,mybatisplus-plus在插入或更新时对指定字段进行自定义复杂sql填充。
16 | 需要在实体类字段上用原生注解@TableField设置fill=FieldFill.INSERT fill=FieldFill.UPDATE或fill=FieldFill.INSERT_UPDATE否则不会触发自定义填充
17 | mybatisplus-plus使用@InsertFill注解触发插入时,执行注解中自定义的sql填充实体类字段
18 | mybatisplus-plus使用@UpdateFill注解触发更新时,执行注解中自定义的sql填充实体类字段
19 | 还可以自动填充主键字段,解决原生mybatisplus不支持多个主键的问题
20 | 使用ColNameUtil.pn静态方法,获取实体类中读取方法对应的列名称
21 |
22 | 在xml中编写resultmap是件头痛的事,特别是表连接时返回的对象是多样的,如果不按照map返回,分别建resultmap工作量会翻倍。
23 | 使用@AutoMap注解entity实体类,就可以在应用启动时解析使用@TableField注解的字段,自动生成scan.mybatis-plus_xxxx为id的resultMap
24 | 可以在xml中直接配置使用这个resultMap实例
25 | 并且还支持继承关系,扫描实体子类会附加上父类的字段信息一起构建子类的resultmap
26 | 对于各种表连接形成的返回实体对象,可以通过继承来生成。通过扫描后自动构建各种resultmap,在xml中引用。
27 |
28 | 做连表查询时,输入参数往往不是单一的实体类,而是采用更灵活的Map对象,
29 | 但map中key参数的名称定义过于随便,可以使用接口定义常量。但原生mybatis在xml中调用静态类方法和变量时需要填写完整的包名不利于大量采用
30 | 是否可以像在mybatisplus中使用lambda表达式翻译entity中的列名称
31 | mpp做了封装支持xml的ognl中引入默认包名(为了兼容jdk11 mpp1.7.0的ognl默认包名功能已经删除),并支持lambda定义列名称
32 | 例如xml使用以下语句引入map参数中create_time
33 | 原生方式
34 | ````
35 | #{create_time}
36 | ````
37 | mpp的默认包名引用接口常量方式(1.7.0此配置已经删除)
38 | 配置文件中mpp.utilBasePath可设置ognl默认包名
39 | ````
40 | #{${@ColInfo@createTime}}
41 | ````
42 | mpp的lambda方式(1.7.0中使用@com.MPP@col)
43 | ````
44 | #{${@MPP@col("TestEntity::getCreateTime")}}
45 | ````
46 | **从中央库引入jar**
47 | ````
48 |
49 | com.github.jeffreyning
50 | mybatisplus-plus
51 | 1.7.5-RELEASE
52 |
53 | ````
54 |
55 |
56 |
57 | **在实体类上设置@KeySequence,在插入时对id字段自动填充复杂计算值**
58 | ````
59 | @KeySequence("select lpad(max(seqno)+3,10,'0') from test")
60 | @TableName(value = "test")
61 | public class TestEntity {
62 | @TableId(value = "id", type=IdType.INPUT)
63 | private Integer id;
64 | ````
65 | **1.5.1版本之后需使用@EnableAutoFill注解整体开启自动注入功能**
66 | **在实体类字段上设置@InsertFill @UpdateFill,插入和更新时使用当前时间填充**
67 | ````
68 | @InsertFill("select now()")
69 | @UpdateFill("select now()")
70 | @TableField(value="update_time",fill=FieldFill.INSERT_UPDATE)
71 | private Date updateTime;
72 | ````
73 |
74 | **在实体类字段上设置@InsertFill,在插入时对seqno字段自动填充复杂计算值**
75 | 查询当前最大的seqno值并加3,转换成10位字符串,不够位数时用0填充
76 | ````
77 | @TableField(value="seqno",fill=FieldFill.INSERT )
78 | @InsertFill("select lpad(max(seqno)+3,10,'0') from test")
79 | private String seqno;
80 | ````
81 |
82 | **在启动类中使用@EnableMPP启动扩展自定义填充功能和自动创建resultmap功能**
83 | **在启动类中使用@EnableKeyGen启动主键自定义主键填充功能**
84 | 注意如果自己实现了IKeyGenerator会与@EnableKeyGen冲突
85 | ````
86 | @SpringBootApplication
87 | @EnableMPP
88 | @EnableKeyGen
89 | public class PlusDemoApplication {
90 | public static void main(String[] args) {
91 | SpringApplication.run(PlusDemoApplication.class, args);
92 | }
93 | }
94 | ````
95 |
96 | **在实体类上使用@AutoMap注解**
97 | JoinEntity是TestEntity的子类
98 | @TableName(autoResultMap=true)
99 | autoResultMap必须设置为true
100 | 父类可以不加@AutoMap,父类设置autoResultMap=true时mybatisplus负责生成resultmap
101 | 但原生mybatisplus生成的resultmap的id为mybatis-plus_xxxx没有scan.前缀
102 | ````
103 | @AutoMap
104 | @TableName(autoResultMap=true)
105 | public class JoinEntity extends TestEntity{
106 | @TableField("some2")
107 | private String some2;
108 |
109 | public String getSome2() {
110 | return some2;
111 | }
112 |
113 | public void setSome2(String some2) {
114 | this.some2 = some2;
115 | }
116 |
117 | @Override
118 | public String toString() {
119 | return "JoinEntity{" +
120 | "some2='" + some2 + '\'' +
121 | '}';
122 | }
123 | }
124 | ````
125 |
126 | **配置文件中加入扫描entity路径,多个路径用逗号分隔**
127 | ````
128 | mpp:
129 | entityBasePath: com.github.jeffreyning.mybatisplus.demo.entity
130 | ````
131 | **配置文件中加入ognl执行java静态方法的类加载默认路径,多个路径用逗号分隔(1.7.0此配置已经删除)**
132 | ````
133 | mpp:
134 | utilBasePath: com.github.jeffreyning.mybatisplus.demo.common
135 | ````
136 |
137 | **xml文件中引入自动生成的resultMap & xml中使用省略包名调用静态方法(1.7.0省略包名已经删除,使用com.MPP) & @com.MPP@col通过entity的lambda表达式翻译列名**
138 | ````
139 |
140 |
141 |
142 |
145 |
150 |
151 | ````
152 |
153 | **接口直接返回实例类**
154 | ````
155 | @Mapper
156 | public interface TestMapper extends BaseMapper {
157 | public List queryUseRM();
158 | public List queryUse(Map param);
159 | }
160 | ````
161 |
162 | **使用ColNameUtil.pn静态方法,获取实体类中读取方法对应的列名称**
163 | ````
164 | System.out.println(ColNameUtil.pn(TestEntity::getCreateTime));
165 | System.out.println(ColNameUtil.pn(TestEntity::getId));
166 | System.out.println(ColNameUtil.pn(JoinEntity::getSome2));
167 | System.out.println(ColNameUtil.pn(JoinEntity::getUpdateTime));
168 | ````
169 |
170 | **根据多个字段联合主键增删改查**
171 | 在实例类成员变量上使用@MppMultiId表明联合主键
172 | ````
173 | @TableName("test07")
174 | public class Test07Entity {
175 | @MppMultiId
176 | @TableField(value = "k1")
177 | private Integer k1;
178 |
179 | @MppMultiId
180 | @TableField(value = "k2")
181 | private String k2;
182 |
183 | @TableField(value = "col1")
184 | private String col1;
185 | @TableField(value = "col2")
186 | private String col2;
187 | ````
188 |
189 | mapper需要继承MppBaseMapper
190 | ````
191 | @Mapper
192 | public interface Test07Mapper extends MppBaseMapper {
193 | }
194 | ````
195 | 根据多主键增删改查
196 | ````
197 | public void testMultiId(){
198 | //id
199 | Test07Entity idEntity=new Test07Entity();
200 | idEntity.setK1(1);
201 | idEntity.setK2("111");
202 | //del
203 | test07Mapper.deleteByMultiId(idEntity);
204 | //add
205 | test07Mapper.insert(idEntity);
206 | //query
207 | Test07Entity retEntity=test07Mapper.selectByMultiId(idEntity);
208 | retEntity.setCol1("xxxx");
209 | //update
210 | test07Mapper.updateByMultiId(retEntity);
211 | }
212 | ````
213 | service层继承IMppService接口和MppServiceImpl
214 | ````
215 | public interface Test07Service extends IMppService {
216 | }
217 | public class Test07ServiceImpl extends MppServiceImpl implements Test07Service {
218 | }
219 | ````
220 |
221 | 在service层调用多主键操作
222 | ````
223 | public void testMultiIdService(){
224 | //id
225 | Test07Entity idEntity=new Test07Entity();
226 | idEntity.setK1(1);
227 | idEntity.setK2("111");
228 | //del
229 |
230 | test07Service.deleteByMultiId(idEntity);
231 | //add
232 | test07Service.save(idEntity);
233 | //query
234 | Test07Entity retEntity=test07Service.selectByMultiId(idEntity);
235 | retEntity.setCol1("xxxx");
236 | //update
237 | test07Mapper.updateByMultiId(retEntity);
238 | }
239 | ````
240 |
241 | 根据复合主键进行批量操作和saveOrUpdate操作
242 | ````
243 | @Test
244 | public void testSaveOrUpdateByMultiIdService(){
245 | //id
246 | Test07Entity idEntity=new Test07Entity();
247 | idEntity.setK1(6);
248 | idEntity.setK2("666");
249 | //del
250 | test07Service.deleteByMultiId(idEntity);
251 | //add
252 | test07Service.saveOrUpdateByMultiId(idEntity);
253 | //update
254 | idEntity.setCol1("ccccc");
255 | test07Service.saveOrUpdateByMultiId(idEntity);
256 |
257 | }
258 |
259 | @Test
260 | public void testSaveOrUpdateBatchByMultiIdService(){
261 | //ids
262 | List entityList=new ArrayList();
263 | for(int i=10;i<30;i++){
264 | Test07Entity idEntity=new Test07Entity();
265 | idEntity.setK1(i);
266 | idEntity.setK2(String.valueOf(i*10));
267 | entityList.add(idEntity);
268 | }
269 |
270 | //del
271 | for(Test07Entity idEntity:entityList) {
272 | test07Service.deleteByMultiId(idEntity);
273 | }
274 | //add batch
275 | test07Service.saveOrUpdateBatchByMultiId(entityList);
276 | //del
277 | for(Test07Entity idEntity:entityList) {
278 | idEntity.setCol1(new Date().toString());
279 | }
280 | //update batch
281 | test07Service.saveOrUpdateBatchByMultiId(entityList);
282 |
283 | }
284 |
285 | @Test
286 | public void testUpdateBatchByMultiIdService(){
287 | //ids
288 | List entityList=new ArrayList();
289 | for(int i=50;i<80;i++){
290 | Test07Entity idEntity=new Test07Entity();
291 | idEntity.setK1(i);
292 | idEntity.setK2(String.valueOf(i*10));
293 | entityList.add(idEntity);
294 | }
295 |
296 | //del
297 | for(Test07Entity idEntity:entityList) {
298 | test07Service.deleteByMultiId(idEntity);
299 | }
300 | //add batch
301 | test07Service.saveOrUpdateBatchByMultiId(entityList);
302 | //del
303 | for(Test07Entity idEntity:entityList) {
304 | idEntity.setCol1(new Date().toString());
305 | }
306 | //update batch
307 | test07Service.updateBatchByMultiId(entityList);
308 |
309 | }
310 | ````
311 | **优化分页插件实现在不分页时进行排序操作(1.6.0版本已删除)**
312 | 使用MppPaginationInterceptor插件
313 | ````
314 | @Bean
315 | public PaginationInterceptor paginationInterceptor() {
316 | return new MppPaginationInterceptor();
317 | }
318 |
319 | ````
320 | mapper中按照一般分页接口定义,同时支持返回值为list或page对象的写法
321 | ````
322 | @Mapper
323 | public interface TestMapper extends BaseMapper {
324 | public List queryUseRM(Page page);
325 | }
326 | ````
327 | page参数设置size=-1为全量查询,size>0时正常分页,设置OrderItem进行无论是否分页都实现排序
328 | ````
329 | public void testOrder(){
330 | Page page=new Page();
331 |
332 | page.setSize(-1);
333 | page.addOrder(new OrderItem().setColumn("test.id").setAsc(true));
334 | page.addOrder(new OrderItem().setColumn("test2.some2").setAsc(true));
335 |
336 | List rp=testMapper.queryUseRM(page);
337 | }
338 | ````
339 |
340 | **常见问题说明**
341 |
342 | _报错Caused by: org.apache.ibatis.binding.BindingException: Invalid bound statement (not found):_
343 |
344 | 解决方法:在启动类中使用@EnableMPP注解,一般报Invalid bound statement (not found)都是没有使用@EnableMPP
345 |
346 | _添加@EnableMPP后启动报错required a single bean, but 2 were found_
347 |
348 | 产生原因:添加@EnableMpp后会启用内置的metaObjectHandler实现类实现自动填充功能,如果旧项目中自行实现了metaObjectHandler会产生required a single bean类似冲突
349 | 解决方法:需删掉自定义的继承metaObjectHandler实现的填充功能
350 |
351 | _如果加了@EnableMPP后仍然报Invalid bound statement (not found)_
352 |
353 | 需要检查是否实现了自定义的SqlSessionFactory,如果实现自定义的SqlSessionFactory则需要手工注入
354 | MppSqlInjector(否则引发Invalid bound statement), MppKeyGenerator(否则无法主键生成), DataAutoFill(否则无法自动填充)
355 | 自定义SqlSessionFactory注入示例如下
356 | ``` @Bean
357 | public SqlSessionFactory sqlSessionFactory(DataSource dateSource, MybatisPlusProperties properties, MppSqlInjector mppSqlInjector, MppKeyGenerator mppKeyGenerator, DataAutoFill dataAutoFill) throws Exception {
358 | MybatisSqlSessionFactoryBean bean=new MybatisSqlSessionFactoryBean();
359 | GlobalConfig globalConfig = properties.getGlobalConfig();
360 | globalConfig.setSqlInjector(mppSqlInjector);
361 | globalConfig.setMetaObjectHandler(dataAutoFill);
362 | globalConfig.getDbConfig().setKeyGenerator(mppKeyGenerator);
363 | bean.setDataSource(dateSource);
364 | bean.setGlobalConfig(globalConfig);
365 | return bean.getObject();
366 | }
367 | ```
368 |
369 | _启动时报异常NoSuchFieldException: modifiers_
370 | ```
371 | org.springframework.beans.factory.BeanCreationException:
372 | Error creating bean with name 'com.github.jeffreyning.mybatisplus.conf.PlusConfig':
373 | Invocation of init method failed; nested exception is java.lang.NoSuchFieldException: modifiers
374 | ```
375 | 应该是jdk11与自定义ognl加载机制不兼容导致的。
376 | mybatisplus-plus1.7.0删除了自定义ognl根路径功能,兼容jdk11。
377 |
378 | _报 not found column for 'id'_
379 | mybatis-plus的问题,所有叫id的属性都自动注册为主键
380 | 1.7.2版本已经解决了此问题,mpp的多主键@MppMultiId可以和mp的单主键@TableId兼容,同时修饰同一个field
381 |
382 | _启动时日志中有mpp.entityBasePath is null skip scan result map_
383 | 只是个提示不影响,不想看到提示,mpp.entityBasePath可以配置到entity的包如entityBasePath: com.github.jeffreyning.mybatisplus.demo.entity;
384 | 如果xml中有resultMap="scan.mybatis-plus_xxx"才需要配置mpp.entityBasePath
385 |
386 | _提示java.lang.RuntimeException: not found column for 'xxx'_
387 | 是由于设置了@MppMultiId的字段没有同时设置@TableField(value = "xxx")导致的
388 |
389 | _使用@UpdateFill和@InsertFill自动填充时报错提示Cause: java.lang.IllegalArgumentException: argument type mismatch_
390 | 由于对某些entity中的字段类型没有做转换如LocalDateTime导致自动填充的sql的返回值类型与entity字段类型不相符,mpp1.7.4版本已经解决了此问题,旧版本需修改entity字段类型与sql返回类型一致。
391 |
392 | _调用saveOrUpdateBatchByMultiId方法报错提示 java.lang.IllegalAccessError: tried to access field com.baomidou.mybatisplus.extension.service.impl.ServiceImpl.entityClass from class com.github.jeffreyning.mybatisplus.service.MppServiceImpl_
393 | 是由于mybatis-plus3.5.5+兼容性造成的,需使用mpp1.7.5版本解决此问题
394 |
395 | _如何整合pagehelper插件_
396 |
397 | mybatisplus本身有分页常见,如果一定要使用pagehelper插件的话,与原生的mybatisplus有冲突
398 | 解决方法为:使用以下代码加载pageHelper(版本为5.1.10)
399 | ```
400 | @Bean
401 | ConfigurationCustomizer mybatisConfigurationCustomizer() {
402 | return new ConfigurationCustomizer() {
403 | @Override
404 | public void customize(Configuration configuration) {
405 | configuration.addInterceptor(new com.github.pagehelper.PageInterceptor());
406 | }
407 | };
408 | }
409 | ```
410 |
411 | **版本说明**
412 |
413 | mybatisplus-plus1.7.5兼容mybatisplus3.5.5+
414 | mybatisplus-plus1.7.4优化自动填充时的字段类型转换功能
415 | mybatisplus-plus1.7.3兼容mybatisplus3.5.1+
416 | mybatisplus-plus1.7.2支持mpp的多主键@MppMultiId可以和mp的单主键@TableId兼容,同时修饰同一个field
417 | mybatisplus-plus1.7.1支持继承多主键entity
418 |
419 |
420 | **兼容性说明**
421 |
422 | mybatisplus-plus1.5.0兼容mybatisplus3.3.1(mybatis3.5.3)到最新版mybatisplus3.4.2(mybatis3.5.6)
423 | mybatisplus-plus1.5.1与最高到mybatisplus3.4.3.1兼容
424 | (mybatisplus-plus1.5.1与mybatisplus3.4.3不兼容,mybatisplus3.4.3自身有bug无法使用,报sun.reflect.generics.reflectiveObjects.TypeVariableImpl cannot be cast to java.lang.Class)
425 | (mybatisplus-plus1.5.1与mybatisplus3.4.3.2不兼容,报org.apache.ibatis.binding.BindingException: Invalid bound statement (not found))
426 | mybatisplus-plus1.6.0与mybatisplus3.4.3.2到mybatisplus3.5.2兼容(已经测试到mybatisplus3.5.2 2022118 )(与mybatisplus3.5.3不兼容)
427 | mybatisplus-plus1.7.0兼容jdk11(删除了自定义ognl根路径功能)(1.7.0/1.7.1/1.7.2 与mpp1.6.0的兼容范围一致)
428 | mybatisplus-plus1.7.3与mybatisplus3.5.1+兼容(已经测试到mybatisplus3.5.3.1 20230129)
429 | mybatisplus-plus1.7.5与mybatisplus3.5.5+兼容(已经测试到mybatisplus3.5.7 20240716)
430 |
431 | **demo下载**
432 | 欢迎添加mpp技术交流qq群 **1028241274** 从群文件中下载各版本demo
433 | 或者从网盘中下载demo,下载密码需要搜索"爱好与编程"订阅公众号,回复"plus"获取
434 |
435 | mybatisplus-plus 1.7.2 示例工程下载地址
436 | https://pan.baidu.com/s/1-FXzhNWF6c35Nb6KgUXKwA
437 |
438 | mybatisplus-plus 1.7.1 示例工程下载地址
439 | https://pan.baidu.com/s/1wJk1xQfs5skqayaihzY_qg
440 |
441 | mybatisplus-plus 1.7.0 示例工程下载地址
442 | https://pan.baidu.com/s/1jI5X0xDDUT4L6gl6QijFHA
443 |
444 | mybatisplus-plus 1.6.0 示例工程下载地址
445 | https://pan.baidu.com/s/1ZnLBkl27dr6KVg8D_Pn0Jw
446 |
447 | mybatisplus-plus 1.5.1 示例工程下载地址
448 | 链接:https://pan.baidu.com/s/1XfRy1jrTyOefp3bqiAmNwg
449 |
450 |
451 |
452 |
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 | 4.0.0
3 | com.github.jeffreyning
4 | mybatisplus-plus
5 | 1.7.5-RELEASE
6 |
7 |
8 | com.baomidou
9 | mybatis-plus-boot-starter
10 | 3.5.7
11 | compile
12 | true
13 |
14 |
15 |
16 | com.github.jeffreyning:mybatisplus-plus
17 | mybatisplus-plus
18 | https://github.com/jeffreyning/mybatisplus-plus
19 |
20 |
21 |
22 | The Apache License, Version 2.0
23 | http://www.apache.org/licenses/LICENSE-2.0.txt
24 |
25 |
26 |
27 |
28 | ninghao
29 | jeffreyning@aliyun.com
30 | jeffreyningsoftware
31 | jeffreyningsoftware.com
32 |
33 |
34 |
35 | scm:git:git@github.com:jeffreyning/mybatisplus-plus.git
36 | scm:git:git@github.com:jeffreyning/mybatisplus-plus.git
37 | git@github.com:jeffreyning/mybatisplus-plus.git
38 |
39 |
40 |
41 |
42 | org.apache.maven.plugins
43 | maven-compiler-plugin
44 | 3.1
45 |
46 | 8
47 | 8
48 | utf-8
49 |
50 |
51 | ${project.basedir}/lib
52 |
53 |
54 |
55 |
56 |
57 | org.apache.maven.plugins
58 | maven-gpg-plugin
59 |
60 |
61 | sign-artifacts
62 | verify
63 |
64 | sign
65 |
66 |
67 |
68 |
69 |
70 |
71 | org.sonatype.plugins
72 | nexus-staging-maven-plugin
73 | 1.6
74 | true
75 |
76 |
77 | ossrh
78 | https://oss.sonatype.org/
79 | false
80 | true
81 | 20
82 | 20
83 | true
84 |
85 |
86 |
87 | org.apache.maven.plugins
88 | maven-source-plugin
89 | 2.2.1
90 |
91 |
92 | attach-sources
93 |
94 | jar-no-fork
95 |
96 |
97 |
98 |
99 |
100 | org.apache.maven.plugins
101 | maven-javadoc-plugin
102 | 2.9.1
103 |
104 |
105 | attach-javadocs
106 |
107 | jar
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 | ossrh
117 | https://oss.sonatype.org/content/repositories/snapshots
118 |
119 |
120 | ossrh
121 | https://oss.sonatype.org/service/local/staging/deploy/maven2/
122 |
123 |
124 |
--------------------------------------------------------------------------------
/qrcode_for_gh.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jeffreyning/mybatisplus-plus/b5220d03c65df73c783debad7b2a4985defc6ad8/qrcode_for_gh.jpg
--------------------------------------------------------------------------------
/src/main/java/com/MPP.java:
--------------------------------------------------------------------------------
1 | package com;
2 |
3 | import com.github.jeffreyning.mybatisplus.util.LambdaUtil;
4 |
5 | /**
6 | * @author ninghao
7 | * for jdk11 cancel ognl 202203 mpp1.7.0 mpp short package path
8 | */
9 | public class MPP {
10 | public static String col(String lamdbaFunc){
11 | return LambdaUtil.parseFunc(lamdbaFunc);
12 | }
13 | public static String trim(String qwSql){
14 | if(qwSql==null || qwSql.length()<=0){
15 | return qwSql;
16 | }
17 | if(qwSql.startsWith("where") || qwSql.startsWith("WHERE")){
18 | return qwSql.substring(5);
19 | }
20 | return qwSql;
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/src/main/java/com/baomidou/mybatisplus/core/metadata/ResultMapHelper.java:
--------------------------------------------------------------------------------
1 | package com.baomidou.mybatisplus.core.metadata;
2 |
3 | import org.apache.ibatis.builder.MapperBuilderAssistant;
4 | import org.apache.ibatis.session.Configuration;
5 |
6 | /**
7 | * @author ninghao
8 | */
9 | public class ResultMapHelper {
10 | public static TableInfo createResultMap(Configuration configuration, Class cls){
11 | MapperBuilderAssistant assistant=new MapperBuilderAssistant(configuration, "");
12 | assistant.setCurrentNamespace("scan");
13 | TableInfo tableInfo = TableInfoHelper.initTableInfo(assistant, cls);
14 | return tableInfo;
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/src/main/java/com/github/jeffreyning/mybatisplus/anno/AutoMap.java:
--------------------------------------------------------------------------------
1 | package com.github.jeffreyning.mybatisplus.anno;
2 |
3 | import java.lang.annotation.*;
4 |
5 | /**
6 | * @author ninghao
7 | */
8 | @Documented
9 | @Retention(RetentionPolicy.RUNTIME)
10 | @Target(ElementType.TYPE)
11 | public @interface AutoMap {
12 | }
13 |
--------------------------------------------------------------------------------
/src/main/java/com/github/jeffreyning/mybatisplus/anno/InsertFill.java:
--------------------------------------------------------------------------------
1 | package com.github.jeffreyning.mybatisplus.anno;
2 |
3 | import java.lang.annotation.Documented;
4 | import java.lang.annotation.ElementType;
5 | import java.lang.annotation.Retention;
6 | import java.lang.annotation.RetentionPolicy;
7 | import java.lang.annotation.Target;
8 |
9 | /**
10 | * @author ninghao
11 | */
12 | @Documented
13 | @Retention(RetentionPolicy.RUNTIME)
14 | @Target(ElementType.FIELD)
15 | public @interface InsertFill {
16 | String value() default "";
17 | }
18 |
--------------------------------------------------------------------------------
/src/main/java/com/github/jeffreyning/mybatisplus/anno/MppMultiId.java:
--------------------------------------------------------------------------------
1 | package com.github.jeffreyning.mybatisplus.anno;
2 |
3 | import java.lang.annotation.*;
4 |
5 | /**
6 | * @author ninghao
7 | */
8 | @Documented
9 | @Retention(RetentionPolicy.RUNTIME)
10 | @Target(ElementType.FIELD)
11 | public @interface MppMultiId {
12 | String value() default "";
13 | }
14 |
--------------------------------------------------------------------------------
/src/main/java/com/github/jeffreyning/mybatisplus/anno/UpdateFill.java:
--------------------------------------------------------------------------------
1 | package com.github.jeffreyning.mybatisplus.anno;
2 |
3 | import java.lang.annotation.Documented;
4 | import java.lang.annotation.ElementType;
5 | import java.lang.annotation.Retention;
6 | import java.lang.annotation.RetentionPolicy;
7 | import java.lang.annotation.Target;
8 |
9 | /**
10 | * @author ninghao
11 | */
12 | @Documented
13 | @Retention(RetentionPolicy.RUNTIME)
14 | @Target(ElementType.FIELD)
15 | public @interface UpdateFill {
16 | String value() default "";
17 | }
18 |
--------------------------------------------------------------------------------
/src/main/java/com/github/jeffreyning/mybatisplus/base/DeleteByMultiIdMethod.java:
--------------------------------------------------------------------------------
1 | package com.github.jeffreyning.mybatisplus.base;
2 |
3 | import com.baomidou.mybatisplus.core.enums.SqlMethod;
4 | import com.baomidou.mybatisplus.core.injector.AbstractMethod;
5 | import com.baomidou.mybatisplus.core.metadata.TableFieldInfo;
6 | import com.baomidou.mybatisplus.core.metadata.TableInfo;
7 | import com.github.jeffreyning.mybatisplus.anno.MppMultiId;
8 | import com.github.jeffreyning.mybatisplus.util.CheckId;
9 | import org.apache.ibatis.mapping.MappedStatement;
10 | import org.apache.ibatis.mapping.SqlSource;
11 | import org.slf4j.Logger;
12 | import org.slf4j.LoggerFactory;
13 |
14 | import java.lang.reflect.Field;
15 | import java.util.HashMap;
16 | import java.util.List;
17 | import java.util.Map;
18 |
19 | /**
20 | * @author ninghao
21 | */
22 | public class DeleteByMultiIdMethod extends AbstractMethod {
23 | private static final Logger logger = LoggerFactory.getLogger(DeleteByMultiIdMethod.class);
24 |
25 | //for mp 5.3.5.1
26 | public DeleteByMultiIdMethod(){
27 | super("deleteByMultiId");
28 | }
29 |
30 | private String getCol(List fieldList, String attrName){
31 | for(TableFieldInfo tableFieldInfo: fieldList){
32 | String prop=tableFieldInfo.getProperty();
33 | if(prop.equals(attrName)){
34 | return tableFieldInfo.getColumn();
35 | }
36 | }
37 | throw new RuntimeException("not found column for "+attrName);
38 | }
39 | private String createWhere(Class> modelClass, TableInfo tableInfo){
40 | List fieldList=tableInfo.getFieldList();
41 | Map idMap=new HashMap();
42 | for(TableFieldInfo fieldInfo: fieldList){
43 | Field field=fieldInfo.getField();
44 | MppMultiId mppMultiId= field.getAnnotation(MppMultiId.class);
45 | if(mppMultiId!=null){
46 | String attrName=field.getName();
47 | String colName=getCol(fieldList, attrName);
48 | idMap.put(attrName, colName);
49 | }
50 | }
51 | //add 1.7.2
52 | CheckId.appendIdColum(modelClass,tableInfo,idMap);
53 | if(idMap.isEmpty()){
54 | logger.info("entity {} not contain MppMultiId anno", modelClass.getName());
55 | return null;
56 | }
57 | StringBuilder sb=new StringBuilder("");
58 | idMap.forEach((attrName, colName)->{
59 | if(sb.length() >0){
60 | sb.append(" and ");
61 | }
62 | sb.append(colName).append("=").append("#{").append(attrName).append("}");
63 | });
64 | return sb.toString();
65 | }
66 | public MappedStatement injectMappedStatement(Class> mapperClass, Class> modelClass, TableInfo tableInfo) {
67 | String cWhere=createWhere(modelClass, tableInfo);
68 | if(cWhere==null){
69 | return null;
70 | }
71 | String methodName="deleteByMultiId";
72 | SqlSource sqlSource;
73 | if (tableInfo.isLogicDelete()) {
74 | String sqlTemp="";
75 | String sql = String.format(sqlTemp, tableInfo.getTableName(), this.sqlLogicSet(tableInfo), tableInfo.getLogicDeleteSql(true, true));
76 | sqlSource = this.languageDriver.createSqlSource(this.configuration, sql, Object.class);
77 | return this.addUpdateMappedStatement(mapperClass, modelClass, methodName, sqlSource);
78 | } else {
79 | String sqlTemp = "DELETE FROM %s WHERE "+cWhere;
80 | String sql = String.format(sqlTemp, tableInfo.getTableName());
81 | sqlSource = this.languageDriver.createSqlSource(this.configuration, sql, Object.class);
82 | return this.addDeleteMappedStatement(mapperClass, methodName, sqlSource);
83 | }
84 | }
85 | }
86 |
--------------------------------------------------------------------------------
/src/main/java/com/github/jeffreyning/mybatisplus/base/MppBaseMapper.java:
--------------------------------------------------------------------------------
1 | package com.github.jeffreyning.mybatisplus.base;
2 |
3 | import com.baomidou.mybatisplus.core.mapper.BaseMapper;
4 | import org.apache.ibatis.annotations.Param;
5 |
6 | import java.io.Serializable;
7 |
8 | /**
9 | * @author ninghao
10 | */
11 | public interface MppBaseMapper extends BaseMapper {
12 |
13 | int deleteByMultiId(T entityId);
14 | int updateByMultiId(@Param("et") T entity);
15 | T selectByMultiId(T entityId);
16 | }
17 |
--------------------------------------------------------------------------------
/src/main/java/com/github/jeffreyning/mybatisplus/base/MppSqlInjector.java:
--------------------------------------------------------------------------------
1 | package com.github.jeffreyning.mybatisplus.base;
2 |
3 | import com.baomidou.mybatisplus.core.injector.AbstractMethod;
4 | import com.baomidou.mybatisplus.core.injector.DefaultSqlInjector;
5 | import com.baomidou.mybatisplus.core.mapper.BaseMapper;
6 | import com.baomidou.mybatisplus.core.metadata.TableInfo;
7 | import com.github.jeffreyning.mybatisplus.handler.DataAutoFill;
8 | import org.slf4j.Logger;
9 | import org.slf4j.LoggerFactory;
10 |
11 | import java.util.List;
12 |
13 | /**
14 | * @author ninghao
15 | */
16 | public class MppSqlInjector extends DefaultSqlInjector {
17 | private static final Logger logger = LoggerFactory.getLogger(MppSqlInjector.class);
18 | //modify 1.6.0
19 | @Override
20 | public List getMethodList(Class> mapperClass, TableInfo tableInfo) {
21 | List methodList = super.getMethodList(mapperClass, tableInfo);
22 | methodList.add(new SelectByMultiIdMethod());
23 | methodList.add(new UpdateByMultiIdMethod());
24 | methodList.add(new DeleteByMultiIdMethod());
25 | return methodList;
26 | }
27 | /* @Override
28 | public List getMethodList(Class> mapperClass) {
29 | List methodList = super.getMethodList(mapperClass);
30 | methodList.add(new SelectByMultiIdMethod());
31 | methodList.add(new UpdateByMultiIdMethod());
32 | methodList.add(new DeleteByMultiIdMethod());
33 | return methodList;
34 | }*/
35 | }
36 |
--------------------------------------------------------------------------------
/src/main/java/com/github/jeffreyning/mybatisplus/base/SelectByMultiIdMethod.java:
--------------------------------------------------------------------------------
1 | package com.github.jeffreyning.mybatisplus.base;
2 |
3 | import com.baomidou.mybatisplus.core.injector.AbstractMethod;
4 | import com.baomidou.mybatisplus.core.metadata.TableFieldInfo;
5 | import com.baomidou.mybatisplus.core.metadata.TableInfo;
6 | import com.baomidou.mybatisplus.core.toolkit.StringUtils;
7 | import com.github.jeffreyning.mybatisplus.anno.MppMultiId;
8 | import com.github.jeffreyning.mybatisplus.conf.PlusConfig;
9 | import com.github.jeffreyning.mybatisplus.util.CheckId;
10 | import org.apache.ibatis.mapping.MappedStatement;
11 | import org.apache.ibatis.mapping.SqlSource;
12 | import org.apache.ibatis.scripting.defaults.RawSqlSource;
13 | import org.slf4j.Logger;
14 | import org.slf4j.LoggerFactory;
15 |
16 | import java.lang.reflect.Field;
17 | import java.util.HashMap;
18 | import java.util.List;
19 | import java.util.Map;
20 |
21 |
22 | /**
23 | * @author ninghao
24 | */
25 | public class SelectByMultiIdMethod extends AbstractMethod {
26 | private static final Logger logger = LoggerFactory.getLogger(SelectByMultiIdMethod.class);
27 |
28 | public SelectByMultiIdMethod(){
29 | super("selectByMultiId");
30 | }
31 |
32 | private String getCol(List fieldList, String attrName){
33 | for(TableFieldInfo tableFieldInfo: fieldList){
34 | String prop=tableFieldInfo.getProperty();
35 | if(prop.equals(attrName)){
36 | return tableFieldInfo.getColumn();
37 | }
38 | }
39 | throw new RuntimeException("not found column for "+attrName);
40 | }
41 |
42 |
43 |
44 | private String createWhere(Class> modelClass, TableInfo tableInfo){
45 | List fieldList=tableInfo.getFieldList();
46 | Map idMap=new HashMap();
47 | for(TableFieldInfo fieldInfo: fieldList){
48 | Field field=fieldInfo.getField();
49 | MppMultiId mppMultiId= field.getAnnotation(MppMultiId.class);
50 | if(mppMultiId!=null){
51 | String attrName=field.getName();
52 | String colName=getCol(fieldList, attrName);
53 | idMap.put(attrName, colName);
54 | }
55 | }
56 | //add 1.7.2
57 | CheckId.appendIdColum(modelClass,tableInfo,idMap);
58 |
59 | if(idMap.isEmpty()){
60 | logger.info("entity {} not contain MppMultiId anno", modelClass.getName());
61 | return null;
62 | }
63 | StringBuilder sb=new StringBuilder("");
64 | idMap.forEach((attrName, colName)->{
65 | if(sb.length() >0){
66 | sb.append(" and ");
67 | }
68 | sb.append(colName).append("=").append("#{").append(attrName).append("}");
69 | });
70 | return sb.toString();
71 | }
72 | @Override
73 | public MappedStatement injectMappedStatement(Class> mapperClass, Class> modelClass, TableInfo tableInfo) {
74 |
75 | String cWhere=createWhere(modelClass, tableInfo);
76 | if(cWhere==null){
77 | return null;
78 | }
79 | //
80 | String sql = "SELECT %s FROM %s WHERE "+cWhere+" %s";
81 |
82 | String method = "selectByMultiId";
83 |
84 | SqlSource sqlSource = new RawSqlSource(this.configuration, String.format(sql, this.sqlSelectColumns(tableInfo, false), tableInfo.getTableName(), tableInfo.getLogicDeleteSql(true, true)), Object.class);
85 | return this.addSelectMappedStatementForTable(mapperClass, method, sqlSource, tableInfo);
86 |
87 | }
88 |
89 | }
90 |
--------------------------------------------------------------------------------
/src/main/java/com/github/jeffreyning/mybatisplus/base/UpdateByMultiIdMethod.java:
--------------------------------------------------------------------------------
1 | package com.github.jeffreyning.mybatisplus.base;
2 |
3 | import com.baomidou.mybatisplus.core.enums.SqlMethod;
4 | import com.baomidou.mybatisplus.core.injector.AbstractMethod;
5 | import com.baomidou.mybatisplus.core.metadata.TableFieldInfo;
6 | import com.baomidou.mybatisplus.core.metadata.TableInfo;
7 | import com.baomidou.mybatisplus.core.toolkit.StringUtils;
8 | import com.github.jeffreyning.mybatisplus.anno.MppMultiId;
9 | import com.github.jeffreyning.mybatisplus.util.CheckId;
10 | import org.apache.ibatis.mapping.MappedStatement;
11 | import org.apache.ibatis.mapping.SqlSource;
12 | import org.slf4j.Logger;
13 | import org.slf4j.LoggerFactory;
14 |
15 | import java.lang.reflect.Field;
16 | import java.util.HashMap;
17 | import java.util.List;
18 | import java.util.Map;
19 |
20 | /**
21 | * @author ninghao
22 | */
23 | public class UpdateByMultiIdMethod extends AbstractMethod {
24 | private static final Logger logger = LoggerFactory.getLogger(UpdateByMultiIdMethod.class);
25 | //for mp 5.3.5.1
26 | public UpdateByMultiIdMethod(){
27 | super("updateByMultiId");
28 | }
29 |
30 |
31 | private String getCol(List fieldList, String attrName){
32 | for(TableFieldInfo tableFieldInfo: fieldList){
33 | String prop=tableFieldInfo.getProperty();
34 | if(prop.equals(attrName)){
35 | return tableFieldInfo.getColumn();
36 | }
37 | }
38 | throw new RuntimeException("not found column for "+attrName);
39 | }
40 |
41 | private String createWhere(Class> modelClass, TableInfo tableInfo){
42 | List fieldList=tableInfo.getFieldList();
43 | Map idMap=new HashMap();
44 | for(TableFieldInfo fieldInfo: fieldList){
45 | Field field=fieldInfo.getField();
46 | MppMultiId mppMultiId= field.getAnnotation(MppMultiId.class);
47 | if(mppMultiId!=null){
48 | String attrName=field.getName();
49 | String colName=getCol(fieldList, attrName);
50 | idMap.put(attrName, colName);
51 | }
52 | }
53 | //add 1.7.2
54 | CheckId.appendIdColum(modelClass,tableInfo,idMap);
55 | if(idMap.isEmpty()){
56 | logger.info("entity {} not contain MppMultiId anno", modelClass.getName());
57 | return null;
58 | }
59 | StringBuilder sb=new StringBuilder("");
60 | idMap.forEach((attrName, colName)->{
61 | if(sb.length() >0){
62 | sb.append(" and ");
63 | }
64 | sb.append(colName).append("=").append("#{et.").append(attrName).append("}");
65 | });
66 | return sb.toString();
67 | }
68 | public MappedStatement injectMappedStatement(Class> mapperClass, Class> modelClass, TableInfo tableInfo) {
69 | String cWhere=createWhere(modelClass, tableInfo);
70 | if(cWhere==null){
71 | return null;
72 | }
73 | String methodName="updateByMultiId";
74 | boolean logicDelete = tableInfo.isLogicDelete();
75 | String sqlTemp="";
76 | String additional = this.optlockVersion(tableInfo) + tableInfo.getLogicDeleteSql(true, true);
77 | String sql = String.format(sqlTemp, tableInfo.getTableName(), this.sqlSet(logicDelete, false, tableInfo, false, "et", "et."), additional);
78 | SqlSource sqlSource = this.languageDriver.createSqlSource(this.configuration, sql, modelClass);
79 | return this.addUpdateMappedStatement(mapperClass, modelClass, methodName, sqlSource);
80 | }
81 | }
82 |
--------------------------------------------------------------------------------
/src/main/java/com/github/jeffreyning/mybatisplus/conf/EnableAutoFill.java:
--------------------------------------------------------------------------------
1 | package com.github.jeffreyning.mybatisplus.conf;
2 |
3 | import com.github.jeffreyning.mybatisplus.handler.DataAutoFill;
4 | import org.springframework.context.annotation.Import;
5 |
6 | import java.lang.annotation.*;
7 |
8 | /**
9 | * @author ninghao
10 | */
11 | @Retention(RetentionPolicy.RUNTIME)
12 | @Target(ElementType.TYPE)
13 | @Documented
14 | @Import(DataAutoFill.class)
15 | public @interface EnableAutoFill {
16 | }
17 |
--------------------------------------------------------------------------------
/src/main/java/com/github/jeffreyning/mybatisplus/conf/EnableKeyGen.java:
--------------------------------------------------------------------------------
1 | package com.github.jeffreyning.mybatisplus.conf;
2 |
3 | import com.github.jeffreyning.mybatisplus.handler.MppKeyGenerator;
4 | import org.springframework.context.annotation.Import;
5 |
6 | import java.lang.annotation.*;
7 |
8 | /**
9 | * @author ninghao
10 | */
11 | @Retention(RetentionPolicy.RUNTIME)
12 | @Target(ElementType.TYPE)
13 | @Documented
14 | @Import(MppKeyGenerator.class)
15 | public @interface EnableKeyGen {
16 | }
17 |
--------------------------------------------------------------------------------
/src/main/java/com/github/jeffreyning/mybatisplus/conf/EnableMPP.java:
--------------------------------------------------------------------------------
1 | package com.github.jeffreyning.mybatisplus.conf;
2 |
3 | import java.lang.annotation.Documented;
4 | import java.lang.annotation.ElementType;
5 | import java.lang.annotation.Retention;
6 | import java.lang.annotation.RetentionPolicy;
7 | import java.lang.annotation.Target;
8 |
9 | import org.springframework.context.annotation.Import;
10 |
11 | import com.github.jeffreyning.mybatisplus.handler.DataAutoFill;
12 |
13 | @Retention(RetentionPolicy.RUNTIME)
14 | @Target(ElementType.TYPE)
15 | @Documented
16 | @Import(PlusConfig.class)
17 | public @interface EnableMPP {
18 |
19 | }
20 |
--------------------------------------------------------------------------------
/src/main/java/com/github/jeffreyning/mybatisplus/conf/PlusConfig.java:
--------------------------------------------------------------------------------
1 | package com.github.jeffreyning.mybatisplus.conf;
2 |
3 | import com.github.jeffreyning.mybatisplus.base.MppSqlInjector;
4 | import com.github.jeffreyning.mybatisplus.handler.DataAutoFill;
5 | //import com.github.jeffreyning.mybatisplus.ognl.NhOgnlClassResolver;
6 | import com.github.jeffreyning.mybatisplus.scan.ResultMapUtil;
7 | import com.github.jeffreyning.mybatisplus.scan.ScanUtil;
8 | import com.github.jeffreyning.mybatisplus.util.LambdaUtil;
9 | import com.github.jeffreyning.mybatisplus.util.PlusACUtils;
10 | //import org.apache.ibatis.scripting.xmltags.OgnlCache;
11 | import org.mybatis.spring.SqlSessionTemplate;
12 | import org.slf4j.Logger;
13 | import org.slf4j.LoggerFactory;
14 | import org.springframework.beans.factory.annotation.Autowired;
15 | import org.springframework.context.annotation.Bean;
16 | import org.springframework.context.annotation.Configuration;
17 | import org.springframework.context.annotation.Import;
18 | import org.springframework.core.env.Environment;
19 |
20 | import javax.annotation.PostConstruct;
21 | import java.util.Arrays;
22 | import java.util.List;
23 | import java.util.Set;
24 |
25 | /**
26 | * @author ninghao
27 | */
28 | //@Import({DataAutoFill.class, PlusACUtils.class, MppSqlInjector.class})
29 | //modify 1.5.1
30 | @Import({PlusACUtils.class, MppSqlInjector.class})
31 | @Configuration
32 | public class PlusConfig {
33 | private static final Logger logger = LoggerFactory.getLogger(PlusConfig.class);
34 | //@Autowired
35 | //private PlusACUtils plusACUtils;
36 | @Autowired
37 | private Environment env;
38 |
39 | /* @Bean
40 | public MppSqlInjector mppSqlInjector(){
41 | return new MppSqlInjector();
42 | }*/
43 |
44 | @PostConstruct
45 | public void initRM() throws Exception {
46 | //for jdk11 cancel ognl 202203 mpp1.7.0
47 | /*
48 | NhOgnlClassResolver resolver=new NhOgnlClassResolver();
49 | resolver.baseList.add("com.github.jeffreyning.mybatisplus.check");
50 | LambdaUtil.setValue(OgnlCache.class,"CLASS_RESOLVER",resolver);
51 | String utilBasePaths=env.getProperty("mpp.utilBasePath");
52 | if(utilBasePaths==null || "".equals(utilBasePaths) ){
53 | logger.info("mpp.utilBasePath is null no util alias for xml");
54 | }else{
55 | String[] utilPaths= utilBasePaths.split(",");
56 | for(String up:utilPaths){
57 | resolver.baseList.add(up);
58 | }
59 | }*/
60 | String basePaths=env.getProperty("mpp.entityBasePath");
61 | if(basePaths==null || "".equals(basePaths) ){
62 | //1.7.2 error -> warn
63 | logger.warn("mpp.entityBasePath is null skip scan result map");
64 | return;
65 | }
66 | String[] paths= basePaths.split(",");
67 | List pathList= Arrays.asList(paths);
68 | for(String base:pathList){
69 | Set set= ScanUtil.getClasses(base);
70 | for(Class cls:set){
71 | ResultMapUtil.createResultMap(cls);
72 | LambdaUtil.createColDict(cls);
73 | }
74 | }
75 |
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/src/main/java/com/github/jeffreyning/mybatisplus/handler/DataAutoFill.java:
--------------------------------------------------------------------------------
1 | package com.github.jeffreyning.mybatisplus.handler;
2 |
3 | import java.lang.reflect.Field;
4 | import java.sql.ResultSet;
5 | import java.sql.SQLException;
6 | import java.util.List;
7 |
8 | import com.baomidou.mybatisplus.core.metadata.TableFieldInfo;
9 | import com.baomidou.mybatisplus.core.metadata.TableInfo;
10 | import com.github.jeffreyning.mybatisplus.util.PlusACUtils;
11 | import com.github.jeffreyning.mybatisplus.util.ReadValueUtil;
12 | import org.apache.ibatis.reflection.MetaObject;
13 | import org.apache.ibatis.session.SqlSession;
14 | import org.apache.ibatis.type.TypeHandler;
15 | import org.mybatis.spring.SqlSessionTemplate;
16 | import org.mybatis.spring.SqlSessionUtils;
17 | import org.slf4j.Logger;
18 | import org.slf4j.LoggerFactory;
19 | import org.springframework.stereotype.Component;
20 | import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
21 | import com.github.jeffreyning.mybatisplus.anno.InsertFill;
22 | import com.github.jeffreyning.mybatisplus.anno.UpdateFill;
23 |
24 | /**
25 | * @author ninghao
26 | */
27 | @Component
28 | public class DataAutoFill implements MetaObjectHandler {
29 | private static final Logger logger = LoggerFactory.getLogger(DataAutoFill.class);
30 | @Override
31 | public void insertFill(MetaObject metaObject) {
32 | Object classObj = metaObject.getOriginalObject();
33 | Field[] declaredFields = classObj.getClass().getDeclaredFields();
34 | if (declaredFields != null) {
35 | for (Field field : declaredFields) {
36 |
37 | InsertFill insertFill = field.getAnnotation(InsertFill.class);
38 | if (insertFill != null) {
39 | String fieldName = field.getName();
40 | String sql = insertFill.value();
41 | if (sql != null && !"".equals(sql)) {
42 | SqlSessionTemplate sqlSessionTemplate=PlusACUtils.getBean(SqlSessionTemplate.class);
43 | SqlSession sqlSession = null;
44 | try {
45 | sqlSession = SqlSessionUtils.getSqlSession(
46 | sqlSessionTemplate.getSqlSessionFactory(), sqlSessionTemplate.getExecutorType(),
47 | sqlSessionTemplate.getPersistenceExceptionTranslator());
48 | ResultSet resultSet = sqlSession.getConnection().createStatement().executeQuery(sql);
49 | if (resultSet != null) {
50 | if (resultSet.next()) {
51 | Class fieldType=field.getType();
52 | Object fieldVal = ReadValueUtil.readValue(resultSet,fieldType);
53 | this.fillStrategy(metaObject,fieldName,fieldVal);
54 | }
55 | }
56 | } catch (SQLException e) {
57 | logger.error("insert fill error", e);
58 | throw new RuntimeException("insert fill error", e);
59 | } finally {
60 | if (sqlSession != null) {
61 | SqlSessionUtils.closeSqlSession(sqlSession, sqlSessionTemplate.getSqlSessionFactory());
62 | }
63 | }
64 | }
65 |
66 | }
67 |
68 | }
69 | }
70 |
71 | }
72 |
73 | @Override
74 | public void updateFill(MetaObject metaObject) {
75 | Object classObj = metaObject.getOriginalObject();
76 | Field[] declaredFields = classObj.getClass().getDeclaredFields();
77 | if (declaredFields != null) {
78 | for (Field field : declaredFields) {
79 | UpdateFill updateFill = field.getAnnotation(UpdateFill.class);
80 | if (updateFill != null) {
81 | String fieldName = field.getName();
82 | String sql = updateFill.value();
83 | if (sql != null && !"".equals(sql)) {
84 | SqlSessionTemplate sqlSessionTemplate=PlusACUtils.getBean(SqlSessionTemplate.class);
85 | SqlSession sqlSession = null;
86 | try {
87 | sqlSession = SqlSessionUtils.getSqlSession(
88 | sqlSessionTemplate.getSqlSessionFactory(), sqlSessionTemplate.getExecutorType(),
89 | sqlSessionTemplate.getPersistenceExceptionTranslator());
90 | ResultSet resultSet = sqlSession.getConnection().createStatement().executeQuery(sql);
91 | if (resultSet != null) {
92 | if (resultSet.next()) {
93 | Class fieldType=field.getType();
94 | Object fieldVal = ReadValueUtil.readValue(resultSet,fieldType);
95 | this.fillStrategy(metaObject,fieldName,fieldVal);
96 | }
97 | }
98 | } catch (SQLException e) {
99 | logger.error("update fill error", e);
100 | throw new RuntimeException("update fill error", e);
101 | } finally {
102 | if (sqlSession != null) {
103 | SqlSessionUtils.closeSqlSession(sqlSession, sqlSessionTemplate.getSqlSessionFactory());
104 | }
105 | }
106 | }
107 | }
108 | }
109 | }
110 | }
111 | }
112 |
--------------------------------------------------------------------------------
/src/main/java/com/github/jeffreyning/mybatisplus/handler/MppKeyGenerator.java:
--------------------------------------------------------------------------------
1 | package com.github.jeffreyning.mybatisplus.handler;
2 |
3 | import com.baomidou.mybatisplus.annotation.DbType;
4 | import com.baomidou.mybatisplus.core.incrementer.IKeyGenerator;
5 | import org.springframework.stereotype.Component;
6 |
7 | /**
8 | * @author ninghao
9 | */
10 | @Component("mppKeyGenerator")
11 | public class MppKeyGenerator implements IKeyGenerator {
12 | @Override
13 | public String executeSql(String incrementerName) {
14 | return incrementerName;
15 | }
16 |
17 | //modify 1.6.0
18 | @Override
19 | public DbType dbType() {
20 | return null;
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/src/main/java/com/github/jeffreyning/mybatisplus/ognl/NhOgnlClassResolver.java:
--------------------------------------------------------------------------------
1 | package com.github.jeffreyning.mybatisplus.ognl;
2 |
3 | import org.apache.ibatis.ognl.OgnlContext;
4 | import org.apache.ibatis.scripting.xmltags.OgnlClassResolver;
5 |
6 | import java.util.ArrayList;
7 | import java.util.List;
8 | import java.util.Map;
9 | import java.util.concurrent.ConcurrentHashMap;
10 |
11 | /**
12 | * @author ninghao
13 | * for jdk11 cancel ognl 202203 mpp1.7.0
14 | */
15 | public class NhOgnlClassResolver extends OgnlClassResolver {
16 | public List baseList=new ArrayList();
17 | public ConcurrentHashMap cacheCls = new ConcurrentHashMap(101);
18 | public Class classForName(String className, OgnlContext context) throws ClassNotFoundException {
19 | Class cls = (Class)this.cacheCls.get(className);
20 | if (cls != null) {
21 | return cls;
22 | } else {
23 | try {
24 | cls = super.classForName(className, context);
25 | return cls;
26 | } catch (ClassNotFoundException e) {
27 | for(String row:baseList){
28 | String fullClassName = row+"." + className;
29 | try {
30 | cls = super.classForName(fullClassName, context);
31 | if (cls != null) {
32 | this.cacheCls.put(className, cls);
33 | return cls;
34 | }
35 | }catch(ClassNotFoundException ex){
36 |
37 | }
38 | };
39 | if(cls==null){
40 | throw new ClassNotFoundException(className);
41 | }
42 | return cls;
43 | }
44 | }
45 |
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/src/main/java/com/github/jeffreyning/mybatisplus/scan/ResultMapUtil.java:
--------------------------------------------------------------------------------
1 | package com.github.jeffreyning.mybatisplus.scan;
2 |
3 | import com.baomidou.mybatisplus.core.metadata.ResultMapHelper;
4 | import com.github.jeffreyning.mybatisplus.anno.AutoMap;
5 | import com.github.jeffreyning.mybatisplus.util.PlusACUtils;
6 | import org.apache.ibatis.session.Configuration;
7 | import org.mybatis.spring.SqlSessionTemplate;
8 |
9 | import java.lang.annotation.Annotation;
10 |
11 | /**
12 | * @author ninghao
13 | */
14 | public class ResultMapUtil {
15 | public static void createResultMap(Class cls){
16 | Annotation[] anns=cls.getAnnotations();
17 | boolean flag=false;
18 | if(anns!=null) {
19 | for (Annotation an : anns) {
20 | if (an.toString().contains(AutoMap.class.getName())) {
21 | flag=true;
22 | break;
23 | }
24 | }
25 | }
26 | if (flag==false){
27 | return;
28 | }
29 | SqlSessionTemplate sqlSessionTemplate = PlusACUtils.getBean(SqlSessionTemplate.class);
30 | Configuration configuration = sqlSessionTemplate.getConfiguration();
31 | ResultMapHelper.createResultMap(configuration, cls);
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/src/main/java/com/github/jeffreyning/mybatisplus/scan/ScanUtil.java:
--------------------------------------------------------------------------------
1 | package com.github.jeffreyning.mybatisplus.scan;
2 |
3 | import com.github.jeffreyning.mybatisplus.anno.AutoMap;
4 | import org.slf4j.Logger;
5 | import org.slf4j.LoggerFactory;
6 |
7 | import java.io.File;
8 | import java.io.IOException;
9 | import java.lang.annotation.Annotation;
10 | import java.net.JarURLConnection;
11 | import java.net.URL;
12 | import java.util.Enumeration;
13 | import java.util.HashSet;
14 | import java.util.Set;
15 | import java.util.jar.JarEntry;
16 | import java.util.jar.JarFile;
17 |
18 | public class ScanUtil {
19 | private static final Logger logger = LoggerFactory.getLogger(ScanUtil.class);
20 | public static Set getClasses(String packagePath) {
21 | Set res = new HashSet();
22 | String path = packagePath.replace(".", "/");
23 | URL url = Thread.currentThread().getContextClassLoader().getResource(path);
24 | if (url == null) {
25 | logger.error(packagePath + " is not exit");
26 | return res;
27 | }
28 | String protocol = url.getProtocol();
29 | if ("jar".equalsIgnoreCase(protocol)) {
30 | try {
31 | res.addAll(getJarClasses(url, packagePath));
32 | } catch (IOException e) {
33 | logger.error("scan error",e);
34 | return res;
35 | }
36 | } else if ("file".equalsIgnoreCase(protocol)) {
37 | res.addAll(getFileClasses(url, packagePath));
38 | }
39 | return res;
40 | }
41 |
42 | private static Set getFileClasses(URL url, String packagePath) {
43 | Set res = new HashSet();
44 | String filePath = url.getFile().replace("%20"," ");
45 | File dir = new File(filePath);
46 | String[] list = dir.list();
47 | if (list == null) return res;
48 | for (String classPath : list) {
49 | if (classPath.endsWith(".class")) {
50 | classPath = classPath.replace(".class", "");
51 | try {
52 | Class> aClass = Class.forName(packagePath + "." + classPath);
53 | res.add(aClass);
54 | /* Annotation[] anns=aClass.getAnnotations();
55 | if(anns!=null) {
56 | for (Annotation an : anns) {
57 | if (an.toString().contains(AutoMap.class.getName())) {
58 | res.add(aClass);
59 | }
60 | }
61 | }*/
62 | } catch (ClassNotFoundException e) {
63 | logger.error("scan error",e);
64 | }
65 | } else {
66 | res.addAll(getClasses(packagePath + "." + classPath));
67 | }
68 | }
69 | return res;
70 | }
71 |
72 | private static Set getJarClasses(URL url, String packagePath) throws IOException {
73 | Set res = new HashSet();
74 | JarURLConnection conn = (JarURLConnection) url.openConnection();
75 | if (conn != null) {
76 | JarFile jarFile = conn.getJarFile();
77 | Enumeration entries = jarFile.entries();
78 | while (entries.hasMoreElements()) {
79 | JarEntry jarEntry = entries.nextElement();
80 | String name = jarEntry.getName();
81 | if (name.contains(".class") && name.replaceAll("/", ".").startsWith(packagePath)) {
82 | String className = name.substring(0, name.lastIndexOf(".")).replace("/", ".");
83 | try {
84 | Class clazz = Class.forName(className);
85 | res.add(clazz);
86 | /* Annotation[] anns=clazz.getAnnotations();
87 | if(anns!=null) {
88 | for (Annotation an : anns) {
89 | if (an.toString().contains(AutoMap.class.getName())) {
90 | res.add(clazz);
91 | }
92 | }
93 | }*/
94 | } catch (ClassNotFoundException e) {
95 | logger.error("scan error",e);
96 | }
97 | }
98 | }
99 | }
100 | return res;
101 | }
102 | }
103 |
--------------------------------------------------------------------------------
/src/main/java/com/github/jeffreyning/mybatisplus/service/IMppService.java:
--------------------------------------------------------------------------------
1 | package com.github.jeffreyning.mybatisplus.service;
2 |
3 | import com.baomidou.mybatisplus.extension.service.IService;
4 | import com.baomidou.mybatisplus.extension.toolkit.SqlHelper;
5 | import com.github.jeffreyning.mybatisplus.base.MppBaseMapper;
6 | import org.springframework.transaction.annotation.Transactional;
7 |
8 | import java.util.Collection;
9 |
10 | /**
11 | * @author ninghao
12 | */
13 | public interface IMppService extends IService {
14 | MppBaseMapper getBaseMapper();
15 | default boolean updateByMultiId(T entity) {
16 | return SqlHelper.retBool(this.getBaseMapper().updateByMultiId(entity));
17 | }
18 |
19 | default boolean deleteByMultiId(T entity) {
20 | return SqlHelper.retBool(this.getBaseMapper().deleteByMultiId(entity));
21 | }
22 |
23 | default T selectByMultiId(T entity) {
24 | return this.getBaseMapper().selectByMultiId(entity);
25 | }
26 |
27 | boolean saveOrUpdateByMultiId(T entity);
28 |
29 | @Transactional(
30 | rollbackFor = {Exception.class}
31 | )
32 | default boolean saveOrUpdateBatchByMultiId(Collection entityList) {
33 | return this.saveOrUpdateBatchByMultiId(entityList, 1000);
34 | }
35 |
36 | boolean saveOrUpdateBatchByMultiId(Collection entityList, int batchSize);
37 |
38 | @Transactional(
39 | rollbackFor = {Exception.class}
40 | )
41 | default boolean updateBatchByMultiId(Collection entityList) {
42 | return this.updateBatchByMultiId(entityList, 1000);
43 | }
44 |
45 | boolean updateBatchByMultiId(Collection entityList, int batchSize);
46 | }
47 |
--------------------------------------------------------------------------------
/src/main/java/com/github/jeffreyning/mybatisplus/service/MppServiceImpl.java:
--------------------------------------------------------------------------------
1 | package com.github.jeffreyning.mybatisplus.service;
2 |
3 | import com.baomidou.mybatisplus.core.enums.SqlMethod;
4 | import com.baomidou.mybatisplus.core.metadata.TableFieldInfo;
5 | import com.baomidou.mybatisplus.core.metadata.TableInfo;
6 | import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
7 | import com.baomidou.mybatisplus.core.toolkit.Assert;
8 | import com.baomidou.mybatisplus.core.toolkit.StringUtils;
9 | import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
10 | import com.baomidou.mybatisplus.extension.toolkit.SqlHelper;
11 | import com.github.jeffreyning.mybatisplus.anno.MppMultiId;
12 | import com.github.jeffreyning.mybatisplus.base.MppBaseMapper;
13 | import com.github.jeffreyning.mybatisplus.util.CheckId;
14 | import org.apache.ibatis.binding.MapperMethod;
15 | import org.springframework.transaction.annotation.Transactional;
16 | import java.lang.reflect.Field;
17 | import java.util.*;
18 |
19 |
20 | /**
21 | * @author ninghao
22 | */
23 | public class MppServiceImpl, T> extends ServiceImpl implements IMppService {
24 |
25 | private String getCol(List fieldList, String attrName){
26 | for(TableFieldInfo tableFieldInfo: fieldList){
27 | String prop=tableFieldInfo.getProperty();
28 | if(prop.equals(attrName)){
29 | return tableFieldInfo.getColumn();
30 | }
31 | }
32 | throw new RuntimeException("not found column for "+attrName);
33 | }
34 |
35 | private Map checkIdCol(Class> modelClass, TableInfo tableInfo){
36 | List fieldList=tableInfo.getFieldList();
37 | Map idMap=new HashMap();
38 | for(TableFieldInfo fieldInfo: fieldList){
39 | Field field=fieldInfo.getField();
40 | MppMultiId mppMultiId= field.getAnnotation(MppMultiId.class);
41 | if(mppMultiId!=null){
42 | String attrName=field.getName();
43 | String colName=getCol(fieldList, attrName);
44 | idMap.put(attrName, colName);
45 | }
46 | }
47 | //add 1.7.2
48 | CheckId.appendIdColum(modelClass,tableInfo,idMap);
49 | return idMap;
50 | }
51 |
52 | @Transactional(
53 | rollbackFor = {Exception.class}
54 | )
55 | public boolean saveOrUpdateByMultiId(T entity) {
56 | if (null == entity) {
57 | return false;
58 | } else {
59 | Class> cls = entity.getClass();
60 | TableInfo tableInfo = TableInfoHelper.getTableInfo(cls);
61 | Assert.notNull(tableInfo, "error: can not execute. because can not find cache of TableInfo for entity!", new Object[0]);
62 |
63 | Map idMap=checkIdCol(cls, tableInfo);
64 | Assert.notEmpty(idMap, "entity {} not contain MppMultiId anno", new Object[]{cls.getName()});
65 |
66 | boolean updateFlag=true;
67 | for(String attr: idMap.keySet()){
68 | if(StringUtils.checkValNull(attr)){
69 | updateFlag=false;
70 | break;
71 | }
72 | }
73 | if(updateFlag){
74 | Object obj=this.selectByMultiId(entity);
75 | if(Objects.isNull(obj)){
76 | updateFlag=false;
77 | }
78 | }
79 | return updateFlag ? this.updateByMultiId(entity) : this.save(entity);
80 | }
81 | }
82 |
83 | @Transactional(
84 | rollbackFor = {Exception.class}
85 | )
86 | public boolean saveOrUpdateBatchByMultiId(Collection entityList, int batchSize) {
87 | TableInfo tableInfo = TableInfoHelper.getTableInfo(this.getEntityClass());
88 | Assert.notNull(tableInfo, "error: can not execute. because can not find cache of TableInfo for entity!", new Object[0]);
89 |
90 | Map idMap=checkIdCol(this.getEntityClass(), tableInfo);
91 | Assert.notEmpty(idMap, "entity {} not contain MppMultiId anno", new Object[]{this.getEntityClass().getName()});
92 |
93 | return this.executeBatch(entityList, batchSize, (sqlSession, entity) -> {
94 | boolean updateFlag=true;
95 | for(String attr: idMap.keySet()){
96 | if(StringUtils.checkValNull(attr)){
97 | updateFlag=false;
98 | break;
99 | }
100 | }
101 | if(updateFlag){
102 | Object obj=this.selectByMultiId(entity);
103 | if(Objects.isNull(obj)){
104 | updateFlag=false;
105 | }
106 | }
107 | if (updateFlag) {
108 | MapperMethod.ParamMap param = new MapperMethod.ParamMap();
109 | param.put("et", entity);
110 | sqlSession.update(tableInfo.getSqlStatement("updateByMultiId"), param);
111 | } else {
112 | sqlSession.insert(tableInfo.getSqlStatement(SqlMethod.INSERT_ONE.getMethod()), entity);
113 | }
114 |
115 | });
116 | }
117 |
118 | @Transactional(
119 | rollbackFor = {Exception.class}
120 | )
121 | public boolean updateBatchByMultiId(Collection entityList, int batchSize) {
122 | String sqlStatement = SqlHelper.table(this.getEntityClass()).getSqlStatement("updateByMultiId");
123 | return this.executeBatch(entityList, batchSize, (sqlSession, entity) -> {
124 | MapperMethod.ParamMap param = new MapperMethod.ParamMap();
125 | param.put("et", entity);
126 | sqlSession.update(sqlStatement, param);
127 | });
128 | }
129 | }
130 |
--------------------------------------------------------------------------------
/src/main/java/com/github/jeffreyning/mybatisplus/util/CheckId.java:
--------------------------------------------------------------------------------
1 | package com.github.jeffreyning.mybatisplus.util;
2 |
3 | import com.baomidou.mybatisplus.core.metadata.TableInfo;
4 | import com.baomidou.mybatisplus.core.toolkit.StringUtils;
5 | import com.github.jeffreyning.mybatisplus.anno.MppMultiId;
6 |
7 | import java.lang.reflect.Field;
8 | import java.util.Map;
9 |
10 | public class CheckId {
11 | //add 1.7.2
12 | public static Field getIdField(Class> modelClass, String fieldName){
13 | try {
14 | Field field=modelClass.getDeclaredField(fieldName);
15 | return field;
16 | } catch (NoSuchFieldException e) {
17 | Class superclass=modelClass.getSuperclass();
18 | if(superclass!=null) {
19 | Field superField = getIdField(superclass, fieldName);
20 | return superField;
21 | }else{
22 | return null;
23 | }
24 | }
25 | }
26 | //add 1.7.2
27 | public static boolean appendIdColum(Class> modelClass, TableInfo tableInfo, Map idMap){
28 | String keycol=tableInfo.getKeyColumn();
29 | String keypro=tableInfo.getKeyProperty();
30 | if(StringUtils.checkValNull(keypro)){
31 | return false;
32 | }
33 | Field idField=getIdField(modelClass, keypro);
34 | if(idField!=null){
35 | MppMultiId mppMultiId=idField.getAnnotation(MppMultiId.class);
36 | if(mppMultiId!=null){
37 | String attrName=keypro;
38 | String colName=keycol;
39 | idMap.put(attrName, colName);
40 | return true;
41 | }
42 | }
43 | return false;
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/src/main/java/com/github/jeffreyning/mybatisplus/util/ColNameUtil.java:
--------------------------------------------------------------------------------
1 | package com.github.jeffreyning.mybatisplus.util;
2 |
3 | import com.baomidou.mybatisplus.annotation.TableField;
4 | import com.baomidou.mybatisplus.annotation.TableId;
5 |
6 | import java.lang.invoke.SerializedLambda;
7 | import java.lang.reflect.Field;
8 | import java.lang.reflect.Method;
9 | import java.util.Locale;
10 |
11 | /**
12 | * @author ninghao
13 | */
14 | public class ColNameUtil {
15 | public static String pn(ParseFun parseFun) {
16 | Object sl =null;
17 | try {
18 | Method writeReplace = parseFun.getClass().getDeclaredMethod("writeReplace");
19 | writeReplace.setAccessible(true);
20 | sl = writeReplace.invoke(parseFun);
21 | }catch (Exception e){
22 | throw new RuntimeException("parseColName error",e);
23 | }
24 | SerializedLambda serializedLambda = (SerializedLambda) sl;
25 | String className=serializedLambda.getImplClass();
26 | className=className.replace("/",".");
27 | String methodName = serializedLambda.getImplMethodName();
28 | if (methodName.startsWith("is")) {
29 | methodName = methodName.substring(2);
30 | } else {
31 | if (!methodName.startsWith("get") && !methodName.startsWith("set")) {
32 | throw new RuntimeException("Error parsing property name '" + methodName
33 | + "'. Didn't start with 'is', 'get' or 'set'.");
34 | }
35 | methodName = methodName.substring(3);
36 | }
37 | if (methodName.length() == 1 || (methodName.length() > 1 && !Character
38 | .isUpperCase(methodName.charAt(1)))) {
39 | methodName = methodName.substring(0, 1).toLowerCase(Locale.ENGLISH) + methodName.substring(1);
40 | }
41 | return getColName(className, methodName);
42 | }
43 | private static String getColName(String className, String attrName) {
44 | Class cls=null;
45 | Field field= null;
46 | try {
47 | cls=Class.forName(className);
48 | field = cls.getDeclaredField(attrName);
49 | } catch (Exception e) {
50 | throw new RuntimeException("getColName error", e);
51 | }
52 | TableField tableField=field.getAnnotation(TableField.class);
53 | if(tableField!=null){
54 | return tableField.value();
55 | }
56 | TableId tableId=field.getAnnotation(TableId.class);
57 | if(tableId!=null){
58 | return tableId.value();
59 | }
60 | throw new RuntimeException("can not found colname for "+className+"."+attrName);
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/src/main/java/com/github/jeffreyning/mybatisplus/util/LambdaUtil.java:
--------------------------------------------------------------------------------
1 | package com.github.jeffreyning.mybatisplus.util;
2 |
3 | import com.baomidou.mybatisplus.annotation.TableField;
4 | import com.baomidou.mybatisplus.annotation.TableId;
5 |
6 | import java.lang.reflect.Field;
7 | //import java.lang.reflect.Modifier;
8 | import java.util.HashMap;
9 | import java.util.Map;
10 |
11 | /**
12 | * @author ninghao
13 | */
14 | public class LambdaUtil {
15 | private static Map colDict=new HashMap<>();
16 | public static String parseFunc(String lamdbaFunc) {
17 | String colName=colDict.get(lamdbaFunc);
18 | if(colName==null || "".equals(colName)){
19 | throw new RuntimeException("can not found colName for "+lamdbaFunc);
20 | }
21 | return colName;
22 | }
23 | //for jdk11 cancel ognl 202203 mpp1.7.0
24 | /* public static void setValue(Class cls, String fileName, Object value)
25 | throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
26 | Field field = cls.getDeclaredField(fileName);
27 | field.setAccessible(true);
28 | Field modifiersField = Field.class.getDeclaredField("modifiers");
29 | modifiersField.setAccessible(true);
30 | modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL);
31 | field.set(cls, value);
32 | }*/
33 | public static void createColDict(Class cls){
34 | Field[] fields=cls.getDeclaredFields();
35 | for (Field field:fields){
36 | TableId annoId=field.getAnnotation(TableId.class);
37 | if(annoId!=null){
38 | String colName=annoId.value();
39 | String fieldName=field.getName();
40 | putColDict(fieldName, colName, cls);
41 | }else {
42 | TableField anno = field.getAnnotation(TableField.class);
43 | if (anno == null) {
44 | continue;
45 | }
46 | String colName=anno.value();
47 | String fieldName=field.getName();
48 | putColDict(fieldName, colName, cls);
49 | }
50 | }
51 | }
52 | private static void putColDict(String fieldName, String colName, Class cls){
53 | if(colName==null || "".equals(colName)){
54 | colName=fieldName;
55 | }
56 | String methodName="get"+fieldName.substring(0,1).toUpperCase()+fieldName.substring(1);
57 | String clsName= cls.getSimpleName();
58 | String key=clsName+"::"+methodName;
59 | colDict.put(key, colName);
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/src/main/java/com/github/jeffreyning/mybatisplus/util/ParseFun.java:
--------------------------------------------------------------------------------
1 | package com.github.jeffreyning.mybatisplus.util;
2 |
3 | import java.io.Serializable;
4 |
5 | @FunctionalInterface
6 | public interface ParseFun extends java.util.function.Function, Serializable {
7 |
8 | }
9 |
--------------------------------------------------------------------------------
/src/main/java/com/github/jeffreyning/mybatisplus/util/PlusACUtils.java:
--------------------------------------------------------------------------------
1 | package com.github.jeffreyning.mybatisplus.util;
2 |
3 |
4 | import org.slf4j.Logger;
5 | import org.slf4j.LoggerFactory;
6 | import org.springframework.beans.BeansException;
7 | import org.springframework.context.ApplicationContext;
8 | import org.springframework.context.ApplicationContextAware;
9 | import org.springframework.stereotype.Component;
10 |
11 | @Component
12 | public class PlusACUtils implements ApplicationContextAware {
13 | private static final Logger logger = LoggerFactory.getLogger(PlusACUtils.class);
14 | private static ApplicationContext context;
15 |
16 |
17 | public static T getBean(Class tClass) {
18 | try {
19 | return context.getBean(tClass);
20 |
21 | } catch (Exception e) {
22 | logger.error("can not get bean="+tClass.getName(), e);
23 | }
24 | return null;
25 | }
26 |
27 | @Override
28 | public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
29 | context = applicationContext;
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/src/main/java/com/github/jeffreyning/mybatisplus/util/ReadValueUtil.java:
--------------------------------------------------------------------------------
1 | package com.github.jeffreyning.mybatisplus.util;
2 |
3 |
4 | import org.apache.ibatis.type.*;
5 | import java.math.BigDecimal;
6 | import java.math.BigInteger;
7 | import java.sql.ResultSet;
8 | import java.sql.SQLException;
9 | import java.sql.Timestamp;
10 | import java.time.*;
11 | import java.util.Date;
12 |
13 | /**
14 | * 自动填充时sql返回值与entity字段类型转换
15 | * @author ninghao
16 | * @version 1.7.4
17 | */
18 | public class ReadValueUtil {
19 |
20 | public static Object readValue(ResultSet rs, Class fieldType) throws SQLException {
21 | if(fieldType.equals(Integer.class)) {
22 | IntegerTypeHandler typeHandler = new IntegerTypeHandler();
23 | return typeHandler.getNullableResult(rs,1);
24 | }else if(fieldType.equals(Boolean.class)){
25 | BooleanTypeHandler typeHandler=new BooleanTypeHandler();
26 | return typeHandler.getNullableResult(rs,1);
27 | }else if(fieldType.equals(Byte.class)) {
28 | ByteTypeHandler typeHandler=new ByteTypeHandler();
29 | return typeHandler.getNullableResult(rs,1);
30 | }else if(fieldType.equals(Short.class)){
31 | ShortTypeHandler typeHandler=new ShortTypeHandler();
32 | return typeHandler.getNullableResult(rs,1);
33 | }else if(fieldType.equals(Long.class)){
34 | LongTypeHandler typeHandler=new LongTypeHandler();
35 | return typeHandler.getNullableResult(rs,1);
36 | }else if(fieldType.equals(Float.class)){
37 | FloatTypeHandler typeHandler=new FloatTypeHandler();
38 | return typeHandler.getNullableResult(rs,1);
39 | }else if(fieldType.equals(Double.class)){
40 | DoubleTypeHandler typeHandler=new DoubleTypeHandler();
41 | return typeHandler.getNullableResult(rs,1);
42 | }else if(fieldType.equals(BigDecimal.class)) {
43 | BigDecimalTypeHandler typeHandler=new BigDecimalTypeHandler();
44 | return typeHandler.getNullableResult(rs,1);
45 | }else if(fieldType.equals(String.class)) {
46 | StringTypeHandler typeHandler=new StringTypeHandler();
47 | return typeHandler.getNullableResult(rs,1);
48 | }else if(fieldType.equals(BigInteger.class)) {
49 | BigIntegerTypeHandler typeHandler=new BigIntegerTypeHandler();
50 | return typeHandler.getNullableResult(rs,1);
51 | }else if(fieldType.equals(Character.class)) {
52 | CharacterTypeHandler typeHandler=new CharacterTypeHandler();
53 | return typeHandler.getNullableResult(rs,1);
54 | }else if(fieldType.equals(Date.class)) {
55 | DateTypeHandler typeHandler=new DateTypeHandler();
56 | return typeHandler.getNullableResult(rs,1);
57 | }else if(fieldType.equals(LocalDateTime.class)) {
58 | LocalDateTimeTypeHandler typeHandler=new LocalDateTimeTypeHandler();
59 | return typeHandler.getNullableResult(rs,1);
60 | }else if(fieldType.equals(LocalDate.class)) {
61 | LocalDateTypeHandler typeHandler=new LocalDateTypeHandler();
62 | return typeHandler.getNullableResult(rs,1);
63 | }else if(fieldType.equals(LocalTime.class)) {
64 | LocalTimeTypeHandler typeHandler=new LocalTimeTypeHandler();
65 | return typeHandler.getNullableResult(rs,1);
66 | }else if(fieldType.equals(Month.class)) {
67 | MonthTypeHandler typeHandler=new MonthTypeHandler();
68 | return typeHandler.getNullableResult(rs,1);
69 | }else if(fieldType.equals(java.sql.Date.class)) {
70 | SqlDateTypeHandler typeHandler=new SqlDateTypeHandler();
71 | return typeHandler.getNullableResult(rs,1);
72 | }else if(fieldType.equals(java.sql.Timestamp.class)) {
73 | SqlTimestampTypeHandler typeHandler=new SqlTimestampTypeHandler();
74 | return typeHandler.getNullableResult(rs,1);
75 | }else if(fieldType.equals(java.sql.Time.class)) {
76 | SqlTimeTypeHandler typeHandler=new SqlTimeTypeHandler();
77 | return typeHandler.getNullableResult(rs,1);
78 | }else if(fieldType.equals(YearMonth.class)) {
79 | YearMonthTypeHandler typeHandler=new YearMonthTypeHandler();
80 | return typeHandler.getNullableResult(rs,1);
81 | }else if(fieldType.equals(Year.class)) {
82 | YearTypeHandler typeHandler=new YearTypeHandler();
83 | return typeHandler.getNullableResult(rs,1);
84 | }else if(fieldType.equals(ZonedDateTime.class)) {
85 | ZonedDateTimeTypeHandler typeHandler=new ZonedDateTimeTypeHandler();
86 | return typeHandler.getNullableResult(rs,1);
87 | }else{
88 | return rs.getObject(1);
89 | }
90 | }
91 | }
92 |
--------------------------------------------------------------------------------