上一篇:springboot2.2.X手册:构建多元化的API接口,我们这样子设计
源码请关注后私信
mybaits,现在很多公司都会用,替换掉hibernate,但是写SQL确实麻烦,比较痛苦。
mybatis plus是国内开源的很好的一个工具,号称为简化开发而生
1、只做增强不做改变,引入它不会对现有工程产生影响,如丝般顺滑。
2、只需简单配置,即可快速进行 CRUD 操作,从而节省大量时间。
3、热加载、代码生成、分页、性能分析等功能一应俱全。
当前最新版本
com.baomidou mybatis-plus 3.3.1.tmp
引入包
org.springframework.bootspring-boot-autoconfigureorg.springframework.bootspring-boot-starter-weborg.mybatismybatis3.5.4org.mybatis.spring.bootmybatis-spring-boot-starter2.1.2com.alibabadruid-spring-boot-starter1.1.21mysqlmysql-connector-javacom.baomidoumybatis-plus-boot-starter3.3.1.tmp
雪花算法自定义数据库的主键id
/** * 自定义ID生成器 * @author:溪云阁 * @date:2020年5月10日 */@Componentpublic class CustomIdGenerator implements IdentifierGenerator { /** * 获取自定义id * @author 溪云阁 * @param entity * @return 返回数据库的主键ID */ @Override public Number nextId(Object entity) { // 采用雪花算法获取id,时间回拨会存在重复,这里用随机数来减少重复的概率 final Snowflake snowflake = IdUtil.createSnowflake(1, (int) (Math.random() * 20 + 1)); return snowflake.nextId(); }}
数据源配置
package com.module.boots.mp;import java.sql.SQLException;import javax.sql.DataSource;import org.springframework.beans.factory.annotation.Value;import org.springframework.boot.web.servlet.FilterRegistrationBean;import org.springframework.boot.web.servlet.ServletComponentScan;import org.springframework.boot.web.servlet.ServletRegistrationBean;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.context.annotation.Primary;import com.alibaba.druid.pool.DruidDataSource;import com.alibaba.druid.support.http.StatViewServlet;import com.alibaba.druid.support.http.WebStatFilter;import lombok.extern.slf4j.Slf4j;/** * 数据源配置 * @author:溪云阁 * @date:2020年5月10日 */@ServletComponentScan@Configuration@Slf4jpublic class DataSourceConfig { @Value("${boots.datasource.druid.url}") private String url; @Value("${boots.datasource.druid.username}") private String username; @Value("${boots.datasource.druid.password}") private String password; @Value("${boots.datasource.druid.initial-size:1}") private int initialSize; @Value("${boots.datasource.druid.max-active:30}") private int maxActive; @Value("${boots.datasource.druid.min-idle:3}") private int minIdle; @Value("${boots.datasource.druid.max-wait:60000}") private int maxWait; @Value("${boots.datasource.druid.time-between-eviction-runs-millis:60000}") private int timeBetweenEvictionRunsMillis; @Value("${boots.datasource.druid.min-evictable-idle-time-millis:300000}") private int minEvictableIdleTimeMillis; @Value("${boots.datasource.druid.validation-query:select 'x'}") private String validationQuery; @Value("${boots.datasource.druid.test-while-idle:true}") private boolean testWhileIdle; @Value("${boots.datasource.druid.test-on-borrow:false}") private boolean testOnBorrow; @Value("${boots.datasource.druid.test-on-return:false}") private boolean testOnReturn; @Value("${boots.datasource.druid.pool-prepared-statements:true}") private boolean poolPreparedStatements; @Value("${boots.datasource.druid.max-pool-prepared-statement-per-connection-size:20}") private int maxPoolPreparedStatementPerConnectionSize; @Value("${boots.datasource.druid.filter-class-names:stat,slf4j}") private String filters; @Value("${boots.datasource.druid.connection-properties:druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000}") private String connectionProperties = ""; /** * 数据源配置 * @author 溪云阁 * @return DataSource */ @Bean(initMethod = "init", destroyMethod = "close") @Primary public DataSource dataSource() { DruidDataSource datasource = null; try { datasource = new DruidDataSource(); datasource.setDriverClassName("com.p6spy.engine.spy.P6SpyDriver"); datasource.setUrl(url); datasource.setUsername(username); datasource.setPassword(password); // 初始化时建立物理连接的个数。初始化发生在显示调用init方法,或者第一次getConnection时 datasource.setInitialSize(initialSize); // 最小连接池数量 datasource.setMinIdle(minIdle); // 最大连接池数量 datasource.setMaxActive(maxActive); // 配置获取连接等待超时的时间 datasource.setMaxWait(maxWait); // 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 datasource.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis); // 配置一个连接在池中最小生存的时间,单位是毫秒 datasource.setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis); // 用来检测连接是否有效的sql,要求是一个查询语句,常用select 'x'. // 如果validationQuery为null,testOnBorrow、testOnReturn、testWhileIdle都不会起作用。 datasource.setValidationQuery(validationQuery); // 建议配置为true,不影响性能,并且保证安全性。申请连接的时候检测,如果空闲时间大于timeBetweenEvictionRunsMillis,执行validationQuery检测连接是否有效。 datasource.setTestWhileIdle(testWhileIdle); // 申请连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能 datasource.setTestOnBorrow(testOnBorrow); // 归还连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能。 datasource.setTestOnReturn(testOnReturn); // 打开PSCache,并且指定每个连接上PSCache的大小 datasource.setPoolPreparedStatements(poolPreparedStatements); datasource.setMaxPoolPreparedStatementPerConnectionSize(maxPoolPreparedStatementPerConnectionSize); // 配置监控统计拦截的filters,去掉后监控界面sql无法统计:监控统计用的filter:stat;日志用的filter:log4j;防御sql注入的filter:wall'wall'用于防火墙 datasource.setFilters(filters); // 通过connectProperties属性来打开mergeSql功能;慢SQL记录 datasource.setConnectionProperties(connectionProperties); } catch (final SQLException e) { log.error("数据初始化中出现问题", e); } return datasource; } /** * druid拦截及账号配置 * @author 溪云阁 * @return ServletRegistrationBean */ @Bean public ServletRegistrationBean druidServlet() { final ServletRegistrationBean reg = new ServletRegistrationBean<>(); reg.setServlet(new StatViewServlet()); // 添加映射路径 reg.addUrlMappings("/druid/*"); // 白名单 reg.addInitParameter("allow", ""); // 添加控制台管理用户 reg.addInitParameter("loginUsername", "admin"); reg.addInitParameter("loginPassword", "admin@123"); reg.addInitParameter("resetEnable", "true"); reg.addInitParameter("logSlowSql", "true"); return reg; } /** * 过滤资源配置 * @author 溪云阁 * @return FilterRegistrationBean */ @Bean public FilterRegistrationBean filterRegistrationBean() { final FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean<>(); filterRegistrationBean.setFilter(new WebStatFilter()); filterRegistrationBean.addUrlPatterns("/*"); filterRegistrationBean.addInitParameter("exclusions", "*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*"); filterRegistrationBean.addInitParameter("profileEnable", "true"); filterRegistrationBean.addInitParameter("principalCookieName", "USER_COOKIE"); filterRegistrationBean.addInitParameter("principalSessionName", "USER_SESSION"); filterRegistrationBean.addInitParameter("DruidWebStatFilter", "/*"); return filterRegistrationBean; }}
配置mybatisPlus
/** * MybatisPlus配置 * @author:溪云阁 * @date:2020年5月10日 */@MapperScan({ "com.boots.**.business.**.dao" })@EnableTransactionManagement@Configurationpublic class MyBatisPlusConfig { @Autowired private DataSource dataSource; @Autowired private CustomIdGenerator customIdGenerator; // 数据库查询后获取的对象存放目录,包含单表查询对象及多表查询对象 private final static String typeAliasPackage = "com.boots.**.business.**.model"; // 数据库查询的xml文件配置 private final static String mapperPath = "classpath:com/boots/**/business/**/dao/*Mapper.xml"; /** * 添加分页插件支持 * @author 溪云阁 * @return PaginationInterceptor */ @Bean public PaginationInterceptor paginationInterceptor() { final PaginationInterceptor paginationInterceptor = new PaginationInterceptor(); return paginationInterceptor; } /** * mybatisplus数据层配置 * @author 溪云阁 * @return DbConfig */ @Bean public DbConfig dbConfig() { final DbConfig dbConfig = new DbConfig(); // 设置ID的生成规则 dbConfig.setIdType(IdType.ASSIGN_ID); // 设置表名是否使用下划线命名 dbConfig.setTableUnderline(true); // 字段插入非空判断 dbConfig.setInsertStrategy(FieldStrategy.NOT_EMPTY); // 字段更新非空判断 dbConfig.setUpdateStrategy(FieldStrategy.NOT_EMPTY); // 字段查询非空判断 dbConfig.setSelectStrategy(FieldStrategy.NOT_EMPTY); return dbConfig; } /** * mybatisplus全局配置 * @author 溪云阁 * @param dbConfig * @return GlobalConfig */ @Bean public GlobalConfig globalConfig(DbConfig dbConfig) { final GlobalConfig globalConfig = new GlobalConfig(); // 设置mybatisplus数据层配置 globalConfig.setDbConfig(dbConfig); // 初始化SqlRunner globalConfig.setEnableSqlRunner(true); // 设置自定义主键ID的生成方式 globalConfig.setIdentifierGenerator(customIdGenerator); return globalConfig; } /** * 配置mybatis * @author 溪云阁 * @return MybatisConfiguration */ @Bean public MybatisConfiguration mybatisConfiguration() { final MybatisConfiguration mybatisConfiguration = new MybatisConfiguration(); // 设置为XML语言驱动 mybatisConfiguration.setDefaultScriptingLanguage(MybatisXMLLanguageDriver.class); // 设置数据库字段值映射方式,例如:updatedId在进行DB操作的时候会被自动解析为updated_id mybatisConfiguration.setMapUnderscoreToCamelCase(true); // 开启Mybatis的二级缓存 mybatisConfiguration.setCacheEnabled(true); // 当查询的返回一行都是null的结果时,MyBatis会帮忙填充一个所有属性都是null的对象 mybatisConfiguration.setCallSettersOnNulls(true); return mybatisConfiguration; } /** * 配置mybatis连接的session工厂 * @author 溪云阁 * @param globalConfig * @param mybatisConfiguration * @param paginationInterceptor * @return * @throws Exception MybatisSqlSessionFactoryBean */ @Bean public MybatisSqlSessionFactoryBean sqlSessionFactory(GlobalConfig globalConfig, MybatisConfiguration mybatisConfiguration, PaginationInterceptor paginationInterceptor) throws Exception { final MybatisSqlSessionFactoryBean sqlSessionFactoryBean = new MybatisSqlSessionFactoryBean(); // 设置数据源 sqlSessionFactoryBean.setDataSource(dataSource); // 设置XML的映射路径 sqlSessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources(mapperPath)); // 设置实体类扫描路径 sqlSessionFactoryBean.setTypeAliasesPackage(typeAliasPackage); // 设置mybatisplus全局配置 sqlSessionFactoryBean.setGlobalConfig(globalConfig); // 设置mybatis的配置 sqlSessionFactoryBean.setConfiguration(mybatisConfiguration); // 设置插件 final List interceptors = new ArrayList<>(); // 设置数据库为mysql,如果是oracle,记得更改 paginationInterceptor.setDbType(DbType.MYSQL); // 设置分页插件 interceptors.add(paginationInterceptor); // 加载插件 sqlSessionFactoryBean.setPlugins(interceptors.get(0)); return sqlSessionFactoryBean; }}
自定义简单分页对象
在上一篇的中springboot2.2.X手册:构建多元化的API接口,我们这样子设计,我们新增一个简单的分页对象
/** * 分页对象 * @author:溪云阁 * @date:2020年5月10日 */@Data@ApiModel(value = "分页对象", description = "分页对象")public class PageMsg { @ApiModelProperty(value = "总数量") private int total = 0; @ApiModelProperty(value = "当前页的行数") private List rows = new ArrayList<>(); public PageMsg() { } public PageMsg(List rows, int total) { this.rows = rows; this.total = total; }}
查询的各个例子
/** * 数据字典接口 * @author:溪云阁 * @date:2020年5月10日 */@SuppressWarnings("deprecation")@Api(tags = { "后台系统服务:数据字典接口" })@RestController@RequestMapping("view/dict")public class DictView { @Autowired private IDictService dictService; /** * 获取数据字典数据 * @author 溪云阁 * @param id * @return ResponseMsg */ @ApiOperation(value = "获取数据字典数据") @GetMapping(value = "/getDict", produces = MediaType.APPLICATION_JSON_UTF8_VALUE) @SneakyThrows(CommonRuntimeException.class) public ResponseMsg getDict(@RequestParam("id") Long id) { final Dict dict = dictService.getById(id); final GetDictVO vo = GetDictVO.builder() .id(dict.getId()) .name(dict.getName()) .value(dict.getValue()) .description(dict.getDescription()) .build(); return MsgUtils.buildSuccessMsg(vo); } /** * 联合查询列表数据 * @author 溪云阁 * @param name * @return ResponseMsg */ @ApiOperation(value = "联合查询列表数据") @GetMapping(value = "/getDictList", produces = MediaType.APPLICATION_JSON_UTF8_VALUE) @SneakyThrows(CommonRuntimeException.class) public ResponseMsg> getDictList(@RequestParam("name") String name) { final List bos = dictService.getDictList(name); final List vos = new ArrayList<>(); bos.forEach(bo -> { final GetDictListVO vo = GetDictListVO.builder() .id(bo.getId()) .name(bo.getName()) .typeName(bo.getTypeName()) .value(bo.getValue()) .description(bo.getDescription()) .build(); vos.add(vo); }); return MsgUtils.buildSuccessMsg(vos); }}
/** * 数据字典服务实现层 * @author:溪云阁 * @date:2020年5月10日 */@Servicepublic class DictServiceImpl extends ServiceImpl implements IDictService{ /** * 联合查询列表数据 * @author 溪云阁 * @param name * @return List */ @Override public List getDictList(String name) { return baseMapper.getDictList(name); }}
/** * 数据字典数据访问层 * @author:溪云阁 * @date:2020年5月10日 */@Mapperpublic interface IDictDao extends BaseMapper { /** * 联合查询列表数据 * @author 溪云阁 * @param name * @return List */ List getDictList(@Param("name") String name);}
selectd.id as id,dt.type_name as typeName,d.name as name,d.value as value,d.description as descriptionfromdict d,dict_type dtwhere d.type_id = dt.type_idand d.name like concat('%',#{name},'%')
加入p6spy做SQL分析打印
加入p6spy包
p6spyp6spy3.9.0
加入p6spy的配置文件
#3.2.1以上使用modulelist=com.baomidou.mybatisplus.extension.p6spy.MybatisPlusLogFactory,com.p6spy.engine.outage.P6OutageFactory#3.2.1以下使用或者不配置#modulelist=com.p6spy.engine.logging.P6LogFactory,com.p6spy.engine.outage.P6OutageFactory# 自定义日志打印logMessageFormat=com.baomidou.mybatisplus.extension.p6spy.P6SpyLogger#日志输出到控制台appender=com.baomidou.mybatisplus.extension.p6spy.StdoutLogger# 使用日志系统记录 sql#appender=com.p6spy.engine.spy.appender.Slf4JLogger# 设置 p6spy driver 代理deregisterdrivers=true# 取消JDBC URL前缀useprefix=true# 配置记录 Log 例外,可去掉的结果集有error,info,batch,debug,statement,commit,rollback,result,resultset.excludecategories=info,debug,result,commit,resultset# 日期格式dateformat=yyyy-MM-dd HH:mm:ss# 实际驱动可多个#driverlist=org.h2.Driver# 是否开启慢SQL记录outagedetection=true# 慢SQL记录标准 2 秒outagedetectioninterval=2
测试接口
运行服务后,我们访问接口localhost:8080/doc.html
源码请关注后私信
--END--
作者:@溪云阁
如需要源码,转发,关注后私信我。
部分图片或代码来源网络,如侵权请联系删除,谢谢!