前言
上次介绍了Spring Boot中Mybatis的简单整合,本篇深入来结合通用Mapper、Mybatis Geneator以及分页PageHelper来打造适合企业开发的模板框架。
正文
项目框架还是跟上一篇一样使用Spring Boot的ace后端模板,不过最近在使用vue,所以前端引用了vue进来改写,代码变得更加简洁。
项目配置:
Spring Boot: 1.5.9.RELEASE
Maven: 3.5
Java: 1.8
Thymeleaf: 3.0.7.RELEASE
Vue.js: v2.5.11
数据源依赖
这里我们还是使用阿里巴巴的druid来当数据库连接池,发现这个有对应的监控界面,我们可以开启。
druid官方文档:https://github.com/alibaba/druid/wiki/常见问题
1 | <dependency> |
对应的application.properties配置:
1 | ## 数据库访问配置 |
对应的bean配置: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
147package com.dudu.config;
/**
* Druid配置
*
* @author dudu
* @date 2017-12-11 0:00
*/
public class DruidConfig {
private Logger logger = LoggerFactory.getLogger(DruidConfig.class);
"${spring.datasource.url:#{null}}") (
private String dbUrl;
"${spring.datasource.username: #{null}}") (
private String username;
"${spring.datasource.password:#{null}}") (
private String password;
"${spring.datasource.driverClassName:#{null}}") (
private String driverClassName;
"${spring.datasource.initialSize:#{null}}") (
private Integer initialSize;
"${spring.datasource.minIdle:#{null}}") (
private Integer minIdle;
"${spring.datasource.maxActive:#{null}}") (
private Integer maxActive;
"${spring.datasource.maxWait:#{null}}") (
private Integer maxWait;
"${spring.datasource.timeBetweenEvictionRunsMillis:#{null}}") (
private Integer timeBetweenEvictionRunsMillis;
"${spring.datasource.minEvictableIdleTimeMillis:#{null}}") (
private Integer minEvictableIdleTimeMillis;
"${spring.datasource.validationQuery:#{null}}") (
private String validationQuery;
"${spring.datasource.testWhileIdle:#{null}}") (
private Boolean testWhileIdle;
"${spring.datasource.testOnBorrow:#{null}}") (
private Boolean testOnBorrow;
"${spring.datasource.testOnReturn:#{null}}") (
private Boolean testOnReturn;
"${spring.datasource.poolPreparedStatements:#{null}}") (
private Boolean poolPreparedStatements;
"${spring.datasource.maxPoolPreparedStatementPerConnectionSize:#{null}}") (
private Integer maxPoolPreparedStatementPerConnectionSize;
"${spring.datasource.filters:#{null}}") (
private String filters;
"{spring.datasource.connectionProperties:#{null}}") (
private String connectionProperties;
public DataSource dataSource(){
DruidDataSource datasource = new DruidDataSource();
datasource.setUrl(this.dbUrl);
datasource.setUsername(username);
datasource.setPassword(password);
datasource.setDriverClassName(driverClassName);
//configuration
if(initialSize != null) {
datasource.setInitialSize(initialSize);
}
if(minIdle != null) {
datasource.setMinIdle(minIdle);
}
if(maxActive != null) {
datasource.setMaxActive(maxActive);
}
if(maxWait != null) {
datasource.setMaxWait(maxWait);
}
if(timeBetweenEvictionRunsMillis != null) {
datasource.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis);
}
if(minEvictableIdleTimeMillis != null) {
datasource.setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis);
}
if(validationQuery!=null) {
datasource.setValidationQuery(validationQuery);
}
if(testWhileIdle != null) {
datasource.setTestWhileIdle(testWhileIdle);
}
if(testOnBorrow != null) {
datasource.setTestOnBorrow(testOnBorrow);
}
if(testOnReturn != null) {
datasource.setTestOnReturn(testOnReturn);
}
if(poolPreparedStatements != null) {
datasource.setPoolPreparedStatements(poolPreparedStatements);
}
if(maxPoolPreparedStatementPerConnectionSize != null) {
datasource.setMaxPoolPreparedStatementPerConnectionSize(maxPoolPreparedStatementPerConnectionSize);
}
if(connectionProperties != null) {
datasource.setConnectionProperties(connectionProperties);
}
List<Filter> filters = new ArrayList<>();
filters.add(statFilter());
filters.add(wallFilter());
datasource.setProxyFilters(filters);
return datasource;
}
public ServletRegistrationBean druidServlet() {
ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean(new StatViewServlet(), "/druid/*");
//控制台管理用户,加入下面2行 进入druid后台就需要登录
//servletRegistrationBean.addInitParameter("loginUsername", "admin");
//servletRegistrationBean.addInitParameter("loginPassword", "admin");
return servletRegistrationBean;
}
public FilterRegistrationBean filterRegistrationBean() {
FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
filterRegistrationBean.setFilter(new WebStatFilter());
filterRegistrationBean.addUrlPatterns("/*");
filterRegistrationBean.addInitParameter("exclusions", "*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*");
filterRegistrationBean.addInitParameter("profileEnable", "true");
return filterRegistrationBean;
}
public StatFilter statFilter(){
StatFilter statFilter = new StatFilter();
statFilter.setLogSlowSql(true); //slowSqlMillis用来配置SQL慢的标准,执行时间超过slowSqlMillis的就是慢。
statFilter.setMergeSql(true); //SQL合并配置
statFilter.setSlowSqlMillis(1000);//slowSqlMillis的缺省值为3000,也就是3秒。
return statFilter;
}
public WallFilter wallFilter(){
WallFilter wallFilter = new WallFilter();
//允许执行多条SQL
WallConfig config = new WallConfig();
config.setMultiStatementAllow(true);
wallFilter.setConfig(config);
return wallFilter;
}
}
mybatis相关依赖
1 | <!--mybatis--> |
上面引入了mybatis相关的一些依赖以及generator的配置,这里generator配置文件指向
src/main/resources/mybatis-generator.xml文件,具体一会贴出。
对应的application.properties配置:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16#指定bean所在包
mybatis.type-aliases-package=com.dudu.domain
#指定映射文件
mybatis.mapperLocations=classpath:mapper/*.xml
#mapper
#mappers 多个接口时逗号隔开
mapper.mappers=com.dudu.util.MyMapper
mapper.not-empty=false
mapper.identity=MYSQL
#pagehelper
pagehelper.helperDialect=mysql
pagehelper.reasonable=true
pagehelper.supportMethodsArguments=true
pagehelper.params=count=countSql
通用Mapper配置
通用Mapper都可以极大的方便开发人员,对单表封装了许多通用方法,省掉自己写增删改查的sql。
通用Mapper插件网址:https://github.com/abel533/Mapper
1 | package com.dudu.util; |
这里实现一个自己的接口,继承通用的mapper,关键点就是这个接口不能被扫描到,不能跟dao这个存放mapper文件放在一起。
最后在启动类中通过MapperScan注解指定扫描的mapper路径:1
2
3
4
5
6
7
8
9
10package com.dudu;
//启注解事务管理
// 启注解事务管理,等同于xml配置方式的 <tx:annotation-driven />
"com.dudu.dao", markerInterface = MyMapper.class) (basePackages =
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
MyBatis Generator配置
这里配置一下上面提到的mybatis-generator.xml文件,该配置文件用来自动生成表对应的Model,Mapper以及xml,该文件位于src/main/resources
下面
Mybatis Geneator 详解: http://blog.csdn.net/isea533/article/details/42102297
1 | <?xml version="1.0" encoding="UTF-8"?> |
其中,我们通过<properties resource="application.properties"/>
引入了配置文件,这样下面指定数据源的时候不用写死。
其中tk.mybatis.mapper.generator.MapperPlugin很重要,用来指定通用Mapper对应的文件,这样我们生成的mapper都会继承这个通用Mapper1
2
3
4
5<plugin type="tk.mybatis.mapper.generator.MapperPlugin">
<property name="mappers" value="com.dudu.util.MyMapper" />
<!--caseSensitive默认false,当数据库表名区分大小写时,可以将该属性设置为true-->
<property name="caseSensitive" value="true"/>
</plugin>
这样就可以通过mybatis-generator插件生成对应的文件啦
如果不是IDEA开发环境也可以直接通过命令:mvn mybatis-generator:generate
自动生成的文件如下图所示
脚本初始化
1 | CREATE DATABASE /*!32312 IF NOT EXISTS*/`spring` /*!40100 DEFAULT CHARACTER SET utf8 */; |
Controller层
到此为止,基本的配置结束了,我们开始实现业务的逻辑,Controller层代码如下
1 | /** 教程页面 |
通用Service
正常情况下具体业务是每个模块的service里面定义许多方法,然后mapper中实现。
但是博主查看插件文档后发现一个通用Mapper在Spring4中的最佳用法。那就是通用的Service。
具体可以查看这里了解:https://gitee.com/free/Mapper2/blob/master/wiki/mapper/4.Spring4.md
定义通用service接口1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20/**
* 通用接口
*/
public interface IService<T> {
T selectByKey(Object key);
int save(T entity);
int delete(Object key);
int updateAll(T entity);
int updateNotNull(T entity);
List<T> selectByExample(Object example);
//TODO 其他...
}
具体实现通用接口类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/**
* 通用Service
* @param <T>
*/
public abstract class BaseService<T> implements IService<T> {
protected Mapper<T> mapper;
public Mapper<T> getMapper() {
return mapper;
}
public T selectByKey(Object key) {
//说明:根据主键字段进行查询,方法参数必须包含完整的主键属性,查询条件使用等号
return mapper.selectByPrimaryKey(key);
}
public int save(T entity) {
//说明:保存一个实体,null的属性也会保存,不会使用数据库默认值
return mapper.insert(entity);
}
public int delete(Object key) {
//说明:根据主键字段进行删除,方法参数必须包含完整的主键属性
return mapper.deleteByPrimaryKey(key);
}
public int updateAll(T entity) {
//说明:根据主键更新实体全部字段,null值会被更新
return mapper.updateByPrimaryKey(entity);
}
public int updateNotNull(T entity) {
//根据主键更新属性不为null的值
return mapper.updateByPrimaryKeySelective(entity);
}
public List<T> selectByExample(Object example) {
//说明:根据Example条件进行查询
//重点:这个查询支持通过Example类指定查询列,通过selectProperties方法指定查询列
return mapper.selectByExample(example);
}
}
到此基本的增删改查通用service就写好了,具体业务的service就直接继承这个接口即可,也可以添加额外的方法,例如:1
2
3
4public interface LearnService extends IService<LearnResource>{
public List<LearnResource> queryLearnResouceList(Page<LeanQueryLeanListReq> page);
public void deleteBatch(Long[] ids);
}
具体实现service1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 /**
* Created by tengj on 2017/4/7.
*/
public class LearnServiceImpl extends BaseService<LearnResource> implements LearnService {
private LearnResourceMapper learnResourceMapper;
public void deleteBatch(Long[] ids) {
Arrays.stream(ids).forEach(id->learnResourceMapper.deleteByPrimaryKey(id));
}
public List<LearnResource> queryLearnResouceList(Page<LeanQueryLeanListReq> page) {
PageHelper.startPage(page.getPage(), page.getRows());
return learnResourceMapper.queryLearnResouceList(page.getCondition());
}
}
可以看到,具体LearnServiceImpl这边就实现了2个方法,其他的都使用了通用service的,在开发上剩下了许多功夫。
Mapper相关
在自动生成的mapper文件中实现sevice自定义的方法:1
2
3public interface LearnResourceMapper extends MyMapper<LearnResource> {
List<LearnResource> queryLearnResouceList(Map<String,Object> map);
}
LearnResourceMapper.xml:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23 <?xml version="1.0" encoding="UTF-8"?>
<mapper namespace="com.dudu.dao.LearnResourceMapper">
<resultMap id="BaseResultMap" type="com.dudu.domain.LearnResource">
<!--
WARNING - @mbg.generated
-->
<id column="id" jdbcType="BIGINT" property="id" />
<result column="author" jdbcType="VARCHAR" property="author" />
<result column="title" jdbcType="VARCHAR" property="title" />
<result column="url" jdbcType="VARCHAR" property="url" />
</resultMap>
<select id="queryLearnResouceList" resultType="com.dudu.domain.LearnResource">
SELECT * from learn_resource where 1=1
<if test="author != null and author!= ''">
and author like CONCAT('%',#{author},'%')
</if>
<if test="title != null and title!= ''">
and title like CONCAT('%',#{title},'%')
</if>
order by id desc
</select>
</mapper>
IDEA可以安装这个插件,这样就可以直接从Mapper文件跳转到xml了
最终项目效果如下,增删改查分页一个都不少:
上面提到druid有对应的监控界面,启动项目后输入http://localhost:8090/spring/druid 即可登录,界面效果如下
总结
到此,一套适合企业级开发的Spring Boot应用模板就好了,Mybatis+通用Mapper、Mybatis Geneator确实可以省下很多开发成本,提高效率。前端整合了vue.js,具体看源码。
想要查看更多Spring Boot干货教程,可前往:Spring Boot干货系列总纲
源码下载
( ̄︶ ̄)↗[相关示例完整代码]
- chapter11==》Spring Boot干货系列:(十一)数据存储篇-Spring Boot整合Mybatis通用Mapper插件
想要ace模板源码的话,在博主公众号回复关键字:ace
一直觉得自己写的不是技术,而是情怀,一篇篇文章是自己这一路走来的痕迹。靠专业技能的成功是最具可复制性的,希望我的这条路能让你少走弯路,希望我能帮你抹去知识的蒙尘,希望我能帮你理清知识的脉络,希望未来技术之巅上有你也有我。