📢 本文由 gemini-3-flash-preview 翻譯
MyBatis 系列
MyBatis 安裝與入門:
https://blog.yexca.net/zh-tw/archives/215
MyBatis 使用: 本文
刪除
使用 #{} 作為佔位符,內部為參數名稱
1
2
3
4
5
6
| @Mapper
public interface EmpMapper {
// 刪除
@Delete("delete from mybatis.emp where id = #{id}")
public void delete(Integer id);
}
|
測試
1
2
3
4
5
6
7
8
9
| @SpringBootTest
class Mybatis02ApplicationTests {
@Autowired
private EmpMapper empMapper;
@Test
void contextLoads() {
empMapper.delete(17);
}
}
|
一般不需要回傳值,回傳值為此次操作影響的列數(筆數)
1
2
3
4
5
6
| @Mapper
public interface EmpMapper {
// 刪除
@Delete("delete from mybatis.emp where id = #{id}")
public int delete(Integer id);
}
|
佔位符
參數佔位符有 #{} 和 ${}
| 佔位符 | #{} | ${} |
|---|
| 形式 | 預編譯 | 拼接 |
| 使用時機 | 參數傳遞、登入等 | 對資料表名稱、列表動態設置 |
| 優劣 | 安全,效能高 | 存在 SQL 注入問題 |
插入 (新增)
同樣使用佔位符,不過若傳遞的參數過多可以使用物件封裝,形式參數名稱為物件的屬性名稱
1
2
3
4
5
6
7
| @Mapper
public interface EmpMapper {
// 新增
@Insert("insert into mybatis.emp(username, name, gender, image, job, entrydate, dept_id, create_time, update_time) "+
"values (#{username},#{name},#{gender},#{image},#{job},#{entrydate},#{deptId},#{createTime},#{updateTime});")
public void insert(Emp emp);
}
|
測試
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
| @SpringBootTest
class Mybatis02ApplicationTests {
@Autowired
private EmpMapper empMapper;
@Test
public void testInsert(){
Emp emp = new Emp();
emp.setUsername("Tom");
emp.setName("湯姆");
emp.setGender((short) 1);
emp.setImage("tom.jpg");
emp.setJob((short) 1);
emp.setEntrydate(LocalDate.of(2000,1,1));
emp.setDeptId(1);
emp.setCreateTime(LocalDateTime.now());
emp.setUpdateTime(LocalDateTime.now());
empMapper.insert(emp);
}
}
|
獲取主鍵值
某些情況下在資料新增成功後,需要獲取寫入資料庫資料的主鍵
1
2
3
4
5
6
7
8
9
| @Mapper
public interface EmpMapper {
// 獲取主鍵,第一個屬性定義主鍵賦值在 emp 物件的 id 屬性,第二個屬性代表需要獲取回傳主鍵值
@Options(keyProperty = "id", useGeneratedKeys = true)
// 新增
@Insert("insert into mybatis.emp(username, name, gender, image, job, entrydate, dept_id, create_time, update_time) "+
"values (#{username},#{name},#{gender},#{image},#{job},#{entrydate},#{deptId},#{createTime},#{updateTime});")
public void insert(Emp emp);
}
|
更新 (修改)
與新增類似,可以封裝到一個物件裡
1
2
3
4
5
6
7
| @Mapper
public interface EmpMapper {
// 修改
@Update("update mybatis.emp set username = #{username}, name = #{name}, gender = #{gender}, image = #{image}, job = #{job}, "+
"entrydate = #{entrydate}, dept_id = #{deptId}, update_time = #{updateTime} where id = #{id};")
public void update(Emp emp);
}
|
測試
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
| @SpringBootTest
class Mybatis02ApplicationTests {
@Autowired
private EmpMapper empMapper;
@Test
public void testUpdate(){
Emp emp = new Emp();
emp.setUsername("Tom2");
emp.setName("湯姆2");
emp.setGender((short) 1);
emp.setImage("tom.jpg");
emp.setJob((short) 1);
emp.setEntrydate(LocalDate.of(2000,1,1));
emp.setDeptId(1);
emp.setUpdateTime(LocalDateTime.now());
emp.setId(19);
empMapper.update(emp);
}
}
|
查詢
查詢有兩種,根據 id 查詢全部屬性,以及根據條件查詢
根據 ID 查詢
介面
1
2
3
4
5
6
| @Mapper
public interface EmpMapper {
// 根據ID查詢
@Select("select * from mybatis.emp where id = #{id}")
public Emp idSelect(Integer id);
}
|
測試
1
2
3
4
5
6
7
8
9
10
11
12
| @SpringBootTest
class Mybatis02ApplicationTests {
@Autowired
private EmpMapper empMapper;
@Test
public void testIdSelect(){
Emp emp = empMapper.idSelect(19);
System.out.println(emp);
}
}
|
結果
1
| Emp(id=19, username=Tom2, password=123456, name=湯姆2, gender=1, image=tom.jpg, job=1, entrydate=2000-01-01, deptId=null, createTime=null, updateTime=null)
|
這樣查詢出來的結果,因為類別 Emp 與資料庫的部分欄位名稱不同,導致結果為 null
有三種方法可以解決
使用別名
1
2
3
4
5
6
7
| @Mapper
public interface EmpMapper {
// 方法一:別名
@Select("select id, username, password, name, gender, image, job, entrydate, dept_id deptId, create_time createTime, update_time updateTime" +
" from mybatis.emp where id = #{id}")
public Emp idSelect(Integer id);
}
|
使用 @Results 註解
1
2
3
4
5
6
7
8
9
10
11
| @Mapper
public interface EmpMapper {
// 方法二:@Results @Result 註解
@Results({
@Result(column = "dept_id", property = "deptId"),
@Result(column = "create_time", property = "createTime"),
@Result(column = "update_time", property = "updateTime")
})
@Select("select * from mybatis.emp where id = #{id}")
public Emp idSelect(Integer id);
}
|
使用自動封裝
如果欄位名稱與屬性名稱符合駝峰式命名規則,可以開啟駝峰式命名自動封裝。MyBatis 會自動透過駝峰式命名規則映射,具體為資料庫使用底線 a_column 映射到 Java 屬性 aColumn
開啟駝峰式命名需在 application.properties 加入
1
| mybatis.configuration.map-underscore-to-camel-case=true
|
然後直接使用最初的程式碼
1
2
3
4
5
6
| @Mapper
public interface EmpMapper {
// 根據ID查詢
@Select("select * from mybatis.emp where id = #{id}")
public Emp idSelect(Integer id);
}
|
為了方便使用,可以在 IDEA 中安裝 MyBatisX 外掛
根據條件查詢
需求:根據員工姓名 (模糊比對)、性別 (精準比對)、到職時間 (範圍) 搜尋符合條件的員工資訊
1
2
3
4
5
6
7
8
| @Mapper
public interface EmpMapper {
// 根據條件查詢
// 注意此處 name like '%${name}%' 使用的是 ${},可以在 '' 內使用
@Select("select * from mybatis.emp where name like '%${name}%' and gender = #{gender} and " +
"entrydate between #{begin} and #{end} order by update_time desc")
public List<Emp> conditionSelect(String name, short gender, LocalDate begin, LocalDate end);
}
|
因為使用了插值進行字串拼接,所以此方法不安全
1
2
3
4
5
6
7
| @Mapper
public interface EmpMapper {
// 更安全的條件查詢
@Select("select * from mybatis.emp where name like concat('%',#{name},'%') and gender = #{gender} and " +
"entrydate between #{begin} and #{end} order by update_time desc")
public List<Emp> conditionSelect(String name, short gender, LocalDate begin, LocalDate end);
}
|
使用了 concat 函式拼接字串。測試
1
2
3
4
5
6
7
8
9
10
11
12
| @SpringBootTest
class Mybatis02ApplicationTests {
@Autowired
private EmpMapper empMapper;
@Test
public void testConditionSelect(){
List<Emp> empList = empMapper.conditionSelect("張", (short) 1, LocalDate.of(2010, 1, 1), LocalDate.of(2020, 1, 1));
System.out.println(empList);
}
}
|
參數名稱說明
在 SQL 中使用的 #{} 變數名稱在 SpringBoot 2.x 以後的版本會被自動辨識,但在 1.x 版本或單獨使用 MyBatis 時,方法定義的變數名稱需要使用註解指定才能被辨識
1
2
3
4
5
6
7
8
9
10
11
| @Mapper
public interface EmpMapper {
// 更安全的條件查詢
@Select("select * from mybatis.emp where name like concat('%',#{name},'%') and gender = #{gender} and " +
"entrydate between #{begin} and #{end} order by update_time desc")
public List<Emp> conditionSelect(String name, short gender, LocalDate begin, LocalDate end);
}
// 上述程式碼在 1.x 或單獨使用 MyBatis 時不會生效,函式要添加註解
public List<Emp> conditionSelect(@Param("name") String name, @Param("gender") short gender, @Param("begin") LocalDate begin, @Param("end") LocalDate end);
|
使用 XML 映射文件
如果 SQL 程式碼比較簡短,使用註解很方便,但若 SQL 較長或複雜則會顯得稍微凌亂,為此可以使用 XML 映射文件。以下為規範:
- XML 映射文件的名稱與 Mapper 介面名稱一致,且將 XML 映射文件和 Mapper 介面放在相同套件 (Package) 下 (同套件同名)
- XML 中的 namespace 屬性與 Mapper 介面全限定名一致
- XML 中 SQL 語句的 id 屬性與 Mapper 介面中的方法名稱一致,且回傳型別一致
XML
在 Maven 專案中,非 Java 檔案放在 src/main/resources 下,在此目錄下建立與 Mapper 介面一致的目錄
如介面為 net.yexca.mapper.EmpMapper.java,則 XML 檔案在 net.yexca.mapper.EmpMapper.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| <?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!-- 上方標頭檔請至官方網站尋找 -->
<!-- namespace 屬性為介面 -->
<mapper namespace="net.yexca.mapper.EmpMapper">
<!-- id 屬性為方法名稱 -->
<!-- resultType 為單筆紀錄所封裝的型別 -->
<select id="conditionSelect" resultType="net.yexca.pojo.Emp">
select * from mybatis.emp where name like concat('%',#{name},'%') and gender = #{gender} and
entrydate between #{begin} and #{end} order by update_time desc
</select>
</mapper>
|
介面類別
1
2
3
4
| @Mapper
public interface EmpMapper {
public List<Emp> conditionSelect(String name, short gender, LocalDate begin, LocalDate end);
}
|
動態 SQL
動態 SQL 是指隨著使用者的輸入或外部條件的變化而變化的 SQL 語句
例如上述最後一個條件查詢,必須輸入全部三個條件才能查詢,但實際使用時或許只想指定其中一兩個或不指定 (查詢全部),如果按照上述指令執行將回傳空結果
where if
用於判斷條件是否成立,使用 test 屬性進行條件判斷,如果條件為 true,則拼接 SQL
例如,當姓名不為空時,拼接條件透過姓名查詢
1
2
3
| <if test="name!=null">
name like contact('%',#{name},'%')
</if>
|
而 where 標籤則管理是否生成 where 關鍵字,並自動去除多餘的 and 和 or 關鍵字
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| <select id="conditionSelect" resultType="net.yexca.pojo.Emp">
select * from mybatis.emp
<where>
<if test="name!=null">
name like concat('%',#{name},'%')
</if>
<if test="gender!=null">
and gender = #{gender}
</if>
<if test="begin!=null and end!=null">
and entrydate between #{begin} and #{end}
</if>
</where>
order by update_time desc
</select>
|
set
替換 SQL 中的 set 關鍵字,並自動去除多餘的逗號,用於 UPDATE 語句中
例如將以下 SQL 語句修改為動態 SQL
1
2
3
4
5
6
7
8
9
10
| update mybatis.emp
set username = #{username},
name = #{name},
gender = #{gender},
image = #{image},
job = #{job},
entrydate = #{entrydate},
dept_id = #{deptId},
update_time = #{updateTime}
where id = #{id};
|
XML
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| <update id="update2">
update mybatis.emp
<set>
<if test="username != null">username = #{username},</if>
<if test="name != null">name = #{name},</if>
<if test="gender != null">gender = #{gender},</if>
<if test="image != null">image = #{image},</if>
<if test="job != null">job = #{job},</if>
<if test="entrydate != null">entrydate = #{entrydate},</if>
<if test="deptId != null">dept_id = #{deptId},</if>
<if test="updateTime != null">update_time = #{updateTime}</if>
</set>
where id = #{id};
</update>
|
foreach
foreach 標籤用於遍歷
1
2
3
4
| <!--遍歷的集合 遍歷出的元素 分隔符號 開始前後拼接的 SQL 片段-->
<foreach collection="ids" item="id" separator="," open="(" close=")">
#{id}
</foreach>
|
假如 ids 為 [13, 14, 15],以上將產生 (13,14,15)
需求:遍歷刪除 id 為 1, 2, 3。SQL 語句為:
1
| delete from emp where id in(1,2,3)
|
介面方法
1
| public void deleteByIds(List<Integer> ids);
|
XML
1
2
3
4
5
6
| <delete id="deleteByIds">
delete from mybatis.emp where id in
<foreach collection="ids" item="id" separator="," open="(" close=")">
#{id}
</foreach>
</delete>
|
測試
1
2
3
4
5
| @Test
public void testDeleteByIds(){
List<Integer> ids = Arrays.asList(13,14,15);
empMapper.deleteByIds(ids);
}
|
sql 與 include
在開發過程中可能會出現大量重複的 SQL 語句,可以用 sql 標籤定義可重用的 SQL 片段,再透過 include 標籤重用
1
2
3
4
5
6
7
| <!-- 透過 id 屬性唯一識別語句 -->
<sql id = "commonCode">
<!-- SQL 語句 -->
</sql>
<!-- 透過 refid 屬性指定引用的語句 -->
<include refid = "commonCode" />
|