Spring Boot2(十一):Mybatis使用總結(自增加、多條件、批量操做、多表查詢等等)

1、前言

上次用Mybatis仍是2017年作項目的時候,已經好久過去了。中途再沒有用過Mybatis。致使如今學習SpringBoot過程當中遇到一些Mybatis的問題,以此作出總結(XML極簡模式)。固然只是實用方面的總結,具體就不深究 ♂ 了。這裏只總結怎麼用!!!html

(此次直接跳到十一,是由於中間是RabbitMQ 詳解,你們看微笑哥的就夠了)java

源碼:github.com/niaobulashi…mysql

2、關於Mybatis

一、什麼是Mybatis

(1)Mybatis是一個半ORM(對象關係映射)框架,它內部封裝了JDBC,開發時只須要關注SQL語句自己,不須要花費精力去處理加載驅動、建立鏈接、建立statement等繁雜的過程。程序員直接編寫原生態sql,能夠嚴格控制sql執行性能,靈活度高。git

(2)MyBatis 可使用 XML 或註解來配置和映射原生信息,將 POJO映射成數據庫中的記錄,避免了幾乎全部的 JDBC 代碼和手動設置參數以及獲取結果集。程序員

(3)經過xml 文件或註解的方式將要執行的各類 statement 配置起來,並經過java對象和 statement中sql的動態參數進行映射生成最終執行的sql語句,最後由mybatis框架執行sql並將結果映射爲java對象並返回。(從執行sql到返回result的過程)。github

二、Mybaits的優勢

(1)基於SQL語句編程,至關靈活,不會對應用程序或者數據庫的現有設計形成任何影響,SQL寫在XML裏,解除sql與程序代碼的耦合,便於統一管理;提供XML標籤,支持編寫動態SQL語句,並可重用。spring

(2)與JDBC相比,減小了50%以上的代碼量,消除了JDBC大量冗餘的代碼,不須要手動開關鏈接;sql

(3)很好的與各類數據庫兼容(由於MyBatis使用JDBC來鏈接數據庫,因此只要JDBC支持的數據庫MyBatis都支持)。數據庫

(4)可以與Spring很好的集成;編程

(5)提供映射標籤,支持對象與數據庫的ORM字段關係映射;提供對象關係映射標籤,支持對象關係組件維護。

三、MyBatis框架的缺點

(1)SQL語句的編寫工做量較大,尤爲當字段多、關聯表多時,對開發人員編寫SQL語句的功底有必定要求。

(2)SQL語句依賴於數據庫,致使數據庫移植性差,不能隨意更換數據庫。

四、MyBatis框架適用場合

(1)MyBatis專一於SQL自己,是一個足夠靈活的DAO層解決方案。

(2)對性能的要求很高,或者需求變化較多的項目,如互聯網項目,MyBatis將是不錯的選擇。

五、MyBatis與Hibernate有哪些不一樣

(1)Mybatis和hibernate不一樣,它不徹底是一個ORM框架,由於MyBatis須要程序員本身編寫Sql語句。

(2)Mybatis直接編寫原生態sql,能夠嚴格控制sql執行性能,靈活度高,很是適合對關係數據模型要求不高的軟件開發,由於這類軟件需求變化頻繁,一但需求變化要求迅速輸出成果。可是靈活的前提是mybatis沒法作到數據庫無關性,若是須要實現支持多種數據庫的軟件,則須要自定義多套sql映射文件,工做量大。

(3)Hibernate對象/關係映射能力強,數據庫無關性好,對於關係模型要求高的軟件,若是用hibernate開發能夠節省不少代碼,提升效率。


3、使用總結

如下的用法實例建議將源碼clone到本地運行,所有使用的是XMl極簡模式

由於我沒有貼出完整的代碼,只貼出關鍵處理的部分

全部測試都已經經過Postman發送請求測試。

不過我建議各位看官能夠用下IDEA的插件:Restfultookit,很是好用的,根據controller定義的url地址快捷生成請求報文,能夠直接測試。對於測試報文來講這個插件簡直無敵!強烈推薦(已經安裝的當我沒說)


一、Java,JDBC與MySQL數據類型對照數據類型關係表

任何MySQL數據類型均可以轉換爲Java數據類型。

若是選擇的Java數值數據類型的精度或容量低於要轉換爲的MySQL數據類型,則可能會出現舍入,溢出或精度損失。

下表列出了始終保證有效的轉換。 第一列列出了一種或多種MySQL數據類型,第二列列出了能夠轉換MySQL類型的一種或多種Java類型。

These MySQL Data Types Can always be converted to these Java types
CHAR, VARCHAR, BLOB, TEXT, ENUM, and SET java.lang.String, java.io.InputStream, java.io.Reader, java.sql.Blob, java.sql.Clob
FLOAT, REAL, DOUBLE PRECISION, NUMERIC, DECIMAL, TINYINT, SMALLINT, MEDIUMINT, INTEGER, BIGINT java.lang.String, java.lang.Short, java.lang.Integer, java.lang.Long, java.lang.Double, java.math.BigDecimal
DATE, TIME, DATETIME, TIMESTAMP java.lang.String, java.sql.Date, java.sql.Timestamp

ResultSet.getObject()方法使用MySQL和Java類型之間的類型轉換,遵循適當的JDBC規範。 ResultSetMetaData.GetColumnTypeName()和ResultSetMetaData.GetColumnClassName()返回的值以下表所示。 有關JDBC類型的更多信息,請參閱java.sql.Types類的參考。

MySQL Type Name Return value of GetColumnTypeName Return value of GetColumnClassName
BIT(1) BIT java.lang.Boolean
BIT( > 1) BIT byte[]
TINYINT TINYINT java.lang.Boolean if the configuration property tinyInt1isBit is set to true (the default) and the storage size is 1, or java.lang.Integer if not.
BOOL, BOOLEAN TINYINT See TINYINT, above as these are aliases for TINYINT(1), currently.
SMALLINT[(M)] [UNSIGNED] SMALLINT [UNSIGNED] java.lang.Integer (regardless of whether it is UNSIGNED or not)
MEDIUMINT[(M)] [UNSIGNED] MEDIUMINT [UNSIGNED] java.lang.Integer (regardless of whether it is UNSIGNED or not)
INT,INTEGER[(M)] [UNSIGNED] INTEGER [UNSIGNED] java.lang.Integer, if UNSIGNED java.lang.Long
BIGINT[(M)] [UNSIGNED] BIGINT [UNSIGNED] java.lang.Long, if UNSIGNED java.math.BigInteger
FLOAT[(M,D)] FLOAT java.lang.Float
DOUBLE[(M,B)] DOUBLE java.lang.Double
DECIMAL[(M[,D])] DECIMAL java.math.BigDecimal
DATE DATE java.sql.Date
DATETIME DATETIME java.sql.Timestamp
TIMESTAMP[(M)] TIMESTAMP java.sql.Timestamp
TIME TIME java.sql.Time
YEAR[(2|4)] YEAR If yearIsDateType configuration property is set to false, then the returned object type is java.sql.Short. If set to true (the default), then the returned object is of type java.sql.Date with the date set to January 1st, at midnight.
CHAR(M) CHAR java.lang.String (unless the character set for the column is BINARY, then byte[] is returned.
VARCHAR(M) [BINARY] VARCHAR java.lang.String (unless the character set for the column is BINARY, then byte[] is returned.
BINARY(M) BINARY byte[]
VARBINARY(M) VARBINARY byte[]
TINYBLOB TINYBLOB byte[]
TINYTEXT VARCHAR java.lang.String
BLOB BLOB byte[]
TEXT VARCHAR java.lang.String
MEDIUMBLOB MEDIUMBLOB byte[]
MEDIUMTEXT VARCHAR java.lang.String
LONGBLOB LONGBLOB byte[]
LONGTEXT VARCHAR java.lang.String
ENUM('value1','value2',...) CHAR java.lang.String
SET('value1','value2',...) CHAR java.lang.String

參考:6.5 Java, JDBC, and MySQL Types

二、當實體類中的屬性名和表中的字段名不同,怎麼辦

其一:定義字段別名,使之與實體類屬性名一致。

<!-- 查詢用戶信息列表1 -->
<select id="queryUserList1" resultType="com.niaobulashi.entity.SysUser">
   SELECT
		u.user_id, u.username userNameStr, u.password, u.salt, u.email,
		u.mobile, u.status, u.dept_id, u.create_time
	FROM
		sys_user u
	where 1=1
</select>
複製代碼

其二:經過resultMap映射字段名和實體類屬性名保持一致

<resultMap id="sysUserInfoMap" type="com.niaobulashi.entity.SysUser">
	<!-- 用戶Id屬性來映射主鍵字段 userId-->
	<id property="id" column="userId"/>
	<!-- 用result屬性來映射非主鍵字段,property爲實體類屬性名,column爲數據表中的屬性-->
	<result property="userNameStr" column="username"/>
</resultMap>

<!--用戶Vo-->
<sql id="selectSysUserVo">
	SELECT
		u.user_id, u.username, u.password, u.salt, 
		u.email, u.mobile, u.status, u.dept_id, u.create_time
	FROM
		sys_user u
</sql>

<!-- 查詢用戶信息列表2 -->
<select id="queryUserList2" resultMap="sysUserInfoMap">
    <include refid="selectSysUserVo"/>
    where 1=1
</select>
複製代碼

推薦使用第二種。

二、獲取Mybatis自增加主鍵

思路:useGeneratedKeys="true" keyProperty="id"

<!-- 獲取自動生成的(主)鍵值 -->
<insert id="insertSysTest" parameterType="com.niaobulashi.model.SysTest" useGeneratedKeys="true" keyProperty="id">
	INSERT INTO sys_test(name, age, nick_name) VALUES (#{name},#{age},#{nickName})
</insert>
複製代碼

獲取自增加主鍵

/** * 獲取自增加主鍵ID * @param sysTest * @throws Exception */
@RequestMapping(value = "/add", method = RequestMethod.POST)
private void addSysTest(@RequestBody SysTest sysTest) throws Exception {
	try {
		SysTest sysTestParam = new SysTest();
        // 將傳入參數Copy到新申明的對象中,這樣才能從sysTestParam中獲取到自增加主鍵
		BeanUtils.copyProperties(sysTest, sysTestParam);
		this.sysTestService.insertSysTest(sysTestParam);
		log.info("獲取自增加主鍵爲:" + sysTestParam.getId());
	} catch (Exception e) {
		e.printStackTrace();
		throw new Exception();
	}
}
複製代碼

三、模糊查詢

使用%"#{value}"%"方法會引發SQL注入

推薦使用:CONCAT('%',#{value},'%')

<!--用戶Vo-->
<sql id="selectSysUserVo">
	SELECT
		u.user_id, u.username, u.password, u.salt, 
		u.email, u.mobile, u.status, u.dept_id, u.create_time
	FROM
		sys_user u
</sql>

<!-- 查詢用戶信息列表2 -->
<select id="queryUserListByName" parameterType="String" resultMap="sysUserInfoMap">
    <include refid="selectSysUserVo"/>
	where 1=1
	and u.username like concat('%',#{userName},'%')
</select>
複製代碼

四、多條件查詢

一、使用@Param

List<SysUser> queryUserByNameAndEmail(@Param("userName") String userName, @Param("email") String email);
複製代碼
<!--使用用戶名和郵箱查詢用戶信息-->
<select id="queryUserByNameAndEmail" resultMap="sysUserInfoMap">
	<include refid="selectSysUserVo"/>
	<where>
        <if test="userName != null and userName != ''">
            AND u.username like concat('%',#{userName},'%')
        </if>
        <if test="email != null and email != ''">
            AND u.email like concat('%',#{email},'%')
        </if>
	</where>
</select>
複製代碼

二、使用JavaBean

這裏給了一些常見的查詢條件:日期、金額。

List<SysUser> queryUserByUser(SysUser sysUser);
複製代碼
<select id="queryUserByUser" parameterType="com.niaobulashi.model.SysUser" resultMap="sysUserInfoMap">
	<include refid="selectSysUserVo"/>
	<where>
		1=1
		<if test="userNameStr != null and userNameStr != ''">
			AND u.username like concat('%', #{userNameStr}, '%')
		</if>
		<if test="email != null and email != ''">
			AND u.email like concat('%', #{email}, '%')
		</if>
		<if test="mobile != null and mobile != ''">
			AND u.mobile like concat('%', #{mobile}, '%')
		</if>
		<if test="createDateStart != null and createDateStart != ''">/*開始時間檢索*/
			AND date_format(u.create_time, '%y%m%d') <![CDATA[ >= ]]> date_format(#{createDateStart}, '%y%m%d')
		</if>
		<if test="createDateEnd != null and createDateEnd != ''">/*結束時間檢索*/
			AND date_format(u.create_time, '%y%m%d') <![CDATA[ <= ]]> date_format(#{createDateEnd}, '%y%m%d')
		</if>
		<if test="amtFrom != null and amtFrom != ''">/*起始金額*/
			AND u.amt <![CDATA[ >= ]]> #{amtFrom}
		</if>
		<if test="amtTo != null and amtTo != ''">/*截至金額*/
			AND u.amt <![CDATA[ <= ]]> #{amtTo}
		</if>
		<if test="updateDateStart != null and updateDateStart != ''">/*開始時間檢索*/
            AND date_format(u.update_date, '%y%m%d') <![CDATA[ >= ]]> date_format(#{updateDateStart}, '%y%m%d')
        </if>
        <if test="updateDateEnd != null and updateDateEnd != ''">/*結束時間檢索*/
            AND date_format(u.update_date, '%y%m%d') <![CDATA[ <= ]]> date_format(#{updateDateEnd}, '%y%m%d')
        </if>
	</where>
</select>
複製代碼

五、批量刪除foreach

xml部分

<delete id="deleteSysTestByIds" parameterType="String">
	delete from sys_test where id in
	<foreach collection="array" item="id" open="(" separator="," close=")">
		#{id}
	</foreach>
</delete>
複製代碼

其中foreach包含屬性講解:

  • open:整個循環內容開頭的字符串。
  • close:整個循環內容結尾的字符串。
  • separator:每次循環的分隔符。
  • item:從迭代對象中取出的每個值。
  • index:若是參數爲集合或者數組,該值爲當前索引值,若是參數爲Map類型時,該值爲Map的key。
  • collection:要迭代循環的屬性名。

dao部分

int deleteSysTestByIds(String[] ids);
複製代碼

service層

@Transactional(rollbackFor = Exception.class)
@Override
public int deleteDictDataByIds(String ids) throws Exception{
	try {
		return sysTestDao.deleteSysTestByIds(ids.split(","));
	} catch (Exception e) {
		e.printStackTrace();
		throw new Exception();
	}
}
複製代碼

controller

@RequestMapping(value = "/deleteIds", method = RequestMethod.POST)
public int deleteIds(String ids) throws Exception {
	try {
		return sysTestService.deleteDictDataByIds(ids);
	} catch (Exception e) {
		e.printStackTrace();
		throw new Exception();
	}
}
複製代碼

請求URL:http://localhost:8081/test/deleteIds

請求報文:

ids : 1,2
複製代碼

六、多表查詢association和collection

多表查詢,多表確定首先咱們先要弄清楚兩個關鍵字:

association: 一對一關聯(has one)collection:一對多關聯(has many)

的各個屬性的含義:

association和collection
property:映射數據庫列的字段或屬性。
colum:數據庫的列名或者列標籤別名。
javaTyp:完整java類名或別名。
jdbcType:支持的JDBC類型列表列出的JDBC類型。這個屬性只在insert,update或delete的時候針對容許空的列有用。
resultMap:一個能夠映射聯合嵌套結果集到一個適合的對象視圖上的ResultMap。這是一個替代的方式去調用另外一個select語句。

這樣提及來可能很差理解,我舉個栗子

涉及到這三張表,我粗略的畫了一下:

- 用戶表 部門表 角色表
表名 sys_user sys_dept sys_role
與用戶表關係 - 一對一(一個用戶只屬於一個部門) 一對多(一個用戶能夠有多個角色)

因而用戶表關聯部門表,咱們用association

用戶表關聯角色表,咱們用collection

固然了,能用得這麼蛋疼關鍵字的前提條件是,你要查詢關聯的字段,若是你只是關聯不查它,那就不須要用這玩意。。

辣麼,我結合這兩個多表查詢的關鍵字associationcollection舉個栗子。

一、用戶表實體類

@Data
public class SysUser implements Serializable {
	private static final long serialVersionUID = 1L;
	/** 用戶ID */
	private Long userId;
	/** 用戶名 */
	private String userNameStr;
	/** 密碼 */
	private String password;
	/** 鹽 */
	private String salt;
	/** 郵箱 */
	private String email;
	/** 手機號 */
	private String mobile;
	/** 狀態 0:禁用 1:正常 */
	private Integer status;
	/** 部門Id */
	private Long deptId;
	/** 建立時間 */
	private Date createTime;
	/****************關聯部分************** /** 部門 */
	private SysDept dept;
	/** 角色集合 */
	private List<SysRole> roles;
}
複製代碼

二、部門表實體類

@Data
public class SysDept implements Serializable {
    /** 部門ID */
    private Long deptId;
    /** 部門名稱 */
    private String deptName;
}
複製代碼

三、角色表實體類

@Data
public class SysRole implements Serializable {
    /** 角色ID */
    private Long roleId;
    /** 角色名稱 */
    private String roleName;
}
複製代碼

四、Mapper、Service部分(略)

List<SysUser> queryUserRoleDept(SysUser user);
複製代碼

五、XML部分

<!--查看用戶部門和角色信息-->
<select id="queryUserRoleDept" parameterType="com.niaobulashi.model.SysUser" resultMap="UserResult">
	select u.user_id, u.username, u.dept_id, d.dept_name, r.role_id, r.role_name
	from sys_user u
	LEFT JOIN sys_dept d on d.dept_id = u.dept_id
	LEFT JOIN sys_user_role ur on ur.user_id = u.user_id
	LEFT JOIN sys_role r on r.role_id = ur.role_id
	WHERE 1=1
	<if test="userId != null and userId != ''">
		AND u.user_id = #{userId}
	</if>
</select>
複製代碼

UserResult部分

<!--用戶表-->
<resultMap type="com.niaobulashi.model.SysUser" id="UserResult">
	<id property="userId" column="user_id"/>
	<result property="userNameStr" column="username"/>
	<result property="password" column="login_name"/>
	<result property="salt" column="password"/>
	<result property="email" column="email"/>
	<result property="mobile" column="mobile"/>
	<result property="status" column="status"/>
	<result property="deptId" column="dept_id"/>
	<result property="createTime" column="create_time"/>
	<association property="dept" column="dept_id" javaType="com.niaobulashi.model.SysDept" resultMap="DeptResult"/>
	<collection property="roles" javaType="java.util.List" resultMap="RoleResult"/>
</resultMap>

<!--部門表-->
<resultMap id="DeptResult" type="com.niaobulashi.model.SysDept">
	<id property="deptId" column="dept_id"/>
	<result property="deptName" column="dept_name"/>
</resultMap>

<!--角色表-->
<resultMap id="RoleResult" type="com.niaobulashi.model.SysRole">
	<id property="roleId" column="role_id"/>
	<result property="roleName" column="role_name"/>
</resultMap>
複製代碼

六、Controller部分

@RequestMapping(value = "/queryUserRoleDept", method = RequestMethod.POST)
private List<SysUser> queryUserRoleDept(@RequestBody SysUser sysUser) {
	List<SysUser> userList = sysUserService.queryUserRoleDept(sysUser);
	return userList;
}
複製代碼

七、測試部分

請求結果:

多表關聯查詢測試報文

七、分頁插件

使用分頁插件PageHelper Spring Boot Starter,引入maven依賴:PageHelper Spring Boot Starter1.2.12

application.yml配置

# PageHelper分頁插件
pagehelper:
 helperDialect: mysql
 reasonable: true
 supportMethodsArguments: true
 params: count=countSql
複製代碼

controller

@RequestMapping(value = "/queryUserByPage", method = RequestMethod.GET)
private PageInfo queryUserByPage(Integer currentPage, Integer pageSize) {
	PageHelper.startPage(currentPage, pageSize);
	List<SysUser> userList = sysUserService.queryUserRoleDept(new SysUser());
	PageInfo info=new PageInfo(userList);
	return info;
}
複製代碼

目前暫時寫到這裏,本篇會持續補充

To be continued

做者:鳥不拉屎 出處: https://juejin.im/user/5b3de9155188251aa0161fe4

本文版權歸做者和掘金共有,歡迎轉載,但未經做者贊成必須保留此段聲明,且在文章頁面明顯位置給出原文鏈接,不然保留追究法律責任的權利。若是以爲還有幫助的話,能夠點一下左上角的【點贊】。

相關文章
相關標籤/搜索