Skip to content

Commit 571ba82

Browse files
author
hewei
committed
fixed:[issues#2]
增加存在即更新插件
1 parent 9d0068d commit 571ba82

File tree

2 files changed

+370
-1
lines changed

2 files changed

+370
-1
lines changed

README.md

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,15 @@
1212
* 批量插入插件(BatchInsertPlugin)
1313
* 逻辑删除插件(LogicalDeletePlugin)
1414
* 数据Model属性对应Column获取插件(ModelColumnPlugin)
15+
* 存在即更新插件(UpsertPlugin)
1516

1617
---------------------------------------
1718
Maven引用:
1819
```xml
1920
<dependency>
2021
<groupId>com.itfsw</groupId>
2122
<artifactId>mybatis-generator-plugin</artifactId>
22-
<version>1.0.5</version>
23+
<version>1.0.6</version>
2324
</dependency>
2425
```
2526
---------------------------------------
@@ -400,4 +401,38 @@ public class Test {
400401
this.tbMapper.batchInsertSelective(list, Tb.Column.field1, Tb.Column.field2, Tb.Column.field3, Tb.Column.createTime);
401402
}
402403
}
404+
```
405+
### 9. 存在即更新插件
406+
使用MySQL的[“insert ... on duplicate key update”](https://dev.mysql.com/doc/refman/5.7/en/insert-on-duplicate.html)实现存在即更新操作,简化数据入库操作([[issues#2]](https://github.com/itfsw/mybatis-generator-plugin/issues/2))。
407+
408+
插件:
409+
```xml
410+
<!-- 存在即更新插件 -->
411+
<plugin type="com.itfsw.mybatis.generator.plugins.UpsertPlugin"/>
412+
```
413+
使用:
414+
```java
415+
public class Test {
416+
public static void main(String[] args) {
417+
// 1. 未入库数据入库,执行insert
418+
Tb tb = new Tb.Builder()
419+
.field1(1)
420+
.field2("xx0")
421+
.delFlag((short)0)
422+
.build();
423+
int k0 = this.tbMapper.upsert(tb);
424+
// 2. 已入库数据再次入库,执行update(!!需要注意如触发update其返回的受影响行数为2)
425+
tb.setField2("xx1");
426+
int k1 = this.tbMapper.upsert(tb);
427+
428+
// 3. 类似insertSelective实现选择入库
429+
Tb tb1 = new Tb.Builder()
430+
.field1(1)
431+
.field2("xx0")
432+
.build();
433+
int k2 = this.tbMapper.upsertSelective(tb1);
434+
tb1.setField2("xx1");
435+
int k3 = this.tbMapper.upsertSelective(tb1);
436+
}
437+
}
403438
```
Lines changed: 334 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,334 @@
1+
/*
2+
* Copyright (c) 2017.
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+
package com.itfsw.mybatis.generator.plugins;
18+
19+
import com.itfsw.mybatis.generator.plugins.utils.CommTools;
20+
import com.itfsw.mybatis.generator.plugins.utils.CommentTools;
21+
import org.mybatis.generator.api.IntrospectedColumn;
22+
import org.mybatis.generator.api.IntrospectedTable;
23+
import org.mybatis.generator.api.PluginAdapter;
24+
import org.mybatis.generator.api.dom.java.*;
25+
import org.mybatis.generator.api.dom.xml.*;
26+
import org.mybatis.generator.codegen.mybatis3.MyBatis3FormattingUtilities;
27+
import org.mybatis.generator.internal.util.StringUtility;
28+
import org.slf4j.Logger;
29+
import org.slf4j.LoggerFactory;
30+
31+
import java.util.List;
32+
33+
/**
34+
* ---------------------------------------------------------------------------
35+
* 存在即更新插件
36+
* ---------------------------------------------------------------------------
37+
* @author: hewei
38+
* @time:2017/3/21 10:59
39+
* ---------------------------------------------------------------------------
40+
*/
41+
public class UpsertPlugin extends PluginAdapter {
42+
private static final Logger logger = LoggerFactory.getLogger(BatchInsertPlugin.class);
43+
public static final String METHOD_UPSERT = "upsert"; // 方法名
44+
public static final String METHOD_UPSERT_SELECTIVE = "upsertSelective"; // 方法名
45+
46+
/**
47+
* {@inheritDoc}
48+
*/
49+
@Override
50+
public boolean validate(List<String> warnings) {
51+
// 插件使用前提是targetRuntime为MyBatis3
52+
if (StringUtility.stringHasValue(getContext().getTargetRuntime()) && "MyBatis3".equalsIgnoreCase(getContext().getTargetRuntime()) == false) {
53+
logger.warn("itfsw:插件" + this.getClass().getTypeName() + "要求运行targetRuntime必须为MyBatis3!");
54+
return false;
55+
}
56+
57+
// 插件使用前提是数据库为MySQL
58+
if ("com.mysql.jdbc.Driver".equalsIgnoreCase(this.getContext().getJdbcConnectionConfiguration().getDriverClass()) == false){
59+
logger.warn("itfsw:插件" + this.getClass().getTypeName() + "插件使用前提是数据库为MySQL!");
60+
return false;
61+
}
62+
63+
return true;
64+
}
65+
66+
/**
67+
* Java Client Methods 生成
68+
* 具体执行顺序 http://www.mybatis.org/generator/reference/pluggingIn.html
69+
*
70+
* @param interfaze
71+
* @param topLevelClass
72+
* @param introspectedTable
73+
* @return
74+
*/
75+
@Override
76+
public boolean clientGenerated(Interface interfaze, TopLevelClass topLevelClass, IntrospectedTable introspectedTable) {
77+
// ====================================== 1. upsert ======================================
78+
Method mUpsert = new Method(METHOD_UPSERT);
79+
// 返回值类型
80+
mUpsert.setReturnType(FullyQualifiedJavaType.getIntInstance());
81+
// 添加参数
82+
mUpsert.addParameter(new Parameter(introspectedTable.getRules().calculateAllFieldsClass(), "record"));
83+
// 添加方法说明
84+
CommentTools.addGeneralMethodComment(mUpsert, introspectedTable);
85+
// interface 增加方法
86+
interfaze.addMethod(mUpsert);
87+
logger.debug("itfsw(存在即更新插件):" + interfaze.getType().getShortName() + "增加upsert方法。");
88+
89+
// ====================================== 2. upsertSelective ======================================
90+
Method mUpsertSelective = new Method(METHOD_UPSERT_SELECTIVE);
91+
// 返回值类型
92+
mUpsertSelective.setReturnType(FullyQualifiedJavaType.getIntInstance());
93+
// 添加参数
94+
mUpsertSelective.addParameter(new Parameter(introspectedTable.getRules().calculateAllFieldsClass(), "record"));
95+
// 添加方法说明
96+
CommentTools.addGeneralMethodComment(mUpsertSelective, introspectedTable);
97+
// interface 增加方法
98+
interfaze.addMethod(mUpsertSelective);
99+
logger.debug("itfsw(存在即更新插件):" + interfaze.getType().getShortName() + "增加upsertSelective方法。");
100+
101+
return true;
102+
}
103+
104+
/**
105+
* SQL Map Methods 生成
106+
* 具体执行顺序 http://www.mybatis.org/generator/reference/pluggingIn.html
107+
*
108+
* @param document
109+
* @param introspectedTable
110+
* @return
111+
*/
112+
@Override
113+
public boolean sqlMapDocumentGenerated(Document document, IntrospectedTable introspectedTable) {
114+
115+
// ====================================== 1. upsert ======================================
116+
XmlElement eleUpsert = new XmlElement("insert");
117+
eleUpsert.addAttribute(new Attribute("id", METHOD_UPSERT));
118+
// 添加注释(!!!必须添加注释,overwrite覆盖生成时,@see XmlFileMergerJaxp.isGeneratedNode会去判断注释中是否存在OLD_ELEMENT_TAGS中的一点,例子:@mbg.generated)
119+
CommentTools.addComment(eleUpsert);
120+
121+
// 参数类型
122+
eleUpsert.addAttribute(new Attribute("parameterType", introspectedTable.getRules().calculateAllFieldsClass().getFullyQualifiedName()));
123+
124+
// 使用JDBC的getGenereatedKeys方法获取主键并赋值到keyProperty设置的领域模型属性中。所以只支持MYSQL和SQLServer
125+
CommTools.useGeneratedKeys(eleUpsert, introspectedTable);
126+
127+
// insert
128+
eleUpsert.addElement(new TextElement("insert into " + introspectedTable.getFullyQualifiedTableNameAtRuntime()));
129+
eleUpsert.addElement(this.generateInsertClause(introspectedTable));
130+
eleUpsert.addElement(new TextElement("values"));
131+
eleUpsert.addElement(this.generateValuesClause(introspectedTable));
132+
eleUpsert.addElement(new TextElement("on duplicate key update "));
133+
eleUpsert.addElement(this.generateDuplicateClause(introspectedTable));
134+
135+
document.getRootElement().addElement(eleUpsert);
136+
logger.debug("itfsw(存在即更新插件):" + introspectedTable.getMyBatis3XmlMapperFileName() + "增加upsert实现方法。");
137+
138+
// ====================================== 2. upsertSelective ======================================
139+
XmlElement eleUpsertSelective = new XmlElement("insert");
140+
eleUpsertSelective.addAttribute(new Attribute("id", METHOD_UPSERT_SELECTIVE));
141+
// 添加注释(!!!必须添加注释,overwrite覆盖生成时,@see XmlFileMergerJaxp.isGeneratedNode会去判断注释中是否存在OLD_ELEMENT_TAGS中的一点,例子:@mbg.generated)
142+
CommentTools.addComment(eleUpsertSelective);
143+
144+
// 参数类型
145+
eleUpsertSelective.addAttribute(new Attribute("parameterType", introspectedTable.getRules().calculateAllFieldsClass().getFullyQualifiedName()));
146+
147+
// 使用JDBC的getGenereatedKeys方法获取主键并赋值到keyProperty设置的领域模型属性中。所以只支持MYSQL和SQLServer
148+
CommTools.useGeneratedKeys(eleUpsertSelective, introspectedTable);
149+
150+
// insert
151+
eleUpsertSelective.addElement(new TextElement("insert into " + introspectedTable.getFullyQualifiedTableNameAtRuntime()));
152+
eleUpsertSelective.addElement(this.generateInsertSelectiveClause(introspectedTable));
153+
eleUpsertSelective.addElement(new TextElement("values"));
154+
eleUpsertSelective.addElement(this.generateValuesSelectiveClause(introspectedTable));
155+
eleUpsertSelective.addElement(new TextElement("on duplicate key update "));
156+
eleUpsertSelective.addElement(this.generateDuplicateSelectiveClause(introspectedTable));
157+
158+
document.getRootElement().addElement(eleUpsertSelective);
159+
logger.debug("itfsw(存在即更新插件):" + introspectedTable.getMyBatis3XmlMapperFileName() + "增加upsertSelective实现方法。");
160+
161+
return true;
162+
}
163+
164+
/**
165+
* 普通insert
166+
*
167+
* @param introspectedTable
168+
* @return
169+
*/
170+
private Element generateInsertClause(IntrospectedTable introspectedTable){
171+
StringBuilder insertClause = new StringBuilder();
172+
173+
insertClause.append(" (");
174+
175+
List<IntrospectedColumn> columns = introspectedTable.getAllColumns();
176+
for (int i = 0; i < columns.size(); i++) {
177+
IntrospectedColumn introspectedColumn = columns.get(i);
178+
179+
insertClause.append(MyBatis3FormattingUtilities.getEscapedColumnName(introspectedColumn));
180+
181+
if (i + 1 < columns.size()) {
182+
insertClause.append(", ");
183+
}
184+
}
185+
186+
insertClause.append(") ");
187+
188+
return new TextElement(insertClause.toString());
189+
}
190+
191+
/**
192+
* 普通 values
193+
*
194+
* @param introspectedTable
195+
* @return
196+
*/
197+
private Element generateValuesClause(IntrospectedTable introspectedTable){
198+
StringBuilder valuesClause = new StringBuilder();
199+
200+
valuesClause.append(" (");
201+
202+
List<IntrospectedColumn> columns = introspectedTable.getAllColumns();
203+
for (int i = 0; i < columns.size(); i++) {
204+
IntrospectedColumn introspectedColumn = columns.get(i);
205+
206+
valuesClause.append(MyBatis3FormattingUtilities.getParameterClause(introspectedColumn));
207+
if (i + 1 < columns.size()) {
208+
valuesClause.append(", ");
209+
}
210+
}
211+
212+
valuesClause.append(") ");
213+
214+
return new TextElement(valuesClause.toString());
215+
}
216+
217+
/**
218+
* 普通duplicate
219+
*
220+
* @param introspectedTable
221+
* @return
222+
*/
223+
private Element generateDuplicateClause(IntrospectedTable introspectedTable){
224+
StringBuilder duplicateClause = new StringBuilder();
225+
226+
List<IntrospectedColumn> columns = introspectedTable.getAllColumns();
227+
for (int i = 0; i < columns.size(); i++) {
228+
IntrospectedColumn introspectedColumn = columns.get(i);
229+
230+
duplicateClause.append(MyBatis3FormattingUtilities.getEscapedColumnName(introspectedColumn));
231+
duplicateClause.append(" = ");
232+
duplicateClause.append(MyBatis3FormattingUtilities.getParameterClause(introspectedColumn));
233+
234+
if (i + 1 < columns.size()) {
235+
duplicateClause.append(", ");
236+
}
237+
}
238+
239+
return new TextElement(duplicateClause.toString());
240+
}
241+
242+
/**
243+
* 普通insert
244+
*
245+
* @param introspectedTable
246+
* @return
247+
*/
248+
private Element generateInsertSelectiveClause(IntrospectedTable introspectedTable){
249+
XmlElement insertTrimEle = new XmlElement("trim");
250+
insertTrimEle.addAttribute(new Attribute("prefix", "("));
251+
insertTrimEle.addAttribute(new Attribute("suffix", ")"));
252+
insertTrimEle.addAttribute(new Attribute("suffixOverrides", ","));
253+
254+
StringBuffer sb = new StringBuffer();
255+
for (IntrospectedColumn introspectedColumn : introspectedTable.getAllColumns()) {
256+
257+
XmlElement insertNotNullElement = new XmlElement("if"); //$NON-NLS-1$
258+
sb.setLength(0);
259+
sb.append(introspectedColumn.getJavaProperty());
260+
sb.append(" != null");
261+
insertNotNullElement.addAttribute(new Attribute("test", sb.toString()));
262+
263+
sb.setLength(0);
264+
sb.append(MyBatis3FormattingUtilities.getEscapedColumnName(introspectedColumn));
265+
sb.append(',');
266+
insertNotNullElement.addElement(new TextElement(sb.toString()));
267+
insertTrimEle.addElement(insertNotNullElement);
268+
}
269+
270+
return insertTrimEle;
271+
}
272+
273+
/**
274+
* 普通 values
275+
*
276+
* @param introspectedTable
277+
* @return
278+
*/
279+
private Element generateValuesSelectiveClause(IntrospectedTable introspectedTable){
280+
XmlElement valuesTrimEle = new XmlElement("trim");
281+
valuesTrimEle.addAttribute(new Attribute("prefix", "("));
282+
valuesTrimEle.addAttribute(new Attribute("suffix", ")"));
283+
valuesTrimEle.addAttribute(new Attribute("suffixOverrides", ","));
284+
285+
StringBuffer sb = new StringBuffer();
286+
for (IntrospectedColumn introspectedColumn : introspectedTable.getAllColumns()) {
287+
288+
XmlElement valuesNotNullElement = new XmlElement("if"); //$NON-NLS-1$
289+
sb.setLength(0);
290+
sb.append(introspectedColumn.getJavaProperty());
291+
sb.append(" != null");
292+
valuesNotNullElement.addAttribute(new Attribute("test", sb.toString()));
293+
294+
sb.setLength(0);
295+
sb.append(MyBatis3FormattingUtilities.getParameterClause(introspectedColumn));
296+
sb.append(',');
297+
valuesNotNullElement.addElement(new TextElement(sb.toString()));
298+
valuesTrimEle.addElement(valuesNotNullElement);
299+
}
300+
return valuesTrimEle;
301+
}
302+
303+
/**
304+
* 普通duplicate
305+
*
306+
* @param introspectedTable
307+
* @return
308+
*/
309+
private Element generateDuplicateSelectiveClause(IntrospectedTable introspectedTable){
310+
XmlElement duplicateTrimEle = new XmlElement("trim");
311+
duplicateTrimEle.addAttribute(new Attribute("suffixOverrides", ","));
312+
313+
StringBuffer sb = new StringBuffer();
314+
for (IntrospectedColumn introspectedColumn : introspectedTable.getAllColumns()) {
315+
316+
XmlElement duplicateNotNullElement = new XmlElement("if"); //$NON-NLS-1$
317+
sb.setLength(0);
318+
sb.append(introspectedColumn.getJavaProperty());
319+
sb.append(" != null");
320+
duplicateNotNullElement.addAttribute(new Attribute("test", sb.toString()));
321+
322+
sb.setLength(0);
323+
sb.append(MyBatis3FormattingUtilities.getEscapedColumnName(introspectedColumn));
324+
sb.append(" = ");
325+
sb.append(MyBatis3FormattingUtilities.getParameterClause(introspectedColumn));
326+
sb.append(",");
327+
328+
duplicateNotNullElement.addElement(new TextElement(sb.toString()));
329+
duplicateTrimEle.addElement(duplicateNotNullElement);
330+
}
331+
return duplicateTrimEle;
332+
}
333+
334+
}

0 commit comments

Comments
 (0)