typora-copy-images-to: img
一.接口代理方式實作Dao
1.1 代理開發方式介紹
采用 Mybatis 的代理開發方式實作 DAO 層的開發,這種方式是我們後面進入企業的主流。
Mapper 接口開發方法隻需要程式員編寫Mapper 接口(相當于Dao 接口),由Mybatis 架構根據接口定義建立接口的動态代理對象,代理對象的方法體同上邊Dao接口實作類方法。
Mapper 接口開發需要遵循以下規範:
1) Mapper.xml檔案中的namespace與mapper接口的全限定名相同
2) Mapper接口方法名和Mapper.xml中定義的每個statement的id相同
3) Mapper接口方法的輸入參數類型和mapper.xml中定義的每個sql的parameterType的類型相同
4) Mapper接口方法的輸出參數類型和mapper.xml中定義的每個sql的resultType的類型相同
總結:
接口開發的方式: 程式員隻需定義接口,就可以對資料庫進行操作,那麼具體的對象怎麼建立?
1.程式員負責定義接口
2.在操作資料庫,mybatis架構根據接口,通過動态代理的方式生成代理對象,負責資料庫的crud操作
1.2.編寫StudentMapper接口
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsISPrdEZwZ1Rh5WNXp1bwNjW1ZUba9VZwlHdsATOfd3bkFGazxCMx8VesATMfhHLlN3XnxCMwEzX0xiRGZkRGZ0Xy9GbvNGLpZTY1EmMZVDUSFTU4VFRR9Fd4VGdsYTMfVmepNHLrJXYtJXZ0F2dvwVZnFWbp1zczV2YvJHctM3cv1Ce-cmbw5SN0IDMxcTYlNWYhRmZ0UjNxYzXwMTNwcTMyEzLcBTMxIDMy8CXn9Gbi9CXzV2Zh1WavwVbvNmLvR3YxUjL0M3Lc9CX6MHc0RHaiojIsJye.png)
1.3 測試代理方式
public Student selectById(Integer id) {
Student stu = null;
SqlSession sqlSession = null;
InputStream is = null;
try{
//1.加載核心配置檔案
is = Resources.getResourceAsStream("MyBatisConfig.xml");
//2.擷取SqlSession工廠對象
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
//3.通過工廠對象擷取SqlSession對象
sqlSession = sqlSessionFactory.openSession(true);
//4.擷取StudentMapper接口的實作類對象
StudentMapper mapper = sqlSession.getMapper(StudentMapper.class); // StudentMapper mapper = new StudentMapperImpl();
//5.通過實作類對象調用方法,接收結果
stu = mapper.selectById(id);
} catch (Exception e) {
} finally {
//6.釋放資源
if(sqlSession != null) {
sqlSession.close();
}
if(is != null) {
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
//7.傳回結果
return stu;
}
1.4 源碼分析
-
分析動态代理對象如何生成的?
通過動态代理開發模式,我們隻編寫一個接口,不寫實作類,我們通過 getMapper() 方法最終擷取到 org.apache.ibatis.binding.MapperProxy 代理對象,然後執行功能,而這個代理對象正是 MyBatis 使用了 JDK 的動态代理技術,幫助我們生成了代理實作類對象。進而可以進行相關持久化操作。
-
分析方法是如何執行的?
動态代理實作類對象在執行方法的時候最終調用了 mapperMethod.execute() 方法,這個方法中通過 switch 語句根據操作類型來判斷是新增、修改、删除、查詢操作,最後一步回到了 MyBatis 最原生的 SqlSession 方式來執行增删改查。
1.5 知識小結
接口代理方式可以讓我們隻編寫接口即可,而實作類對象由 MyBatis 生成。
實作規則 :
- 映射配置檔案中的名稱空間必須和 Dao 層接口的全類名相同。
- 映射配置檔案中的增删改查标簽的 id 屬性必須和 Dao 層接口的方法名相同。
- 映射配置檔案中的增删改查标簽的 parameterType 屬性必須和 Dao 層接口方法的參數相同。
- 映射配置檔案中的增删改查标簽的 resultType 屬性必須和 Dao 層接口方法的傳回值相同。
- 擷取動态代理對象 SqlSession 功能類中的 getMapper() 方法。
二. 動态sql語句
2.1 動态sql語句概述
Mybatis 的映射檔案中,前面我們的 SQL 都是比較簡單的,有些時候業務邏輯複雜時,我們的 SQL是動态變化的,此時在前面的學習中我們的 SQL 就不能滿足要求了。
參考的官方文檔,描述如下:
2.2 動态 SQL 之<if>
我們根據實體類的不同取值,使用不同的 SQL語句來進行查詢。比如在 id如果不為空時可以根據id查詢,如果username 不同空時還要加入使用者名作為條件。這種情況在我們的多條件組合查詢中經常會碰到。
如下圖:
<select id="findByCondition" parameterType="student" resultType="student">
select * from student
<where>
<if test="id!=0">
and id=#{id}
</if>
<if test="username!=null">
and username=#{username}
</if>
</where>
</select>
當查詢條件id和username都存在時,控制台列印的sql語句如下:
… … …
//獲得MyBatis架構生成的StudentMapper接口的實作類
StudentMapper mapper = sqlSession.getMapper( StudentMapper.class);
Student condition = new Student();
condition.setId(1);
condition.setUsername("lucy");
Student student = mapper.findByCondition(condition);
… … …
當查詢條件隻有id存在時,控制台列印的sql語句如下:
… … …
//獲得MyBatis架構生成的UserMapper接口的實作類
StudentMapper mapper = sqlSession.getMapper( StudentMapper.class);
Student condition = new Student();
condition.setId(1);
Student student = mapper.findByCondition(condition);
… … …
總結文法:
<where>:條件标簽。如果有動态條件,則使用該标簽代替 where 關鍵字。
<if>:條件判斷标簽。
<if test=“條件判斷”>
查詢條件拼接
</if>
2.3 動态 SQL 之<foreach>
循環執行sql的拼接操作,例如:SELECT * FROM student WHERE id IN (1,2,5)。
<select id="findByIds" parameterType="list" resultType="student">
select * from student
<where>
<foreach collection="array" open="id in(" close=")" item="id" separator=",">
#{id}
</foreach>
</where>
</select>
測試代碼片段如下:
… … …
//獲得MyBatis架構生成的UserMapper接口的實作類
StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
int[] ids = new int[]{2,5};
List<Student> sList = mapper.findByIds(ids);
System.out.println(sList);
… … …
<foreach>:循環周遊标簽。适用于多個參數或者的關系。
<foreach collection=“”open=“”close=“”item=“”separator=“”>
擷取參數
</foreach>
屬性
collection:參數容器類型, (list-集合, array-數組)。
open:開始的 SQL 語句。
close:結束的 SQL 語句。
item:參數變量名。
separator:分隔符。
2.4 SQL片段抽取
Sql 中可将重複的 sql 提取出來,使用時用 include 引用即可,最終達到 sql 重用的目的
<!--抽取sql片段簡化編寫-->
<sql id="selectStudent" select * from student</sql>
<select id="findById" parameterType="int" resultType="student">
<include refid="selectStudent"></include> where id=#{id}
</select>
<select id="findByIds" parameterType="list" resultType="student">
<include refid="selectStudent"></include>
<where>
<foreach collection="array" open="id in(" close=")" item="id" separator=",">
#{id}
</foreach>
</where>
</select>
我們可以将一些重複性的 SQL 語句進行抽取,以達到複用的效果。
- <sql>:抽取 SQL 語句标簽。
- <include>:引入 SQL 片段标簽。
<sql id=“片段唯一辨別”>抽取的 SQL 語句</sql> <include refid=“片段唯一辨別”/>
2.5 知識小結
MyBatis映射檔案配置:
<select>:查詢
<insert>:插入
<update>:修改
<delete>:删除
<where>:where條件
<if>:if判斷
<foreach>:循環
<sql>:sql片段抽取
三. 分頁插件
3.1 分頁插件介紹
- 分頁可以将很多條結果進行分頁顯示。
- 如果目前在第一頁,則沒有上一頁。如果目前在最後一頁,則沒有下一頁。
- 需要明确目前是第幾頁,這一頁中顯示多少條結果。
- MyBatis分頁插件總結
- 在企業級開發中,分頁也是一種常見的技術。而目前使用的 MyBatis 是不帶分頁功能的,如果想實作分頁的 功能,需要我們手動編寫 LIMIT 語句。但是不同的資料庫實作分頁的 SQL 語句也是不同的,是以手寫分頁 成本較高。這個時候就可以借助分頁插件來幫助我們實作分頁功能。
- PageHelper:第三方分頁助手。将複雜的分頁操作進行封裝,進而讓分頁功能變得非常簡單。
3.2 分頁插件的使用
MyBatis可以使用第三方的插件來對功能進行擴充,分頁助手PageHelper是将分頁的複雜操作進行封裝,使用簡單的方式即可獲得分頁的相關資料
開發步驟:
①導入與PageHelper的jar包
②在mybatis核心配置檔案中配置PageHelper插件
<!-- 注意:分頁助手的插件 配置在通用mapper之前 -->
<plugin interceptor="com.github.pagehelper.PageHelper">
<!-- 指定方言 -->
<property name="dialect" value="mysql"/>
</plugin>
③測試分頁資料擷取
@Test
public void testPageHelper(){
//設定分頁參數
PageHelper.startPage(1,2);
List<User> select = userMapper2.select(null);
for(User user : select){
System.out.println(user);
}
}
3.3 分頁插件的參數擷取
獲得分頁相關的其他參數:
//其他分頁的資料
PageInfo<User> pageInfo = new PageInfo<User>(select);
System.out.println("總條數:"+pageInfo.getTotal());
System.out.println("總頁數:"+pageInfo.getPages());
System.out.println("目前頁:"+pageInfo.getPageNum());
System.out.println("每頁顯示長度:"+pageInfo.getPageSize());
System.out.println("是否第一頁:"+pageInfo.isIsFirstPage());
System.out.println("是否最後一頁:"+pageInfo.isIsLastPage());
3.4 分頁插件知識小結
分頁:可以将很多條結果進行分頁顯示。
- 分頁插件 jar 包: pagehelper-5.1.10.jar jsqlparser-3.1.jar
- :內建插件标簽。
-
分頁助手相關 API
1.PageHelper:分頁助手功能類。
- startPage():設定分頁參數
- PageInfo:分頁相關參數功能類。
- getTotal():擷取總條數
- getPages():擷取總頁數
- getPageNum():擷取目前頁
- getPageSize():擷取每頁顯示條數
- getPrePage():擷取上一頁
- getNextPage():擷取下一頁
- isIsFirstPage():擷取是否是第一頁
- isIsLastPage():擷取是否是最後一頁
四.MyBatis的多表操作
4.1 多表模型介紹
我們之前學習的都是基于單表操作的,而實際開發中,随着業務難度的加深,肯定需要多表操作的。
- 多表模型分類 一對一:在任意一方建立外鍵,關聯對方的主鍵。
- 一對多:在多的一方建立外鍵,關聯一的一方的主鍵。
- 多對多:借助中間表,中間表至少兩個字段,分别關聯兩張表的主鍵。
4.2 多表模型一對一操作
- 一對一模型: 人和身份證,一個人隻有一個身份證
- 代碼實作
- 步驟一: sql語句準備
CREATE TABLE person( id INT PRIMARY KEY AUTO_INCREMENT, NAME VARCHAR(20), age INT ); INSERT INTO person VALUES (NULL,'張三',23); INSERT INTO person VALUES (NULL,'李四',24); INSERT INTO person VALUES (NULL,'王五',25); CREATE TABLE card( id INT PRIMARY KEY AUTO_INCREMENT, number VARCHAR(30), pid INT, CONSTRAINT cp_fk FOREIGN KEY (pid) REFERENCES person(id) ); INSERT INTO card VALUES (NULL,'12345',1); INSERT INTO card VALUES (NULL,'23456',2); INSERT INTO card VALUES (NULL,'34567',3);
- 步驟二:配置檔案
<?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.itheima.table01.OneToOneMapper"> <!--配置字段和實體對象屬性的映射關系--> <resultMap id="oneToOne" type="card"> <id column="cid" property="id" /> <result column="number" property="number" /> <!-- association:配置被包含對象的映射關系 property:被包含對象的變量名 javaType:被包含對象的資料類型 --> <association property="p" javaType="person"> <id column="pid" property="id" /> <result column="name" property="name" /> <result column="age" property="age" /> </association> </resultMap> <select id="selectAll" resultMap="oneToOne"> SELECT c.id cid,number,pid,NAME,age FROM card c,person p WHERE c.pid=p.id </select> </mapper>
- 步驟三:測試類
@Test public void selectAll() throws Exception{ //1.加載核心配置檔案 InputStream is = Resources.getResourceAsStream("MyBatisConfig.xml"); //2.擷取SqlSession工廠對象 SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is); //3.通過工廠對象擷取SqlSession對象 SqlSession sqlSession = sqlSessionFactory.openSession(true); //4.擷取OneToOneMapper接口的實作類對象 OneToOneMapper mapper = sqlSession.getMapper(OneToOneMapper.class); //5.調用實作類的方法,接收結果 List<Card> list = mapper.selectAll(); //6.處理結果 for (Card c : list) { System.out.println(c); } //7.釋放資源 sqlSession.close(); is.close(); }
<resultMap>:配置字段和對象屬性的映射關系标簽。 id 屬性:唯一辨別 type 屬性:實體對象類型 <id>:配置主鍵映射關系标簽。 <result>:配置非主鍵映射關系标簽。 column 屬性:表中字段名稱 property 屬性: 實體對象變量名稱 <association>:配置被包含對象的映射關系标簽。 property 屬性:被包含對象的變量名 javaType 屬性:被包含對象的資料類型
- 步驟一: sql語句準備
4.3 多表模型一對多操作
- 一對多模型: 一對多模型:班級和學生,一個班級可以有多個學生。
-
-
CREATE TABLE classes( id INT PRIMARY KEY AUTO_INCREMENT, NAME VARCHAR(20) ); INSERT INTO classes VALUES (NULL,'黑馬一班'); INSERT INTO classes VALUES (NULL,'黑馬二班'); CREATE TABLE student( id INT PRIMARY KEY AUTO_INCREMENT, NAME VARCHAR(30), age INT, cid INT, CONSTRAINT cs_fk FOREIGN KEY (cid) REFERENCES classes(id) ); INSERT INTO student VALUES (NULL,'張三',23,1); INSERT INTO student VALUES (NULL,'李四',24,1); INSERT INTO student VALUES (NULL,'王五',25,2); INSERT INTO student VALUES (NULL,'趙六',26,2);
-
<mapper namespace="com.itheima.table02.OneToManyMapper"> <resultMap id="oneToMany" type="classes"> <id column="cid" property="id"/> <result column="cname" property="name"/> <!-- collection:配置被包含的集合對象映射關系 property:被包含對象的變量名 ofType:被包含對象的實際資料類型 --> <collection property="students" ofType="student"> <id column="sid" property="id"/> <result column="sname" property="name"/> <result column="sage" property="age"/> </collection> </resultMap> <select id="selectAll" resultMap="oneToMany"> SELECT c.id cid,c.name cname,s.id sid,s.name sname,s.age sage FROM classes c,student s WHERE c.id=s.cid </select> </mapper>
-
@Test public void selectAll() throws Exception{ //1.加載核心配置檔案 InputStream is = Resources.getResourceAsStream("MyBatisConfig.xml"); //2.擷取SqlSession工廠對象 SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is); //3.通過工廠對象擷取SqlSession對象 SqlSession sqlSession = sqlSessionFactory.openSession(true); //4.擷取OneToManyMapper接口的實作類對象 OneToManyMapper mapper = sqlSession.getMapper(OneToManyMapper.class); //5.調用實作類的方法,接收結果 List<Classes> classes = mapper.selectAll(); //6.處理結果 for (Classes cls : classes) { System.out.println(cls.getId() + "," + cls.getName()); List<Student> students = cls.getStudents(); for (Student student : students) { System.out.println("\t" + student); } } //7.釋放資源 sqlSession.close(); is.close(); }
<resultMap>:配置字段和對象屬性的映射關系标簽。 id 屬性:唯一辨別 type 屬性:實體對象類型 <id>:配置主鍵映射關系标簽。 <result>:配置非主鍵映射關系标簽。 column 屬性:表中字段名稱 property 屬性: 實體對象變量名稱 <collection>:配置被包含集合對象的映射關系标簽。 property 屬性:被包含集合對象的變量名 ofType 屬性:集合中儲存的對象資料類型
-
4.4 多表模型多對多操作
- 多對多模型:學生和課程,一個學生可以選擇多門課程、一個課程也可以被多個學生所選擇。
-
-
CREATE TABLE course( id INT PRIMARY KEY AUTO_INCREMENT, NAME VARCHAR(20) ); INSERT INTO course VALUES (NULL,'國文'); INSERT INTO course VALUES (NULL,'數學'); CREATE TABLE stu_cr( id INT PRIMARY KEY AUTO_INCREMENT, sid INT, cid INT, CONSTRAINT sc_fk1 FOREIGN KEY (sid) REFERENCES student(id), CONSTRAINT sc_fk2 FOREIGN KEY (cid) REFERENCES course(id) ); INSERT INTO stu_cr VALUES (NULL,1,1); INSERT INTO stu_cr VALUES (NULL,1,2); INSERT INTO stu_cr VALUES (NULL,2,1); INSERT INTO stu_cr VALUES (NULL,2,2);
-
<?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.itheima.table03.ManyToManyMapper"> <resultMap id="manyToMany" type="student"> <id column="sid" property="id"/> <result column="sname" property="name"/> <result column="sage" property="age"/> <collection property="courses" ofType="course"> <id column="cid" property="id"/> <result column="cname" property="name"/> </collection> </resultMap> <select id="selectAll" resultMap="manyToMany"> SELECT sc.sid,s.name sname,s.age sage,sc.cid,c.name cname FROM student s,course c,stu_cr sc WHERE sc.sid=s.id AND sc.cid=c.id </select> </mapper>
-
@Test public void selectAll() throws Exception{ //1.加載核心配置檔案 InputStream is = Resources.getResourceAsStream("MyBatisConfig.xml"); //2.擷取SqlSession工廠對象 SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is); //3.通過工廠對象擷取SqlSession對象 SqlSession sqlSession = sqlSessionFactory.openSession(true); //4.擷取ManyToManyMapper接口的實作類對象 ManyToManyMapper mapper = sqlSession.getMapper(ManyToManyMapper.class); //5.調用實作類的方法,接收結果 List<Student> students = mapper.selectAll(); //6.處理結果 for (Student student : students) { System.out.println(student.getId() + "," + student.getName() + "," + student.getAge()); List<Course> courses = student.getCourses(); for (Course cours : courses) { System.out.println("\t" + cours); } } //7.釋放資源 sqlSession.close(); is.close(); }
<resultMap>:配置字段和對象屬性的映射關系标簽。 id 屬性:唯一辨別 type 屬性:實體對象類型 <id>:配置主鍵映射關系标簽。 <result>:配置非主鍵映射關系标簽。 column 屬性:表中字段名稱 property 屬性: 實體對象變量名稱 <collection>:配置被包含集合對象的映射關系标簽。 property 屬性:被包含集合對象的變量名 ofType 屬性:集合中儲存的對象資料類型
-
4.5 多表模型操作總結
<resultMap>:配置字段和對象屬性的映射關系标簽。
id 屬性:唯一辨別
type 屬性:實體對象類型
<id>:配置主鍵映射關系标簽。
<result>:配置非主鍵映射關系标簽。
column 屬性:表中字段名稱
property 屬性: 實體對象變量名稱
<association>:配置被包含對象的映射關系标簽。
property 屬性:被包含對象的變量名
javaType 屬性:被包含對象的資料類型
<collection>:配置被包含集合對象的映射關系标簽。
property 屬性:被包含集合對象的變量名
ofType 屬性:集合中儲存的對象資料類型