Spring Boot干货系列:(十一)数据存储篇-Spring Boot整合Mybatis通用Mapper插件

Spring Boot干货系列:(十一)数据存储篇-Spring Boot整合Mybatis通用Mapper插件_第1张图片

前言

上次介绍了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/常见问题


    mysql
    mysql-connector-java



    com.alibaba
    druid
    1.0.19

对应的application.properties配置:

## 数据库访问配置
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.driver-class-name = com.mysql.jdbc.Driver
spring.datasource.url = jdbc:mysql://localhost:3306/spring?useUnicode=true&characterEncoding=utf-8
spring.datasource.username = root
spring.datasource.password = root

# 下面为连接池的补充设置,应用到上面所有数据源中
# 初始化大小,最小,最大
spring.datasource.initialSize=5
spring.datasource.minIdle=5
spring.datasource.maxActive=20
# 配置获取连接等待超时的时间
spring.datasource.maxWait=60000
# 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
spring.datasource.timeBetweenEvictionRunsMillis=60000
# 配置一个连接在池中最小生存的时间,单位是毫秒
spring.datasource.minEvictableIdleTimeMillis=300000
spring.datasource.validationQuery=SELECT 1 FROM DUAL
spring.datasource.testWhileIdle=true
spring.datasource.testOnBorrow=false
spring.datasource.testOnReturn=false
# 打开PSCache,并且指定每个连接上PSCache的大小
spring.datasource.poolPreparedStatements=true
spring.datasource.maxPoolPreparedStatementPerConnectionSize=20
# 配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall'用于防火墙
spring.datasource.filters=stat,wall,log4j
# 合并多个DruidDataSource的监控数据
#spring.datasource.useGlobalDataSourceStat=true

对应的bean配置:

package com.dudu.config;

/**
 * Druid配置
 *
 * @author dudu
 * @date 2017-12-11 0:00
 */
@Configuration
public class DruidConfig {
    private Logger logger = LoggerFactory.getLogger(DruidConfig.class);

    @Value("${spring.datasource.url:#{null}}")
    private String dbUrl;
    @Value("${spring.datasource.username: #{null}}")
    private String username;
    @Value("${spring.datasource.password:#{null}}")
    private String password;
    @Value("${spring.datasource.driverClassName:#{null}}")
    private String driverClassName;
    @Value("${spring.datasource.initialSize:#{null}}")
    private Integer initialSize;
    @Value("${spring.datasource.minIdle:#{null}}")
    private Integer minIdle;
    @Value("${spring.datasource.maxActive:#{null}}")
    private Integer maxActive;
    @Value("${spring.datasource.maxWait:#{null}}")
    private Integer maxWait;
    @Value("${spring.datasource.timeBetweenEvictionRunsMillis:#{null}}")
    private Integer timeBetweenEvictionRunsMillis;
    @Value("${spring.datasource.minEvictableIdleTimeMillis:#{null}}")
    private Integer minEvictableIdleTimeMillis;
    @Value("${spring.datasource.validationQuery:#{null}}")
    private String validationQuery;
    @Value("${spring.datasource.testWhileIdle:#{null}}")
    private Boolean testWhileIdle;
    @Value("${spring.datasource.testOnBorrow:#{null}}")
    private Boolean testOnBorrow;
    @Value("${spring.datasource.testOnReturn:#{null}}")
    private Boolean testOnReturn;
    @Value("${spring.datasource.poolPreparedStatements:#{null}}")
    private Boolean poolPreparedStatements;
    @Value("${spring.datasource.maxPoolPreparedStatementPerConnectionSize:#{null}}")
    private Integer maxPoolPreparedStatementPerConnectionSize;
    @Value("${spring.datasource.filters:#{null}}")
    private String filters;
    @Value("{spring.datasource.connectionProperties:#{null}}")
    private String connectionProperties;

    @Bean
    @Primary
    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 filters = new ArrayList<>();
        filters.add(statFilter());
        filters.add(wallFilter());
        datasource.setProxyFilters(filters);

        return datasource;
    }

    @Bean
    public ServletRegistrationBean druidServlet() {
        ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean(new StatViewServlet(), "/druid/*");

        //控制台管理用户,加入下面2行 进入druid后台就需要登录
        //servletRegistrationBean.addInitParameter("loginUsername", "admin");
        //servletRegistrationBean.addInitParameter("loginPassword", "admin");
        return servletRegistrationBean;
    }

    @Bean
    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;
    }

    @Bean
    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;
    }

    @Bean
    public WallFilter wallFilter(){
        WallFilter wallFilter = new WallFilter();
        //允许执行多条SQL
        WallConfig config = new WallConfig();
        config.setMultiStatementAllow(true);
        wallFilter.setConfig(config);
        return wallFilter;
    }
}

mybatis相关依赖



    org.mybatis.spring.boot
    mybatis-spring-boot-starter
    1.3.1



    tk.mybatis
    mapper-spring-boot-starter
    1.1.5



    com.github.pagehelper
    pagehelper-spring-boot-starter
    1.2.3



    
        
            org.springframework.boot
            spring-boot-maven-plugin
        

        
            org.mybatis.generator
            mybatis-generator-maven-plugin
            1.3.5
            
                
                
                    mysql
                    mysql-connector-java
                    5.1.44
                
                
                    tk.mybatis
                    mapper
                    3.4.0
                
            
            
                
                    Generate MyBatis Artifacts
                    package
                    
                        generate
                    
                
            
            
                
                true
                
                true
                
                src/main/resources/mybatis-generator.xml
            
        
    


上面引入了mybatis相关的一些依赖以及generator的配置,这里generator配置文件指向
src/main/resources/mybatis-generator.xml文件,具体一会贴出。

对应的application.properties配置:

#指定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

package com.dudu.util;

import tk.mybatis.mapper.common.Mapper;
import tk.mybatis.mapper.common.MySqlMapper;

/**
 * 继承自己的MyMapper
 *
 * @author
 * @since 2017-06-26 21:53
 */
public interface MyMapper extends Mapper, MySqlMapper {
    //FIXME 特别注意,该接口不能被扫描到,否则会出错
}

这里实现一个自己的接口,继承通用的mapper,关键点就是这个接口不能被扫描到,不能跟dao这个存放mapper文件放在一起。

最后在启动类中通过MapperScan注解指定扫描的mapper路径:

package com.dudu;
@SpringBootApplication
//启注解事务管理
@EnableTransactionManagement  // 启注解事务管理,等同于xml配置方式的 
@MapperScan(basePackages = "com.dudu.dao", markerInterface = MyMapper.class)
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




    
    

    

        
            
            
          
        

        
        
            
            
            
        

        
        
        

        
            
        

        
        
            
            
        

        
        
            
        

        
        
       
            
       

        
        
        

其中,我们通过引入了配置文件,这样下面指定数据源的时候不用写死。

其中tk.mybatis.mapper.generator.MapperPlugin很重要,用来指定通用Mapper对应的文件,这样我们生成的mapper都会继承这个通用Mapper


    
  
  

这样就可以通过mybatis-generator插件生成对应的文件啦


image.png

如果不是IDEA开发环境也可以直接通过命令:mvn mybatis-generator:generate

自动生成的文件如下图所示


image.png

脚本初始化

CREATE DATABASE /*!32312 IF NOT EXISTS*/`spring` /*!40100 DEFAULT CHARACTER SET utf8 */;
USE `spring`;
DROP TABLE IF EXISTS `learn_resource`;

CREATE TABLE `learn_resource` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'ID',
  `author` varchar(20) DEFAULT NULL COMMENT '作者',
  `title` varchar(100) DEFAULT NULL COMMENT '描述',
  `url` varchar(100) DEFAULT NULL COMMENT '地址链接',
  PRIMARY KEY (`id`)
) ENGINE=MyISAM AUTO_INCREMENT=1029 DEFAULT CHARSET=utf8;

insert into `learn_resource`(`id`,`author`,`title`,`url`) values (999,'官方SpriongBoot例子','官方SpriongBoot例子','https://github.com/spring-projects/spring-boot/tree/master/spring-boot-samples');
insert into `learn_resource`(`id`,`author`,`title`,`url`) values (1000,'龙果学院','Spring Boot 教程系列学习','http://www.roncoo.com/article/detail/124661');
insert into `learn_resource`(`id`,`author`,`title`,`url`) values (1001,'嘟嘟MD独立博客','Spring Boot干货系列','http://tengj.top/');
insert into `learn_resource`(`id`,`author`,`title`,`url`) values (1002,'后端编程嘟','Spring Boot视频教程','http://www.toutiao.com/m1559096720023553/');

Controller层

到此为止,基本的配置结束了,我们开始实现业务的逻辑,Controller层代码如下

/** 教程页面
 * Created by tengj on 2017/12/19
 */
@Controller
@RequestMapping("/learn")
public class LearnController  extends AbstractController{
    @Autowired
    private LearnService learnService;
    private Logger logger = LoggerFactory.getLogger(this.getClass());

    @RequestMapping("")
    public String learn(Model model){
        model.addAttribute("ctx", getContextPath()+"/");
        return "learn-resource";
    }

    /**
     * 查询教程列表
     * @param page
     * @return
     */
    @RequestMapping(value = "/queryLeanList",method = RequestMethod.POST)
    @ResponseBody
    public AjaxObject queryLearnList(Page page){
        List learnList=learnService.queryLearnResouceList(page);
        PageInfo pageInfo =new PageInfo(learnList);
        return AjaxObject.ok().put("page", pageInfo);
    }
    /**
     * 新添教程
     * @param learn
     */
    @RequestMapping(value = "/add",method = RequestMethod.POST)
    @ResponseBody
    public AjaxObject addLearn(@RequestBody LearnResource learn){
        learnService.save(learn);
        return AjaxObject.ok();
    }

    /**
     * 修改教程
     * @param learn
     */
    @RequestMapping(value = "/update",method = RequestMethod.POST)
    @ResponseBody
    public AjaxObject updateLearn(@RequestBody LearnResource learn){
        learnService.updateNotNull(learn);
        return AjaxObject.ok();
    }

    /**
     * 删除教程
     * @param ids
     */
    @RequestMapping(value="/delete",method = RequestMethod.POST)
    @ResponseBody
    public AjaxObject deleteLearn(@RequestBody Long[] ids){
        learnService.deleteBatch(ids);
        return AjaxObject.ok();
    }
}

通用Service

正常情况下具体业务是每个模块的service里面定义许多方法,然后mapper中实现。

但是博主查看插件文档后发现一个通用Mapper在Spring4中的最佳用法。那就是通用的Service。
具体可以查看这里了解:https://gitee.com/free/Mapper2/blob/master/wiki/mapper/4.Spring4.md

定义通用service接口

/**
 * 通用接口
 */
@Service
public interface IService {

    T selectByKey(Object key);

    int save(T entity);

    int delete(Object key);

    int updateAll(T entity);

    int updateNotNull(T entity);

    List selectByExample(Object example);

    //TODO 其他...
}

具体实现通用接口类

/**
 * 通用Service
 * @param 
 */
public abstract class BaseService implements IService {

    @Autowired
    protected Mapper mapper;
    public Mapper getMapper() {
        return mapper;
    }

    @Override
    public T selectByKey(Object key) {
        //说明:根据主键字段进行查询,方法参数必须包含完整的主键属性,查询条件使用等号
        return mapper.selectByPrimaryKey(key);
    }

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

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

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

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

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

到此基本的增删改查通用service就写好了,具体业务的service就直接继承这个接口即可,也可以添加额外的方法,例如:

public interface LearnService  extends IService{
    public List queryLearnResouceList(Page page);
    public void deleteBatch(Long[] ids);
}

具体实现service


/**
 * Created by tengj on 2017/4/7.
 */
@Service
public class LearnServiceImpl extends BaseService  implements LearnService {

    @Autowired
    private LearnResourceMapper  learnResourceMapper;

    @Override
    public void deleteBatch(Long[] ids) {
        Arrays.stream(ids).forEach(id->learnResourceMapper.deleteByPrimaryKey(id));
    }

    @Override
    public List queryLearnResouceList(Page page) {
        PageHelper.startPage(page.getPage(), page.getRows());
        return learnResourceMapper.queryLearnResouceList(page.getCondition());
    }
}

可以看到,具体LearnServiceImpl这边就实现了2个方法,其他的都使用了通用service的,在开发上剩下了许多功夫。

Mapper相关

在自动生成的mapper文件中实现sevice自定义的方法:

public interface LearnResourceMapper extends MyMapper {
    List queryLearnResouceList(Map map);
}

LearnResourceMapper.xml:




  
    
    
    
    
    
  
    

IDEA可以安装这个插件,这样就可以直接从Mapper文件跳转到xml了


Spring Boot干货系列:(十一)数据存储篇-Spring Boot整合Mybatis通用Mapper插件_第2张图片
image.png

Spring Boot干货系列:(十一)数据存储篇-Spring Boot整合Mybatis通用Mapper插件_第3张图片
image.png

最终项目效果如下,增删改查分页一个都不少:


image.png

上面提到druid有对应的监控界面,启动项目后输入http://localhost:8090/spring/druid 即可登录,界面效果如下

Spring Boot干货系列:(十一)数据存储篇-Spring Boot整合Mybatis通用Mapper插件_第4张图片
image.png

总结

到此,一套适合企业级开发的Spring Boot应用模板就好了,Mybatis+通用Mapper、Mybatis Geneator确实可以省下很多开发成本,提高效率。前端整合了vue.js,具体看源码。

想要查看更多Spring Boot干货教程,可前往:Spring Boot干货系列总纲

源码下载

( ̄︶ ̄)↗[相关示例完整代码]

  • chapter11==》Spring Boot干货系列:(十一)数据存储篇-Spring Boot整合Mybatis通用Mapper插件

想要ace模板源码的话,在博主公众号回复关键字:ace

一直觉得自己写的不是技术,而是情怀,一篇篇文章是自己这一路走来的痕迹。靠专业技能的成功是最具可复制性的,希望我的这条路能让你少走弯路,希望我能帮你抹去知识的蒙尘,希望我能帮你理清知识的脉络,希望未来技术之巅上有你也有我。

你可能感兴趣的:(Spring Boot干货系列:(十一)数据存储篇-Spring Boot整合Mybatis通用Mapper插件)