Spring Boot干貨系列:(十一)數(shù)據(jù)存儲篇-Spring Boot整合Mybatis通用Mapper插件

前言

上次介紹了Spring Boot中Mybatis的簡單整合爬虱,本篇深入來結(jié)合通用Mapper碎浇、Mybatis Geneator以及分頁P(yáng)ageHelper來打造適合企業(yè)開發(fā)的模板框架。

正文

項(xiàng)目框架還是跟上一篇一樣使用Spring Boot的ace后端模板乒裆,不過最近在使用vue套利,所以前端引用了vue進(jìn)來改寫,代碼變得更加簡潔缸兔。

項(xiàng)目配置:

Spring Boot: 1.5.9.RELEASE
Maven: 3.5
Java: 1.8
Thymeleaf: 3.0.7.RELEASE
Vue.js: v2.5.11

數(shù)據(jù)源依賴

這里我們還是使用阿里巴巴的druid來當(dāng)數(shù)據(jù)庫連接池日裙,發(fā)現(xiàn)這個(gè)有對應(yīng)的監(jiān)控界面,我們可以開啟惰蜜。
druid官方文檔:https://github.com/alibaba/druid/wiki/常見問題

<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
</dependency>

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid</artifactId>
    <version>1.0.19</version>
</dependency>

對應(yīng)的application.properties配置:

## 數(shù)據(jù)庫訪問配置
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

# 下面為連接池的補(bǔ)充設(shè)置,應(yīng)用到上面所有數(shù)據(jù)源中
# 初始化大小受神,最小抛猖,最大
spring.datasource.initialSize=5
spring.datasource.minIdle=5
spring.datasource.maxActive=20
# 配置獲取連接等待超時(shí)的時(shí)間
spring.datasource.maxWait=60000
# 配置間隔多久才進(jìn)行一次檢測,檢測需要關(guān)閉的空閑連接鼻听,單位是毫秒
spring.datasource.timeBetweenEvictionRunsMillis=60000
# 配置一個(gè)連接在池中最小生存的時(shí)間财著,單位是毫秒
spring.datasource.minEvictableIdleTimeMillis=300000
spring.datasource.validationQuery=SELECT 1 FROM DUAL
spring.datasource.testWhileIdle=true
spring.datasource.testOnBorrow=false
spring.datasource.testOnReturn=false
# 打開PSCache,并且指定每個(gè)連接上PSCache的大小
spring.datasource.poolPreparedStatements=true
spring.datasource.maxPoolPreparedStatementPerConnectionSize=20
# 配置監(jiān)控統(tǒng)計(jì)攔截的filters撑碴,去掉后監(jiān)控界面sql無法統(tǒng)計(jì)撑教,'wall'用于防火墻
spring.datasource.filters=stat,wall,log4j
# 合并多個(gè)DruidDataSource的監(jiān)控?cái)?shù)據(jù)
#spring.datasource.useGlobalDataSourceStat=true

對應(yīng)的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<Filter> 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行 進(jìn)入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慢的標(biāo)準(zhǔn)醉拓,執(zhí)行時(shí)間超過slowSqlMillis的就是慢伟姐。
        statFilter.setMergeSql(true); //SQL合并配置
        statFilter.setSlowSqlMillis(1000);//slowSqlMillis的缺省值為3000,也就是3秒亿卤。
        return statFilter;
    }

    @Bean
    public WallFilter wallFilter(){
        WallFilter wallFilter = new WallFilter();
        //允許執(zhí)行多條SQL
        WallConfig config = new WallConfig();
        config.setMultiStatementAllow(true);
        wallFilter.setConfig(config);
        return wallFilter;
    }
}

mybatis相關(guān)依賴

<!--mybatis-->
<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>1.3.1</version>
</dependency>
<!--通用mapper-->
<dependency>
    <groupId>tk.mybatis</groupId>
    <artifactId>mapper-spring-boot-starter</artifactId>
    <version>1.1.5</version>
</dependency>
<!--pagehelper 分頁插件-->
<dependency>
    <groupId>com.github.pagehelper</groupId>
    <artifactId>pagehelper-spring-boot-starter</artifactId>
    <version>1.2.3</version>
</dependency>

<build>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
        </plugin>

        <plugin>
            <groupId>org.mybatis.generator</groupId>
            <artifactId>mybatis-generator-maven-plugin</artifactId>
            <version>1.3.5</version>
            <dependencies>
                <!--配置這個(gè)依賴主要是為了等下在配置mybatis-generator.xml的時(shí)候可以不用配置classPathEntry這樣的一個(gè)屬性愤兵,避免代碼的耦合度太高-->
                <dependency>
                    <groupId>mysql</groupId>
                    <artifactId>mysql-connector-java</artifactId>
                    <version>5.1.44</version>
                </dependency>
                <dependency>
                    <groupId>tk.mybatis</groupId>
                    <artifactId>mapper</artifactId>
                    <version>3.4.0</version>
                </dependency>
            </dependencies>
            <executions>
                <execution>
                    <id>Generate MyBatis Artifacts</id>
                    <phase>package</phase>
                    <goals>
                        <goal>generate</goal>
                    </goals>
                </execution>
            </executions>
            <configuration>
                <!--允許移動生成的文件 -->
                <verbose>true</verbose>
                <!-- 是否覆蓋 -->
                <overwrite>true</overwrite>
                <!-- 自動生成的配置 -->
                <configurationFile>src/main/resources/mybatis-generator.xml</configurationFile>
            </configuration>
        </plugin>
    </plugins>
</build>

上面引入了mybatis相關(guān)的一些依賴以及generator的配置,這里generator配置文件指向
src/main/resources/mybatis-generator.xml文件排吴,具體一會貼出秆乳。

對應(yīng)的application.properties配置:

#指定bean所在包
mybatis.type-aliases-package=com.dudu.domain
#指定映射文件
mybatis.mapperLocations=classpath:mapper/*.xml

#mapper
#mappers 多個(gè)接口時(shí)逗號隔開
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都可以極大的方便開發(fā)人員,對單表封裝了許多通用方法,省掉自己寫增刪改查的sql。
通用Mapper插件網(wǎng)址: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<T> extends Mapper<T>, MySqlMapper<T> {
    //FIXME 特別注意屹堰,該接口不能被掃描到肛冶,否則會出錯
}

這里實(shí)現(xiàn)一個(gè)自己的接口,繼承通用的mapper,關(guān)鍵點(diǎn)就是這個(gè)接口不能被掃描到扯键,不能跟dao這個(gè)存放mapper文件放在一起淑趾。

最后在啟動類中通過MapperScan注解指定掃描的mapper路徑:

package com.dudu;
@SpringBootApplication
//啟注解事務(wù)管理
@EnableTransactionManagement  // 啟注解事務(wù)管理,等同于xml配置方式的 <tx:annotation-driven />
@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文件,該配置文件用來自動生成表對應(yīng)的Model,Mapper以及xml,該文件位于src/main/resources下面
Mybatis Geneator 詳解: http://blog.csdn.net/isea533/article/details/42102297

<?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>
    <!--加載配置文件忧陪,為下面讀取數(shù)據(jù)庫信息準(zhǔn)備-->
    <properties resource="application.properties"/>

    <context id="Mysql" targetRuntime="MyBatis3Simple" defaultModelType="flat">

        <plugin type="tk.mybatis.mapper.generator.MapperPlugin">
            <property name="mappers" value="com.dudu.util.MyMapper" />
            <!--caseSensitive默認(rèn)false扣泊,當(dāng)數(shù)據(jù)庫表名區(qū)分大小寫時(shí),可以將該屬性設(shè)置為true-->
          <property name="caseSensitive" v
          alue="true"/>
        </plugin>

        <!-- 阻止生成自動注釋 -->
        <commentGenerator>
            <property name="javaFileEncoding" value="UTF-8"/>
            <property name="suppressDate" value="true"/>
            <property name="suppressAllComments" value="true"/>
        </commentGenerator>

        <!--數(shù)據(jù)庫鏈接地址賬號密碼-->
        <jdbcConnection driverClass="${spring.datasource.driver-class-name}"
                        connectionURL="${spring.datasource.url}"
                        userId="${spring.datasource.username}"
                        password="${spring.datasource.password}">
        </jdbcConnection>

        <javaTypeResolver>
            <property name="forceBigDecimals" value="false"/>
        </javaTypeResolver>

        <!--生成Model類存放位置-->
        <javaModelGenerator targetPackage="com.dudu.domain" targetProject="src/main/java">
            <property name="enableSubPackages" value="true"/>
            <property name="trimStrings" value="true"/>
        </javaModelGenerator>

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

        <!--生成Dao類存放位置-->
        <!-- 客戶端代碼嘶摊,生成易于使用的針對Model對象和XML配置文件 的代碼
                type="ANNOTATEDMAPPER",生成Java Model 和基于注解的Mapper對象
                type="XMLMAPPER",生成SQLMap XML文件和獨(dú)立的Mapper接口
        -->
       <javaClientGenerator type="XMLMAPPER" targetPackage="com.dudu.dao" targetProject="src/main/java">
            <property name="enableSubPackages" value="true"/>
       </javaClientGenerator>

        <!--生成對應(yīng)表及類名
        去掉Mybatis Generator生成的一堆 example
        -->
        <table tableName="LEARN_RESOURCE" domainObjectName="LearnResource" enableCountByExample="false" enableUpdateByExample="false" enableDeleteByExample="false" enableSelectByExample="false" selectByExampleQueryId="false">
            <generatedKey column="id" sqlStatement="Mysql" identity="true"/>
        </table>
    </context>
</generatorConfiguration>

其中延蟹,我們通過<properties resource="application.properties"/>引入了配置文件,這樣下面指定數(shù)據(jù)源的時(shí)候不用寫死叶堆。

其中tk.mybatis.mapper.generator.MapperPlugin很重要阱飘,用來指定通用Mapper對應(yīng)的文件,這樣我們生成的mapper都會繼承這個(gè)通用Mapper

<plugin type="tk.mybatis.mapper.generator.MapperPlugin">
    <property name="mappers" value="com.dudu.util.MyMapper" />
  <!--caseSensitive默認(rèn)false虱颗,當(dāng)數(shù)據(jù)庫表名區(qū)分大小寫時(shí)沥匈,可以將該屬性設(shè)置為true-->
  <property name="caseSensitive" value="true"/>
</plugin>

這樣就可以通過mybatis-generator插件生成對應(yīng)的文件啦


image.png

如果不是IDEA開發(fā)環(huán)境也可以直接通過命令: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,'龍果學(xué)院','Spring Boot 教程系列學(xué)習(xí)','http://www.roncoo.com/article/detail/124661');
insert into `learn_resource`(`id`,`author`,`title`,`url`) values (1001,'嘟嘟MD獨(dú)立博客','Spring Boot干貨系列','http://tengj.top/');
insert into `learn_resource`(`id`,`author`,`title`,`url`) values (1002,'后端編程嘟','Spring Boot視頻教程','http://www.toutiao.com/m1559096720023553/');

Controller層

到此為止,基本的配置結(jié)束了忘渔,我們開始實(shí)現(xiàn)業(yè)務(wù)的邏輯高帖,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<LeanQueryLeanListReq> page){
        List<LearnResource> learnList=learnService.queryLearnResouceList(page);
        PageInfo<LearnResource> pageInfo =new PageInfo<LearnResource>(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

正常情況下具體業(yè)務(wù)是每個(gè)模塊的service里面定義許多方法,然后mapper中實(shí)現(xiàn)畦粮。

但是博主查看插件文檔后發(fā)現(xiàn)一個(gè)通用Mapper在Spring4中的最佳用法散址。那就是通用的Service。
具體可以查看這里了解:https://gitee.com/free/Mapper2/blob/master/wiki/mapper/4.Spring4.md

定義通用service接口

/**
 * 通用接口
 */
@Service
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 其他...
}

具體實(shí)現(xiàn)通用接口類

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

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

    @Override
    public T selectByKey(Object key) {
        //說明:根據(jù)主鍵字段進(jìn)行查詢宣赔,方法參數(shù)必須包含完整的主鍵屬性预麸,查詢條件使用等號
        return mapper.selectByPrimaryKey(key);
    }

    @Override
    public int save(T entity) {
        //說明:保存一個(gè)實(shí)體,null的屬性也會保存儒将,不會使用數(shù)據(jù)庫默認(rèn)值
        return mapper.insert(entity);
    }

    @Override
    public int delete(Object key) {
        //說明:根據(jù)主鍵字段進(jìn)行刪除吏祸,方法參數(shù)必須包含完整的主鍵屬性
        return mapper.deleteByPrimaryKey(key);
    }

    @Override
    public int updateAll(T entity) {
        //說明:根據(jù)主鍵更新實(shí)體全部字段,null值會被更新
        return mapper.updateByPrimaryKey(entity);
    }

    @Override
    public int updateNotNull(T entity) {
        //根據(jù)主鍵更新屬性不為null的值
        return mapper.updateByPrimaryKeySelective(entity);
    }

    @Override
    public List<T> selectByExample(Object example) {
        //說明:根據(jù)Example條件進(jìn)行查詢
        //重點(diǎn):這個(gè)查詢支持通過Example類指定查詢列钩蚊,通過selectProperties方法指定查詢列
        return mapper.selectByExample(example);
    }
}

到此基本的增刪改查通用service就寫好了贡翘,具體業(yè)務(wù)的service就直接繼承這個(gè)接口即可,也可以添加額外的方法,例如:

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

具體實(shí)現(xiàn)service


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

    @Autowired
    private LearnResourceMapper  learnResourceMapper;

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

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

可以看到两疚,具體LearnServiceImpl這邊就實(shí)現(xiàn)了2個(gè)方法床估,其他的都使用了通用service的,在開發(fā)上剩下了許多功夫诱渤。

Mapper相關(guān)

在自動生成的mapper文件中實(shí)現(xiàn)sevice自定義的方法:

public interface LearnResourceMapper extends MyMapper<LearnResource> {
    List<LearnResource> queryLearnResouceList(Map<String,Object> map);
}

LearnResourceMapper.xml:

<?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.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可以安裝這個(gè)插件丐巫,這樣就可以直接從Mapper文件跳轉(zhuǎn)到xml了


image.png

image.png

最終項(xiàng)目效果如下,增刪改查分頁一個(gè)都不少:


image.png

上面提到druid有對應(yīng)的監(jiān)控界面,啟動項(xiàng)目后輸入http://localhost:8090/spring/druid 即可登錄,界面效果如下

image.png

總結(jié)

到此递胧,一套適合企業(yè)級開發(fā)的Spring Boot應(yīng)用模板就好了碑韵,Mybatis+通用Mapper、Mybatis Geneator確實(shí)可以省下很多開發(fā)成本缎脾,提高效率祝闻。前端整合了vue.js,具體看源碼。

想要查看更多Spring Boot干貨教程,可前往:Spring Boot干貨系列總綱

源碼下載

( ̄︶ ̄)↗[相關(guān)示例完整代碼]

  • chapter11==》Spring Boot干貨系列:(十一)數(shù)據(jù)存儲篇-Spring Boot整合Mybatis通用Mapper插件

想要ace模板源碼的話遗菠,在博主公眾號回復(fù)關(guān)鍵字:ace

一直覺得自己寫的不是技術(shù)联喘,而是情懷,一篇篇文章是自己這一路走來的痕跡辙纬』碓猓靠專業(yè)技能的成功是最具可復(fù)制性的,希望我的這條路能讓你少走彎路贺拣,希望我能幫你抹去知識的蒙塵蓖谢,希望我能幫你理清知識的脈絡(luò),希望未來技術(shù)之巔上有你也有我譬涡。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末闪幽,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子涡匀,更是在濱河造成了極大的恐慌盯腌,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,188評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件渊跋,死亡現(xiàn)場離奇詭異腊嗡,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)拾酝,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,464評論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來卡者,“玉大人蒿囤,你說我怎么就攤上這事〕缇觯” “怎么了材诽?”我有些...
    開封第一講書人閱讀 165,562評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長恒傻。 經(jīng)常有香客問我脸侥,道長,這世上最難降的妖魔是什么盈厘? 我笑而不...
    開封第一講書人閱讀 58,893評論 1 295
  • 正文 為了忘掉前任睁枕,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘外遇。我一直安慰自己注簿,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,917評論 6 392
  • 文/花漫 我一把揭開白布跳仿。 她就那樣靜靜地躺著诡渴,像睡著了一般。 火紅的嫁衣襯著肌膚如雪菲语。 梳的紋絲不亂的頭發(fā)上妄辩,一...
    開封第一講書人閱讀 51,708評論 1 305
  • 那天,我揣著相機(jī)與錄音山上,去河邊找鬼眼耀。 笑死,一個(gè)胖子當(dāng)著我的面吹牛胶哲,可吹牛的內(nèi)容都是我干的畔塔。 我是一名探鬼主播,決...
    沈念sama閱讀 40,430評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼鸯屿,長吁一口氣:“原來是場噩夢啊……” “哼澈吨!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起寄摆,我...
    開封第一講書人閱讀 39,342評論 0 276
  • 序言:老撾萬榮一對情侶失蹤谅辣,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后婶恼,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體桑阶,經(jīng)...
    沈念sama閱讀 45,801評論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,976評論 3 337
  • 正文 我和宋清朗相戀三年勾邦,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了蚣录。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,115評論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡眷篇,死狀恐怖萎河,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情蕉饼,我是刑警寧澤虐杯,帶...
    沈念sama閱讀 35,804評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站昧港,受9級特大地震影響擎椰,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜创肥,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,458評論 3 331
  • 文/蒙蒙 一达舒、第九天 我趴在偏房一處隱蔽的房頂上張望值朋。 院中可真熱鬧,春花似錦休弃、人聲如沸吞歼。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,008評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽篙骡。三九已至,卻和暖如春丈甸,著一層夾襖步出監(jiān)牢的瞬間糯俗,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,135評論 1 272
  • 我被黑心中介騙來泰國打工睦擂, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留得湘,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,365評論 3 373
  • 正文 我出身青樓顿仇,卻偏偏與公主長得像淘正,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子臼闻,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,055評論 2 355

推薦閱讀更多精彩內(nèi)容