一、前言

如果项目中使用到了MyBatis框架,那么使用通用Mapper和PageHelper分页插件将极大的简化我们的操作。通用Mapper可以简化对单表的CRUD操作,PageHelper分页插件可以帮我们自动拼接分页SQL,并且可以使用MyBatis Geneator来自动生成实体类,Mapper接口和Mapper xml代码,非常的方便。插件地址及作者链接https://gitee.com/free。

二、引入依赖

这里使用Spring Boot来构建,可参考 《Spring Boot整合MyBatis》 搭建一个Spring boot + MyBatis的框架,然后在pom中引入:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
       <!--mybatis-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.1</version>
</dependency>
<!-- mysql 驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.22</version>
</dependency>

<!--通用mapper-->
<!-- 通用Mapper插件文档地址:https://gitee.com/free/Mapper/wikis/Home -->
<dependency>
<groupId>tk.mybatis</groupId>
<artifactId>mapper-spring-boot-starter</artifactId>
<version>2.1.5</version>
</dependency>
<!--pagehelper 分页插件-->
<!-- 分页插件文档地址:https://github.com/pagehelper/Mybatis-PageHelper/blob/master/wikis/zh/HowToUse.md -->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
<version>1.2.13</version>
</dependency>

接着在pom中配置MyBatis Geneator:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
             <plugin>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-maven-plugin</artifactId>
<version>1.3.7</version>
<dependencies>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.21</version>
</dependency>
<dependency>
<groupId>tk.mybatis</groupId>
<artifactId>mapper</artifactId>
<version>4.1.5</version>
</dependency>
</dependencies>
<configuration>
<!-- 对应generator配置文件的路径 -->
<configurationFile>src/main/resources/mybatis-generator.xml</configurationFile>
<overwrite>true</overwrite>
<verbose>true</verbose>
</configuration>
</plugin>

三、配置Geneator

3.1 mybatis-generator.xml

在路径src/main/resources/下新建mybatis-generator.xml:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration
PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
"http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
<generatorConfiguration>

<!-- 本地数据库驱动程序jar包的全路径
<classPathEntry location="E:\repo\mysql\mysql-connector-java\8.0.21\mysql-connector-java-8.0.21.jar"/>
-->

<!-- 一个数据库一个context -->
<!--defaultModelType="flat" 大数据字段,不分表 -->
<context id="mysql" targetRuntime="MyBatis3Simple" defaultModelType="flat">

<!-- 自动识别数据库关键字,默认false,如果设置为true,根据SqlReservedWords中定义的关键字列表;
一般保留默认值,遇到数据库关键字(Java关键字),使用columnOverride覆盖 -->
<property name="autoDelimitKeywords" value="true" />
<!-- 生成的Java文件的编码 -->
<property name="javaFileEncoding" value="utf-8" />
<!-- beginningDelimiter和endingDelimiter:指明数据库的用于标记数据库对象名的符号,比如ORACLE就是双引号,MYSQL默认是`反引号; -->
<property name="beginningDelimiter" value="`" />
<property name="endingDelimiter" value="`" />

<!-- 格式化java代码 -->
<property name="javaFormatter" value="org.mybatis.generator.api.dom.DefaultJavaFormatter"/>
<!-- 格式化XML代码 -->
<property name="xmlFormatter" value="org.mybatis.generator.api.dom.DefaultXmlFormatter"/>

<plugin type="org.mybatis.generator.plugins.SerializablePlugin" />

<plugin type="org.mybatis.generator.plugins.ToStringPlugin" />

<plugin type="tk.mybatis.mapper.generator.MapperPlugin">
<property name="mappers" value="com.wno704.boot.config.BaseMapper" />
<!--caseSensitive默认false,当数据库表名区分大小写时,可以将该属性设置为true-->
<property name="caseSensitive" value="false"/>
</plugin>

<!-- 阻止生成自动注释 -->
<commentGenerator>
<property name="javaFileEncoding" value="UTF-8"/>
<!-- 是否生成注释代时间戳-->
<property name="suppressDate" value="true"/>
<!-- 是否取消注释 -->
<property name="suppressAllComments" value="true"/>
</commentGenerator>

<!--数据库链接地址账号密码-->
<jdbcConnection driverClass="com.mysql.cj.jdbc.Driver"
connectionURL="jdbc:mysql://127.0.0.1:3306/springboot?useUnicode=true&amp;characterEncoding=utf8&amp;serverTimezone=UTC&amp;allowMultiQueries=true&amp;useSSL=false"
userId="spring"
password="spring#123">
<!--MySQL 8.x 需要指定服务器的时区-->
<property name="serverTimezone" value="UTC"/>
<!--MySQL 不支持 schema 或者 catalog 所以需要添加这个-->
<!--参考 : http://www.mybatis.org/generator/usage/mysql.html-->
<property name="nullCatalogMeansCurrent" value="true"/>
<!-- MySQL8默认启用 SSL ,不关闭会有警告-->
<property name="useSSL" value="false"/>
</jdbcConnection>

<!-- 非必需,类型处理器,在数据库类型和java类型之间的转换控制-->
<javaTypeResolver>
<!-- 是否使用bigDecimal, false可自动转化以下类型(Long, Integer, Short, etc.) -->
<property name="forceBigDecimals" value="false"/>
</javaTypeResolver>

<!--生成Model类存放位置-->
<javaModelGenerator targetPackage="com.wno704.boot.model" targetProject="src/main/java">
<!-- 是否对model添加 构造函数 -->
<property name="constructorBased" value="false"/>
<!-- 是否允许子包,即targetPackage.schemaName.tableName -->
<property name="enableSubPackages" value="true"/>
<!-- 建立的Model对象是否 不可改变 即生成的Model对象不会有 setter方法,只有构造方法 -->
<property name="immutable" value="false"/>
<!-- 给Model添加一个父类 -->
<!--<property name="rootClass" value="com.foo.louis.Hello"/>-->
<!-- 是否对类CHAR类型的列的数据进行trim操作 -->
<property name="trimStrings" value="true"/>
</javaModelGenerator>

<!--生成映射文件存放位置-->
<sqlMapGenerator targetPackage="mapper" targetProject="src/main/resources">
<property name="enableSubPackages" value="false"/>
</sqlMapGenerator>

<!--生成Dao类存放位置-->
<!-- 客户端代码,生成易于使用的针对Model对象和XML配置文件 的代码
type="ANNOTATEDMAPPER",生成Java Model 和基于注解的Mapper对象
type="XMLMAPPER",生成SQLMap XML文件和独立的Mapper接口 -->
<javaClientGenerator type="XMLMAPPER" targetPackage="com.wno704.boot.mapper" targetProject="src/main/java">
<property name="enableSubPackages" value="false"/>
<!--定义Maper.java 源代码中的ByExample() 方法的可视性,可选的值有: public;private;protected;default,注意:如果 targetRuntime="MyBatis3",此参数被忽略
-->
<property name="exampleMethodVisibility" value=""/>
<!--方法名计数器Important note: this property is ignored if the target runtime is MyBatis3.-->
<property name="methodNameCalculator" value=""/>
<!--为生成的接口添加父接口-->
<property name="rootInterface" value=""/>
</javaClientGenerator>

<!--生成对应表及类名去掉Mybatis Generator生成的一堆 example-->
<table tableName="t_user" domainObjectName="User" enableCountByExample="false" enableUpdateByExample="false" enableDeleteByExample="false" enableSelectByExample="false" selectByExampleQueryId="false">
<generatedKey column="USER_ID" sqlStatement="mysql" identity="true"/>
</table>

<!--
<table tableName="t_dept" domainObjectName="Dept" enableCountByExample="false" enableUpdateByExample="false" enableDeleteByExample="false" enableSelectByExample="false" selectByExampleQueryId="false">
<generatedKey column="DEPT_ID" sqlStatement="mysql" identity="true"/>
</table>
-->
<!--
<table tableName="person" domainObjectName="Person" enableCountByExample="false"
enableUpdateByExample="false" enableDeleteByExample="false"
enableSelectByExample="false" selectByExampleQueryId="false">
<generatedKey column="id" sqlStatement="mysql" identity="true"/>
</table>
-->
</context>
</generatorConfiguration>

更详细的说明可参考链接: http://blog.csdn.net/isea533/article/details/42102297
代码生成

3.2 BaseMapper

1
2
3
4
public interface BaseMapper<T> extends Mapper<T>, MySqlMapper<T> {
}

值得注意的是,该接口不能被扫描到,应该和自己定义的Mapper分开。自己定义的Mapper都需要继承这个接口。

3.3 mybatis-generator:generate

配置好MyBatis Geneator后,在eclipse中运行命令mybatis-generator:generate:
如下图所示:

生成的代码如下:

以下为自动成成的代码:

3.3.1 User

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
@Table(name = "t_user")
public class User implements Serializable {
/**
* 用户ID
*/
@Id
@Column(name = "USER_ID")
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long userId;

/**
* 用户名
*/
@Column(name = "USERNAME")
private String username;

/**
* 密码
*/
@Column(name = "PASSWORD")
private String password;

/**
* 部门ID
*/
@Column(name = "DEPT_ID")
private Long deptId;

/**
* 邮箱
*/
@Column(name = "EMAIL")
private String email;

/**
* 联系电话
*/
@Column(name = "MOBILE")
private String mobile;

/**
* 状态 0锁定 1有效
*/
@Column(name = "STATUS")
private String status;

/**
* 创建时间
*/
@Column(name = "CREATE_TIME")
private Date createTime;

/**
* 修改时间
*/
@Column(name = "MODIFY_TIME")
private Date modifyTime;

/**
* 最近访问时间
*/
@Column(name = "LAST_LOGIN_TIME")
private Date lastLoginTime;

/**
* 性别 0男 1女 2保密
*/
@Column(name = "SSEX")
private String ssex;

/**
* 是否开启tab,0关闭 1开启
*/
@Column(name = "IS_TAB")
private String isTab;

/**
* 主题
*/
@Column(name = "THEME")
private String theme;

/**
* 头像
*/
@Column(name = "AVATAR")
private String avatar;

/**
* 描述
*/
@Column(name = "DESCRIPTION")
private String description;

private static final long serialVersionUID = 1L;

/**
* 获取用户ID
*
* @return USER_ID - 用户ID
*/
public Long getUserId() {
return userId;
}

/**
* 设置用户ID
*
* @param userId 用户ID
*/
public void setUserId(Long userId) {
this.userId = userId;
}

/**
* 获取用户名
*
* @return USERNAME - 用户名
*/
public String getUsername() {
return username;
}

/**
* 设置用户名
*
* @param username 用户名
*/
public void setUsername(String username) {
this.username = username == null ? null : username.trim();
}

/**
* 获取密码
*
* @return PASSWORD - 密码
*/
public String getPassword() {
return password;
}

/**
* 设置密码
*
* @param password 密码
*/
public void setPassword(String password) {
this.password = password == null ? null : password.trim();
}

/**
* 获取部门ID
*
* @return DEPT_ID - 部门ID
*/
public Long getDeptId() {
return deptId;
}

/**
* 设置部门ID
*
* @param deptId 部门ID
*/
public void setDeptId(Long deptId) {
this.deptId = deptId;
}

/**
* 获取邮箱
*
* @return EMAIL - 邮箱
*/
public String getEmail() {
return email;
}

/**
* 设置邮箱
*
* @param email 邮箱
*/
public void setEmail(String email) {
this.email = email == null ? null : email.trim();
}

/**
* 获取联系电话
*
* @return MOBILE - 联系电话
*/
public String getMobile() {
return mobile;
}

/**
* 设置联系电话
*
* @param mobile 联系电话
*/
public void setMobile(String mobile) {
this.mobile = mobile == null ? null : mobile.trim();
}

/**
* 获取状态 0锁定 1有效
*
* @return STATUS - 状态 0锁定 1有效
*/
public String getStatus() {
return status;
}

/**
* 设置状态 0锁定 1有效
*
* @param status 状态 0锁定 1有效
*/
public void setStatus(String status) {
this.status = status == null ? null : status.trim();
}

/**
* 获取创建时间
*
* @return CREATE_TIME - 创建时间
*/
public Date getCreateTime() {
return createTime;
}

/**
* 设置创建时间
*
* @param createTime 创建时间
*/
public void setCreateTime(Date createTime) {
this.createTime = createTime;
}

/**
* 获取修改时间
*
* @return MODIFY_TIME - 修改时间
*/
public Date getModifyTime() {
return modifyTime;
}

/**
* 设置修改时间
*
* @param modifyTime 修改时间
*/
public void setModifyTime(Date modifyTime) {
this.modifyTime = modifyTime;
}

/**
* 获取最近访问时间
*
* @return LAST_LOGIN_TIME - 最近访问时间
*/
public Date getLastLoginTime() {
return lastLoginTime;
}

/**
* 设置最近访问时间
*
* @param lastLoginTime 最近访问时间
*/
public void setLastLoginTime(Date lastLoginTime) {
this.lastLoginTime = lastLoginTime;
}

/**
* 获取性别 0男 1女 2保密
*
* @return SSEX - 性别 0男 1女 2保密
*/
public String getSsex() {
return ssex;
}

/**
* 设置性别 0男 1女 2保密
*
* @param ssex 性别 0男 1女 2保密
*/
public void setSsex(String ssex) {
this.ssex = ssex == null ? null : ssex.trim();
}

/**
* 获取是否开启tab,0关闭 1开启
*
* @return IS_TAB - 是否开启tab,0关闭 1开启
*/
public String getIsTab() {
return isTab;
}

/**
* 设置是否开启tab,0关闭 1开启
*
* @param isTab 是否开启tab,0关闭 1开启
*/
public void setIsTab(String isTab) {
this.isTab = isTab == null ? null : isTab.trim();
}

/**
* 获取主题
*
* @return THEME - 主题
*/
public String getTheme() {
return theme;
}

/**
* 设置主题
*
* @param theme 主题
*/
public void setTheme(String theme) {
this.theme = theme == null ? null : theme.trim();
}

/**
* 获取头像
*
* @return AVATAR - 头像
*/
public String getAvatar() {
return avatar;
}

/**
* 设置头像
*
* @param avatar 头像
*/
public void setAvatar(String avatar) {
this.avatar = avatar == null ? null : avatar.trim();
}

/**
* 获取描述
*
* @return DESCRIPTION - 描述
*/
public String getDescription() {
return description;
}

/**
* 设置描述
*
* @param description 描述
*/
public void setDescription(String description) {
this.description = description == null ? null : description.trim();
}

@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append(getClass().getSimpleName());
sb.append(" [");
sb.append("Hash = ").append(hashCode());
sb.append(", userId=").append(userId);
sb.append(", username=").append(username);
sb.append(", password=").append(password);
sb.append(", deptId=").append(deptId);
sb.append(", email=").append(email);
sb.append(", mobile=").append(mobile);
sb.append(", status=").append(status);
sb.append(", createTime=").append(createTime);
sb.append(", modifyTime=").append(modifyTime);
sb.append(", lastLoginTime=").append(lastLoginTime);
sb.append(", ssex=").append(ssex);
sb.append(", isTab=").append(isTab);
sb.append(", theme=").append(theme);
sb.append(", avatar=").append(avatar);
sb.append(", description=").append(description);
sb.append(", serialVersionUID=").append(serialVersionUID);
sb.append("]");
return sb.toString();
}
}

3.3.2 UserMapper

1
2
public interface UserMapper extends BaseMapper<User> {
}

3.3.3 UserMapper.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.wno704.boot.mapper.UserMapper">
<resultMap id="BaseResultMap" type="com.wno704.boot.model.User">
<!--
WARNING - @mbg.generated
-->
<id column="USER_ID" jdbcType="BIGINT" property="userId" />
<result column="USERNAME" jdbcType="VARCHAR" property="username" />
<result column="PASSWORD" jdbcType="VARCHAR" property="password" />
<result column="DEPT_ID" jdbcType="BIGINT" property="deptId" />
<result column="EMAIL" jdbcType="VARCHAR" property="email" />
<result column="MOBILE" jdbcType="VARCHAR" property="mobile" />
<result column="STATUS" jdbcType="CHAR" property="status" />
<result column="CREATE_TIME" jdbcType="TIMESTAMP" property="createTime" />
<result column="MODIFY_TIME" jdbcType="TIMESTAMP" property="modifyTime" />
<result column="LAST_LOGIN_TIME" jdbcType="TIMESTAMP" property="lastLoginTime" />
<result column="SSEX" jdbcType="CHAR" property="ssex" />
<result column="IS_TAB" jdbcType="CHAR" property="isTab" />
<result column="THEME" jdbcType="VARCHAR" property="theme" />
<result column="AVATAR" jdbcType="VARCHAR" property="avatar" />
<result column="DESCRIPTION" jdbcType="VARCHAR" property="description" />
</resultMap>
</mapper>

3.4 扫描mapper

3.4.1 需要启动类上增加mapper扫描

@MapperScan("com.wno704.boot.mapper")

注意:引用路径为:import tk.mybatis.spring.annotation.MapperScan;

3.4.2 application.yml配置mybatis

1
2
3
4
5
6
7
mybatis:
# type-aliases扫描路径
# type-aliases-package:
# mapper xml实现扫描路径
mapper-locations: classpath:mapper/*.xml
property:
order: BEFORE

四、测试

4.1 通用Service

BaseService.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public interface BaseService<T> {

List<T> selectAll();

int save(T entity);

int delete(Object key);

int updateAll(T entity);

int updateNotNull(T entity);

List<T> selectByExample(Object example);

}

4.2 BaseService实现类

BaseServiceImpl.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
public abstract class BaseServiceImpl<T> implements BaseService<T> {
@Autowired
protected Mapper<T> mapper;

public Mapper<T> getMapper() {
return mapper;
}

@Override
public List<T> selectAll() {
//说明:查询所有数据
return this.mapper.selectAll();
}

@Override
public int save(T entity) {
//说明:保存一个实体,null的属性也会保存,不会使用数据库默认值
return this.mapper.insert(entity);
}

@Override
public int delete(Object key) {
//说明:根据主键字段进行删除,方法参数必须包含完整的主键属性
return this.mapper.deleteByPrimaryKey(key);
}

@Override
public int updateAll(T entity) {
//说明:根据主键更新实体全部字段,null值会被更新
return this.mapper.updateByPrimaryKey(entity);
}

@Override
public int updateNotNull(T entity) {
//根据主键更新属性不为null的值
return this.mapper.updateByPrimaryKeySelective(entity);
}

@Override
public List<T> selectByExample(Object example) {
//说明:根据Example条件进行查询
//重点:这个查询支持通过Example类指定查询列,通过selectProperties方法指定查询列
return this.mapper.selectByExample(example);
}
}

4.3 定义UserService

1
2
public interface UserService extends BaseService<User>{
}

4.4 UserService实现类

UserServiceImpl.java

1
2
3
@Repository("userService")
public class UserServiceImpl extends BaseServiceImpl<User> implements UserService{
}

4.5 创建测试Controller

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
@RestController
public class TestController {

@Autowired
private PersonService personService;

@Autowired
private UserService userService;

@RequestMapping( value = "/queryperson", method = RequestMethod.GET)
public String queryStudentBySno() {
return this.personService.selectAll().toString();
}

@RequestMapping( value = "/queryuser", method = RequestMethod.GET)
public String queryUser() {
return this.userService.selectAll().toString();
}

@RequestMapping( value = "/queryuserpage", method = RequestMethod.GET)
public String queryUserPage() {

PageHelper.startPage(2, 2);
List<User> list = this.userService.selectAll();
PageInfo<User> pageInfo = new PageInfo<User>(list);
List<User> result = pageInfo.getList();
for (User u : result) {
System.out.println(u.getUsername());
}

return pageInfo.getList().toString();
}

}

访问路径 http://localhost:8080/queryuserpage