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
當然了,能用得這麼蛋疼關鍵字的前提條件是,你要查詢關聯的欄位,如果你只是關聯不查它,那就不需要用這玩意。。
辣麼,我結合這兩個多表查詢的關鍵字association、collection舉個栗子。
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;
}
目前暫時寫到這裡,本篇會持續補充
To be contin