Spring boot學習(五)Spring boot整合Mybatis Generator以及PageHelper

諾丨言發表於2019-01-20

前言

在之前的部落格中介紹了Spring boot整合Mybatis以及Druid,本篇部落格主要介紹如何在Spring boot中整合Mybatis的逆向工程Mybatis Generator以下簡稱為MBG,以及實際專案中十分方便簡單的物理分頁外掛PageHelper,使用這些外掛能極大的減輕我們的開發過程那些簡單重複的過程,本篇部落格在專案已經整合了Mybatis的基礎上進行,沒有整合Mybatis的朋友可以參考 Spring boot整合Mybatis

文章首發於個人部落格:【www.xiongfrblog.cn

整合MBG

為什麼使用MBG

使用過Mybatis的朋友應該都知道,在使用之前我們需要根據資料庫表的情況在專案中建立出對應的實體類,sql語句介面層,XML檔案等,這一系列的操作是非常簡單的,但又是必不可少的,耗時耗力還沒什麼營養,如果資料庫的表足夠多的時候還要手動建立這些內容,那對程式設計師來說簡直是一種折磨,所以在實際的開發過程中MBG是非常有必要的,下面我們就詳細介紹如何在Spring boot專案中配置MBG

在pom.xml中新增MBG外掛

<plugins></plugins>標籤中新增如下內容:

<!-- Mybatis Generator外掛 -->
<plugin>
	<groupId>org.mybatis.generator</groupId>
    <artifactId>mybatis-generator-maven-plugin</artifactId>
    <version>1.3.2</version>
    <configuration>
    	<!-- 配置檔案的位置 -->
    	<configurationFile>src/main/resources/generator/generatorConfig.xml</configurationFile>
    	<!-- 允許移動生成的檔案 -->
    	<verbose>true</verbose>
        <!-- 是否覆蓋,true表示會替換生成的Java檔案,false則不覆蓋 -->
        <overwrite>true</overwrite>
    </configuration>
    <executions>
       <execution>
           <id>Generate MyBatis Artifacts</id>
           <goals>
               <goal>generate</goal>
           </goals>
       </execution>
   </executions>
   <dependencies>
       <dependency>
	      <groupId>org.mybatis.generator</groupId>
	      <artifactId>mybatis-generator-core</artifactId>
	      <version>1.3.2</version>
	    </dependency>
	    <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.6</version>
        </dependency>
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.4.0</version>
        </dependency>
   </dependencies>
</plugin>

複製程式碼

其中configurationFileMBG配置檔案的位置可以根據自己的實際情況改寫。

注意:如上所示,我們在外掛內還新增了mysqlmybatis的依賴項,這兩項依賴我們在專案的依賴項裡已經新增過了,但是這裡還需要再新增一次,我在配置這裡的時候如果這裡不新增執行外掛的時候就會報錯。

新增generatorConfig.xml配置檔案

這一步是我們生成實體類等的關鍵,所有的規則都是在這個配置檔案裡配置的。 首先在src/main/resources目錄下建立generator目錄,在該目錄下建立generatorConfig.xml配置檔案,內容如下:

<?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>

	<!-- 匯入配置檔案 -->
    <properties resource="application.properties"/>
	
	<!-- defaultModelType="flat" s設定複合主鍵時不單獨為主鍵建立實體 -->
    <context id="MySql" defaultModelType="flat" targetRuntime="MyBatis3Simple">
    
    	<property name="javaFileEncoding" value="UTF-8"/>
		<!-- 當表名或者欄位名為SQL關鍵字的時候,可以設定該屬性為true,MBG會自動給表名或欄位名新增分隔符(反單引號) -->
		<property name="autoDelimitKeywords" value="true"/>
		
		<!-- 由於beginningDelimiter和endingDelimiter的預設值為雙引號("),在Mysql中不能這麼寫,所以還要將這兩個預設值改為反單引號(`), -->
		<property name="beginningDelimiter" value="`"/>
		<property name="endingDelimiter" value="`"/> 
		
        <!-- 生成的POJO實現java.io.Serializable介面 -->
        <plugin type="org.mybatis.generator.plugins.SerializablePlugin" />

        <!--註釋-->
        <commentGenerator>
            <!-- 阻止生成註釋 -->
            <property name="suppressAllComments" value="true"/>
            <!-- 註釋裡不新增日期 -->
            <property name="suppressDate" value="true"/>
        </commentGenerator>
        <!-- 資料庫連線,直接通過${}讀取application.properties裡的配置 -->
        <jdbcConnection
                driverClass="${spring.datasource.driverClassName}"
                connectionURL="${spring.datasource.url}"
                userId="${spring.datasource.username}"
                password="${spring.datasource.password}"/>

        <!-- 生成POJO物件,並將類放到com.web.springbootmybatis.entity包下 -->
        <javaModelGenerator targetPackage="com.web.springboot.entity" targetProject="src/main/java"></javaModelGenerator>
        <!-- 生成mapper xml檔案,並放到resources下的mapper資料夾下 -->
        <sqlMapGenerator targetPackage="mapper"  targetProject="src/main/resources"></sqlMapGenerator>

        <!-- 生成mapper xml對應dao介面,放到com.web.springbootmybatis.dao包下-->
        <javaClientGenerator targetPackage="com.web.springboot.dao" targetProject="src/main/java" type="XMLMAPPER"></javaClientGenerator>

        <!-- table標籤可以有多個,至少一個,tableName指定表名,可以使用_和%萬用字元,我這裡的配置表明匹配所有的表 -->
        <table tableName="%">
            <!-- 是否只生成POJO物件 -->
            <property name="modelOnly" value="false"/>
            <!-- 插入一條資料時,將id對映到實體類中 -->
            <generatedKey column="id" sqlStatement="Mysql"/>
        </table>
    </context>
</generatorConfiguration>

複製程式碼

上面的配置是最常用的配置,注意使用的時候包名,資料庫連線等某些配置需要根據自己的實際情況改寫,一般使用這個就已經可以了,如果有其他的特別的需求可以參考這篇部落格,裡邊講解了該配置檔案所有標籤,屬性的意思,解釋的非常清楚,想要深入瞭解的朋友請移步【傳送門

執行外掛

  1. eclipse中,右鍵專案-->Run As-->Maven build...-->在Goals中填寫命令mybatis-generator:generate,點選Run即可。
  2. 在命令列中執行,需要切換到專案pom.xml檔案所在的目錄,執行命令mvn mybatis-generator:generate即可。

執行完之後會發現專案中已經創好了相應的類以及介面檔案,專案結構圖:

在這裡插入圖片描述

實體類demo:

package com.web.springboot.entity;

import java.io.Serializable;

public class SysUser implements Serializable {
    private Integer id;

    private String usercode;

    private String username;

    private String password;

    private String salt;

    private String locked;

    private static final long serialVersionUID = 1L;

    //省略getter.setter方法
}
複製程式碼

sql介面demo:

package com.web.springboot.dao;

import com.web.springboot.entity.SysUser;
import java.util.List;

public interface SysUserMapper {
    int deleteByPrimaryKey(Integer id);

    int insert(SysUser record);

    SysUser selectByPrimaryKey(Integer id);

    List<SysUser> selectAll();

    int updateByPrimaryKey(SysUser record);
}
複製程式碼

xml檔案demo:

<?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.web.springboot.dao.SysUserMapper" >
  <resultMap id="BaseResultMap" type="com.web.springboot.entity.SysUser" >
    <id column="id" property="id" jdbcType="INTEGER" />
    <result column="usercode" property="usercode" jdbcType="VARCHAR" />
    <result column="username" property="username" jdbcType="VARCHAR" />
    <result column="password" property="password" jdbcType="VARCHAR" />
    <result column="salt" property="salt" jdbcType="VARCHAR" />
    <result column="locked" property="locked" jdbcType="CHAR" />
  </resultMap>
  <delete id="deleteByPrimaryKey" parameterType="java.lang.Integer" >
    delete from sys_user
    where id = #{id,jdbcType=INTEGER}
  </delete>
  <insert id="insert" parameterType="com.web.springboot.entity.SysUser" >
    <selectKey resultType="java.lang.Integer" keyProperty="id" order="BEFORE" >
      SELECT LAST_INSERT_ID()
    </selectKey>
    insert into sys_user (id, usercode, username, 
      `password`, salt, locked
      )
    values (#{id,jdbcType=INTEGER}, #{usercode,jdbcType=VARCHAR}, #{username,jdbcType=VARCHAR}, 
      #{password,jdbcType=VARCHAR}, #{salt,jdbcType=VARCHAR}, #{locked,jdbcType=CHAR}
      )
  </insert>
  <update id="updateByPrimaryKey" parameterType="com.web.springboot.entity.SysUser" >
    update sys_user
    set usercode = #{usercode,jdbcType=VARCHAR},
      username = #{username,jdbcType=VARCHAR},
      `password` = #{password,jdbcType=VARCHAR},
      salt = #{salt,jdbcType=VARCHAR},
      locked = #{locked,jdbcType=CHAR}
    where id = #{id,jdbcType=INTEGER}
  </update>
  <select id="selectByPrimaryKey" resultMap="BaseResultMap" parameterType="java.lang.Integer" >
    select id, usercode, username, `password`, salt, locked
    from sys_user
    where id = #{id,jdbcType=INTEGER}
  </select>
  <select id="selectAll" resultMap="BaseResultMap" >
    select id, usercode, username, `password`, salt, locked
    from sys_user
  </select>
</mapper>
複製程式碼

我使用過的就這兩種,親測可行,還有其他的方式,具體請參考官方文件【傳送門

配置中的一些常見錯誤及解決方式

  • pom.xml檔案中新增完外掛之後報錯如下:

    Plugin execution not covered by lifecycle configuration:
    org.mybatis.generator:mybatis-generator-maven-plugin:1.3.2:generate
    (execution: Generate MyBatis Artifacts, phase: generate-sources)
    複製程式碼

    解決方式:在<plugins> 標籤外再套一個 <pluginManagement> 標籤,如下:

    <build>
        <pluginManagement>
            <plugins>
                <plugin> ... </plugin>
                <plugin> ... </plugin>
                ....
            </plugins>
        </pluginManagement>
    </build>
    複製程式碼

    如果還沒解決可以參照這裡【傳送門

  • 執行外掛的時候失敗並報錯如下:

    在這裡插入圖片描述

    這一類錯誤出現的最多,解決的辦法也不是唯一的,我根據自己的踩坑以及網上的資料提供兩個大的方向。

    解決方式:

    1. 外掛版本問題,可以網上搜尋換一個版本試試。
    2. generatorConfig.xml裡邊有錯誤,比如變數名不對,包名不對等,仔細檢查往往有驚喜。

整合PageHelper

PageHelper是一款非常簡單的分頁外掛,只需要兩行程式碼即可,不需要我們自己編寫sql語句,自動幫我們實現,非常好用,下面開始介紹Spring boot整合PageHelper

新增依賴

首先在pom.xml檔案中新增依賴,如下:

<!-- springboot分頁外掛 -->
<dependency>
    <groupId>com.github.pagehelper</groupId>
    <artifactId>pagehelper-spring-boot-starter</artifactId>
    <!-- 特別注意版本問題 -->
    <version>1.2.3</version>
</dependency>
複製程式碼

新增這個依賴之後,我們其實不需要再新增mybatis-spring-boot-starter的依賴了,因為pagehelper-spring-boot-starter已經將其包含在其中了。

配置檔案中新增配置

aiilication.properties檔案中新增如下內容:

#pageHelper配置
pagehelper.helperDialect=mysql
pagehelper.reasonable=true
pagehelper.params=count=countSql
pagehelper.supportMethodsArguments=true
複製程式碼

這裡簡單介紹下幾個引數的意思,網上的教程普遍沒寫,這裡介紹下:

  • helperDialect:配置使用哪種資料庫語言,不配置的話pageHelper也會自動檢測,我這裡使用的mysql
  • reasonable:配置分頁引數合理化功能,預設是false。啟用合理化時,如果pageNum<1會查詢第一頁,如果pageNum>總頁數會查詢最後一頁; 禁用合理化時,如果pageNum<1或pageNum>總頁數會返回空資料。
  • params:為了支援startPage(Object params)方法,增加了該引數來配置引數對映,用於從物件中根據屬性名取值, 可以配置 pageNum,pageSize,count,pageSizeZero,reasonable,不配置對映的用預設值,預設值為pageNum=pageNum;pageSize=pageSize;count=countSql;reasonable=reasonable;pageSizeZero=pageSizeZero
  • supportMethodsArguments:支援通過Mapper介面引數來傳遞分頁引數,預設值false,分頁外掛會從查詢方法的引數值中,自動根據上面 params 配置的欄位中取值,查詢到合適的值時就會自動分頁。

更多引數的介紹請移步【傳送門

程式碼中使用

經過上面的介紹使用MBG已經為我們建立好了實體類,sql介面以及xml檔案,現在我們需要建立service層,這裡我們利用泛型知識先建立一個service層的基類介面IBaseService.java,之後所有的service介面都繼承這個基類介面,這在實際的專案中是很有必要的,減少重複沒必要的操作,程式碼如下:

package com.web.springboot.service;

import java.util.List;

/**
* @author Promise
* @createTime 2019年1月20日 下午8:24:40
* @description service介面層基類,包含基本的	CRUD操作。
*/
public interface IBaseService<T> {
	
	/**
	 * 根據主鍵刪除
	 * @param id
	 * @return
	 */
	int deleteByPrimaryKey(Object id);

	/**
	 * 新增一條記錄
	 * @param entity
	 * @return
	 */
    int insert(T entity);

    /**
     * 根據主鍵查詢
     * @param id
     * @return
     */
    T selectByPrimaryKey(Object id);

    /**
     * 查詢全部記錄
     * @return
     */
    List<T> selectAll();

    /**
     * 根據主鍵修改資料
     * @param entity
     * @return
     */
    int updateByPrimaryKey(T entity);
}
複製程式碼

該基類介面定義了基本的CRUD操作,我這裡寫的比較簡單,實際中完全可以根據自己的需求完善。

然後建立ISysUserService.java介面,繼承該基類介面,並定義兩個我們驗證pageHelper的方法:

package com.web.springboot.service;

import java.util.List;

import com.github.pagehelper.PageInfo;
import com.web.springboot.entity.SysUser;

/**
* @author Promise
* @createTime 2019年1月20日 下午8:31:09
* @description 
*/
public interface ISysUserService extends IBaseService<SysUser>{

	List<SysUser> findAllByPage(int pageNum, int pageSize);
	
	PageInfo<SysUser> findAllByPage2(int pageNum, int pageSize);
}

複製程式碼
  • findAllByPage:實現簡單的分頁,返回所需物件的list集合,其中引數pageNum代表當前頁,pageSize代表每頁多少條資料。
  • findAllByPage2:實現簡單的分頁,返回PageInfo<T>物件,包括所需物件的list集合,還包括資料庫總記錄數,總頁數,當前頁,下一頁等諸多資訊,其中引數pageNum代表當前頁,pageSize代表每頁多少條資料。

可以根據專案實際需要選擇使用哪一種方法。

介面實現類SysUserServiceImpl.java,程式碼如下:

package com.web.springboot.service.impl;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.web.springboot.dao.SysUserMapper;
import com.web.springboot.entity.SysUser;
import com.web.springboot.service.ISysUserService;

/**
* @author Promise
* @createTime 2019年1月20日 下午8:31:50
* @description 
*/
@Service("sysUserService")
public class SysUserServiceImpl implements ISysUserService{
	
	@Autowired
	private SysUserMapper sysUserMapper;

	@Override
	public int deleteByPrimaryKey(Object id) {
		// TODO Auto-generated method stub
		return 0;
	}

	@Override
	public int insert(SysUser entity) {
		// TODO Auto-generated method stub
		return 0;
	}

	@Override
	public SysUser selectByPrimaryKey(Object id) {
		// TODO Auto-generated method stub
		return sysUserMapper.selectByPrimaryKey((Integer)id);
	}

	@Override
	public List<SysUser> selectAll() {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public int updateByPrimaryKey(SysUser entity) {
		// TODO Auto-generated method stub
		return 0;
	}

	@Override
	public List<SysUser> findAllByPage(int pageNum, int pageSize) {
		// TODO Auto-generated method stub
		PageHelper.startPage(pageNum, pageSize);
		List<SysUser> lists = sysUserMapper.selectAll();
		return lists;
	}

	@Override
	public PageInfo<SysUser> findAllByPage2(int pageNum, int pageSize) {
		// TODO Auto-generated method stub
		PageHelper.startPage(pageNum, pageSize);
		List<SysUser> lists = sysUserMapper.selectAll();
		PageInfo<SysUser> pageInfo = new PageInfo<SysUser>(lists);
		return pageInfo;
	}
}

複製程式碼

可以看到該類中實現了我們在基類介面IBaseService.java以及ISysUSerService.java裡邊定義的所有方法,這裡我們先重點關注findAllByPage(int pageNum, int pageSize)方法中的

PageHelper.startPage(pageNum, pageSize);
List<SysUser> lists = sysUserMapper.selectAll();
複製程式碼

這段程式碼,第一句使用了我們的分頁外掛,就這一句即可,需要注意的是查詢語句必須緊跟這一句,且只能使用一次,意思就是如果還有一個分頁查詢需要再定義一次PageHelper.startPage(pageNum, pageSize)

再來說findAllByPage2(int pageNum, int pageSize)方法,只比上邊的方法多一句話,且返回結果不一樣

PageInfo<SysUser> pageInfo = new PageInfo<SysUser>(lists);
複製程式碼

當然,PageHelper還有其它的多種使用方式,上面的方式要想保證正確性必須要查詢程式碼緊跟分頁程式碼才行,需要程式設計師去控制,這就增加了出現錯誤的機率,所以這裡再介紹一種更為安全的方式--ISelect 介面方式:

findAllByPage(int pageNum, int pageSize)改版:

@Override
public List<SysUser> findAllByPage(int pageNum, int pageSize) {
	//這種寫法需要jdk8 lambda用法
	Page<SysUser> page = PageHelper.startPage(pageNum, pageSize).doSelectPage(()-> sysUserMapper.selectAll());
	//如果是低版本的jdk,則使用如下寫法(兩種寫法根據自己jdk版本二選一)
	Page<SysUser> page = PageHelper.startPage(pageNum, pageSize).doSelectPage(new ISelect() {
		@Override
		public void doSelect() {
			sysUserMapper.selectAll();
		}
	});
	return page;
}
複製程式碼

看到這裡有的朋友或許會發現我們返回的是一個Page<SysUser>物件,而方法定義的是一個List<SysUser>物件,這裡我直接貼出PageHelper中該方法定義的原始碼大家就一目瞭然了:

public class Page<E> extends ArrayList<E> implements Closeable {
    //為了不佔過多的篇幅,這裡只貼出方法定義的原始碼,想要了解具體內容請自行檢視原始碼。
    //可以看到該方法繼承了ArrayList<T>這也是我們可以直接返回Page<SysUser>的原因。
}
複製程式碼

findAllByPage2(int pageNum, int pageSize)改版:

@Override
public PageInfo<SysUser> findAllByPage2(int pageNum, int pageSize) {
    //這種寫法需要jdk8 lambda用法
	PageInfo<SysUser> pageInfo = PageHelper.startPage(pageNum, pageSize).doSelectPageInfo(()-> sysUserMapper.selectAll());
	//如果是低版本的jdk,則使用如下寫法(兩種寫法根據自己jdk版本二選一)
	PageInfo<SysUser> pageInfo = PageHelper.startPage(pageNum, pageSize).doSelectPageInfo(new ISelect() {
		@Override
		public void doSelect() {
			sysUserMapper.selectAll();
		}
	});
	return pageInfo;
}
複製程式碼

這裡就給出這兩種使用PageHelper的方法了,想要了解更多請移步【傳送門

接下來建立測試控制器TestController.java,如下:

package com.web.springboot.controller;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import com.github.pagehelper.PageInfo;
import com.web.springboot.entity.SysUser;
import com.web.springboot.service.ISysUserService;

/**
* @author Promise
* @createTime 2019年1月20日 下午8:21:02
* @description 
*/
@RestController
public class TestController {
	
	@Autowired
	private ISysUserService sysUserService;

	@RequestMapping("/users/{pageNum}/{pageSize}")
	public Object getAllUser(@PathVariable int pageNum, @PathVariable int pageSize) {
		List<SysUser> lists=sysUserService.findAllByPage(pageNum, pageSize);
		return lists;
	}
	
	@RequestMapping("/users2/{pageNum}/{pageSize}")
	public Object getAllUser2(@PathVariable int pageNum, @PathVariable int pageSize) {
		PageInfo<SysUser> pageInfo=sysUserService.findAllByPage2(pageNum, pageSize);
		return pageInfo;
	}
}

複製程式碼

好了,程式碼就編寫完成了!

驗證分頁

在這之前,我們需要在資料中準備一些資料,這裡先貼上我的資料庫資料情況,實際以自身的資料庫資料為準:

在這裡插入圖片描述

啟動專案,瀏覽器訪問localhost:8080/users/1/2,表示訪問第一頁,每頁兩條資料,結果如下:

在這裡插入圖片描述

對比資料庫,資料返回正確,接下來訪問localhost:8080/user2/1/2,結果如下:

在這裡插入圖片描述

發現json資料沒有格式,不利於我們檢視,可以使用線上json格式轉化工具格式化內容如下:

{
	"pageNum": 1,
	"pageSize": 2,
	"size": 2,
	"startRow": 1,
	"endRow": 2,
	"total": 6,
	"pages": 3,
	"list": [{
		"id": 1,
		"usercode": "Promise",
		"username": "eran",
		"password": "123456",
		"salt": null,
		"locked": "0"
	}, {
		"id": 2,
		"usercode": "Promise2",
		"username": "eran",
		"password": "123456",
		"salt": null,
		"locked": "0"
	}],
	"prePage": 0,
	"nextPage": 2,
	"isFirstPage": true,
	"isLastPage": false,
	"hasPreviousPage": false,
	"hasNextPage": true,
	"navigatePages": 8,
	"navigatepageNums": [1, 2, 3],
	"navigateFirstPage": 1,
	"navigateLastPage": 3,
	"lastPage": 3,
	"firstPage": 1
}
複製程式碼

可以發現返回了很多有用的內容,這裡就不一一介紹了,欄位都淺顯易懂,實際開發中我們可以根據需要選擇使用哪種方式。

結語

關於Spring boot整合MBG以及pageHelper的內容就到這裡了,下篇部落格見。bye~

相關文章