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


一、前言

上次用Mybatis還是2017年做項目的時候,已經很久過去了。中途再沒有用過Mybatis。導致現在學習SpringBoot過程中遇到一些Mybatis的問題,以此做出總結(XML極簡模式)。當然只是實用方面的總結,具體就不深究♂了。這里只總結怎么用!!!

(這次直接跳到十一,是因為中間是RabbitMQ 詳解,大家看微笑哥的就夠了)

二、關於Mybatis

1、什么是Mybatis

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

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

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

2、Mybaits的優點

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

(2)與JDBC相比,減少了50%以上的代碼量,消除了JDBC大量冗余的代碼,不需要手動開關連接;

(3)很好的與各種數據庫兼容(因為MyBatis使用JDBC來連接數據庫,所以只要JDBC支持的數據庫MyBatis都支持)。

(4)能夠與Spring很好的集成;

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

3、MyBatis框架的缺點

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

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

4、MyBatis框架適用場合

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

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

5、MyBatis與Hibernate有哪些不同

(1)Mybatis和hibernate不同,它不完全是一個ORM框架,因為MyBatis需要程序員自己編寫Sql語句。

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

(3)Hibernate對象/關系映射能力強,數據庫無關性好,對於關系模型要求高的軟件,如果用hibernate開發可以節省很多代碼,提高效率。


三、使用總結

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

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

所有測試都已經通過Postman發送請求測試。

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


1、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

2、當實體類中的屬性名和表中的字段名不一樣,怎么辦

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

<!-- 查詢用戶信息列表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>

推薦使用第二種。

2、獲取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();
	}
}

3、模糊查詢

使用%"#{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>

4、多條件查詢

1、使用@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>

2、使用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>
	</where>
</select>

5、批量刪除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

6、多表查詢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舉個栗子。

1、用戶表實體類

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

2、部門表實體類

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

3、角色表實體類

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

4、Mapper、Service部分(略)

List<SysUser> queryUserRoleDept(SysUser user);

5、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>

6、Controller部分

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

7、測試部分

請求結果:

多表關聯查詢測試報文

7、分頁插件

使用分頁插件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;
}

參考:https://www.cnblogs.com/java-gcs/p/10979821.html

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

To be continued


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM