文章目錄
MyBatis進階
1、MyBatis回顧
MyBatis就是用來幫我們簡化數據庫操作的。
創建第一個MyBatis程序步驟
- 創建數據庫和表
- 創建一個簡單Maven項目,添加Javaweb框架
- 導入MyBatis、mysql、junit、lombok依賴
- 編輯核心配置文件Mybatis.config.xml
- 編寫MyBatisUtil工具類
- 創建實體類,一個實體類和一張數據庫中的表對應
- 創建操作實體類的對應接口
- 編寫接口對應的xml配置文件
- 將xml配置文件註冊到Mybatis.config.xml編輯核心配置文件中
- 編寫測試類,使用junit測試工具進行測試
新增業務邏輯步驟
- 數據庫建表
- 編寫實體類
- 編寫接口
- 編寫接口對應xml配置文件
- 將xml配置文件註冊到MyBatis核心依賴文件Mybatis.config.xml中
- 測試
改進
-
問題:在工具類中,我們所有的增刪改操作都要手動去提交事務,很麻煩
解決方案:在工具類中重載getSession()方法,添加參數flag
// 原有的獲取sqlSession連接方法
public static SqlSession getSession() {
return sqlSessionFactory.openSession();
}
// 新增重載獲取sqlSession連接方法,當參數爲true時,默認開啓事務提交
public static SqlSession getSession(boolean flag) {
return sqlSessionFactory.openSession(flag);
}
2、使用註解開發
注意:雖然推薦使用註解進行簡化開發,但是MyBatis是一個例外,MyBatis推薦使用對應的xml文檔進行配置
1、面向接口編程思想
本質就是爲了解耦
- 增加系統可擴展性
- 接口中方法複用
- 利於管理
- 利於分層開發
- dao層、接口對應Impl實現類(業務邏輯)
- service、servlet等(控制層)
- html、css等(視圖層)
2、使用註解編程的步驟
-
在接口方法上使用註解
與xml文檔配置一樣,每一個xml文檔標籤都有一個對應的註解
在註解後的括號內些對應的SQL語句,寫了註解就不用再寫接口對應的xml文檔了
//例如 //根據ID獲得信息 @Select("select * from user where id = #{id}") User getUserById(int id);
-
綁定到MyBatis核心配置文件mybatis-config.xml中
<!--與使用xml文檔進行配置一樣--> <mapper class="org.westos.mapper.UserMapper2"/>
-
進行測試
對於簡單的CRUD(增刪改查)來說,使用註解會很方便,但是對於複雜的查詢,還是使用xml文檔配置比較好
xml配置和註解可以同時使用嗎?有哪些條件
-
註解和接口可以同時使用,但是對於複雜的代碼來說,使用xml配置更簡潔明瞭
-
接口要註冊在MyBatis核心配置文件mybatis-config.xml中,使用xml文檔配置還要在文檔中配置對應的接口
-
如果接口和接口.xml在同一個包下,只需要註冊接口就好了,優先使用class屬性進行配置
注意
- 註解的SQL語句和xml配置的SQl語句是相同的
- 超過多個參數必須要使用@param註解,或者使用Map集合進行參數傳遞(鍵值對)
- 對於可能存在的SQL注入問題
-
#{}
PrepareStatement,沒有SQL注入,建議使用
-
${}
statement,存在SQL注入,不建議使用
-
3、CRUD註解練習
使用註解完成以下練習
1. 通過id查詢用戶
2. 更新用戶信息
3. 添加用戶
4. 根據id刪除用戶
5. 最後使用junit測試
規範:增刪改要自動提交事務
//實體類User,對應數據庫中的表user
package org.westos.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
private int id;
private String name;
private String pwd;
}
//接口
package org.westos.mapper;
import org.apache.ibatis.annotations.*;
import org.westos.pojo.User;
public interface UserMapper2 {
//根據ID獲得信息
@Select("select * from user where id = #{id}")
User getUserById(int id);
//更新用戶
@Update("update user set name=#{name},pwd=#{pwd} where id=#{id}")
int updateUser(@Param("id") int id, @Param("name") String name, @Param("pwd") String pwd);
//插入用戶
@Insert("insert into user (id,name,pwd) values (#{id},#{name},#{pwd})")
int insertUser(User user);
//刪除用戶
@Delete("delete from user where id=#{id}")
int deleteUser(int id);
}
//使用junit測試工具進行測試
package org.westos.mapper;
import org.junit.Test;
import org.westos.pojo.User;
import org.westos.utils.MyBatisUtil;
public class UserMapper2Test {
//測試根據ID獲得信息
@Test
public void testGetUserById() {
UserMapper2 mapper = MyBatisUtil.getSession().getMapper(UserMapper2.class);
User userById = mapper.getUserById(1);
System.out.println(userById);
}
//更新用戶
//增刪改都要設置自動提交事務,在工具類中設置getSession()方法重載,添加布爾類型參數
@Test
public void testUpdateUser() {
UserMapper2 mapper = MyBatisUtil.getSession(true).getMapper(UserMapper2.class);
int i = mapper.updateUser(1, "張三", "666666");
System.out.println(i);
}
//插入新用戶
@Test
public void testInsertUser() {
UserMapper2 mapper = MyBatisUtil.getSession(true).getMapper(UserMapper2.class);
int i = mapper.insertUser(new User(2,"李四","111111"));
System.out.println(i);
}
//刪除用戶
@Test
public void testDeleteUser() {
UserMapper2 mapper = MyBatisUtil.getSession(true).getMapper(UserMapper2.class);
int i = mapper.deleteUser(4);
System.out.println(i);
}
}
mybatis-config.xml配置文檔中進行配置
<mappers>
<mapper class="org.westos.mapper.UserMapper2"/>
</mappers>
3、一對多(collection)
在一個學校裏有老師也有學生,一個老師可以有多個學生,所以針對某一門課,老師和學生是一對多的關係
一對多從一的一方的角度出發,少的找多的(老師找學生),是包含的關係(一個老師下包含多個學生,所以老師類要有一個students集合,存放包含的學生)
-
設計數據庫表
老師有兩個屬性,id和name,而學生除了id和name之外,還有另一個屬性tid(對應教師的id)
使用sql語句,創建表teacher和student
CREATE TABLE `teacher` ( `id` INT(10) NOT NULL, `name` VARCHAR(30) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=INNODB DEFAULT CHARSET=utf8 INSERT INTO teacher(`id`, `name`) VALUES (1, '秦老師'); CREATE TABLE `student` ( `id` INT(10) NOT NULL, `name` VARCHAR(30) DEFAULT NULL, `tid` INT(10) DEFAULT NULL, PRIMARY KEY (`id`), KEY `fktid` (`tid`), CONSTRAINT `fktid` FOREIGN KEY (`tid`) REFERENCES `teacher` (`id`) ) ENGINE=INNODB DEFAULT CHARSET=utf8 INSERT INTO `student` (`id`, `name`, `tid`) VALUES ('1', '小明', '1'); INSERT INTO `student` (`id`, `name`, `tid`) VALUES ('2', '小紅', '1'); INSERT INTO `student` (`id`, `name`, `tid`) VALUES ('3', '小張', '1'); INSERT INTO `student` (`id`, `name`, `tid`) VALUES ('4', '小李', '1'); INSERT INTO `student` (`id`, `name`, `tid`) VALUES ('5', '小王', '1');
-
設計實體類
實體類Teacher中,除了id和name屬性之外,還應該有一個學生集合List<Student>,用來存儲該老師對應的學生
實體類Student中,除了id和name屬性之外,還應該有對應教師的id
使用lombok創建實體類
//Teacher類 package org.westos.pojo; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; import java.util.List; @Data @NoArgsConstructor @AllArgsConstructor public class Teacher { private int id; private String name; //存儲名下的學生集合 List<Student> students; } //Student類 package org.westos.pojo; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; @Data @NoArgsConstructor @AllArgsConstructor public class Student { private int id; private String name; //對應的教師id private int tid; }
-
編寫接口
我們現在想讀取id=1的教師的id和name,並將該教師名下的所有學生集合students輸出出來
編寫接口TeacherMapper
package org.westos.mapper; import org.westos.pojo.Teacher; public interface TeacherMapper { //根據老師的ID得到老師信息和對應學生的信息 public Teacher getTeacherById(int id); }
-
編寫接口對應的xml配置文檔
先獲取Teacher的id和name屬性,至於students集合,我們沒辦法從teacher一張用戶表中查出,而是要使用student和teacher表聯合查詢。
這個時候就要使用collection標籤,將student表中的信息映射出來
<?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="org.westos.mapper.TeacherMapper"> <!--因爲teacher對象的students屬性沒辦法直接從teacher表中查詢出來,所以要使用連表查詢--> <!--凡是我們無法直接從數據庫表中得到的數據,都要做resultMap集合映射--> <select id="getTeacherById" resultMap="TeacherStudent"> SELECT t.id tid,t.name tname,s.`id` sid,s.`name` sname ,s.`tid` tid FROM student s,teacher t WHERE t.`id`=1 </select> <resultMap id="TeacherStudent" type="org.westos.pojo.Teacher"> <result property="id" column="tid"/> <result property="name" column="tname"/> <!--對於teacher對象的students屬性,我們無法直接從teacher表中得到,所以要做映射--> <!--類型是Student--> <collection property="students" ofType="org.westos.pojo.Student"> <!--對照上面的sql語句,一一進行別名--> <!--左邊是Java類中的屬性名稱,右邊是數據庫的字段名稱--> <result property="id" column="sid"/> <result property="name" column="sname"/> <result property="tid" column="tid"/> </collection> </resultMap> </mapper>
-
配置到MyBatis核心配置文檔
<mapper class="org.westos.mapper.TeacherMapper"/>
-
使用junit測試工具測試
package org.westos.mapper; import org.junit.Test; import org.westos.pojo.Teacher; import org.westos.utils.MyBatisUtil; public class TeacherMapperTest { @Test public void testGetTeacherById(){ //得到mapper對象,因爲是查詢,就沒有開啓事務 TeacherMapper mapper = MyBatisUtil.getSession().getMapper(TeacherMapper.class); Teacher teacherById = mapper.getTeacherById(1); System.out.println(teacherById); } }
小結:
一對多是指從一的一方
(教師)看待,在設計實體類的時候,教師下應該設計一個包含多的一方
(學生)的集合,用來存儲學生的信息。而學生應該設置一個屬性用來存放教師的id,以便一對多的關係映射
4、多對一(association)
在指定的一門課上,全班的學生對應一個老師,學生有多個,而老師只有一個,這就是一對多
多對一從多的一方的角度出發,多的去找少的(學生找老師),是關聯關係(一個學生關聯一個老師,所以student類中要有一個teacher對象屬性,用來關聯對應的老師)
-
設計數據庫表
老師有兩個屬性,id和name,而學生除了id和name之外,還有另一個屬性tid(對應教師的id)
使用sql語句,創建表teacher和student
CREATE TABLE `teacher` ( `id` INT(10) NOT NULL, `name` VARCHAR(30) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=INNODB DEFAULT CHARSET=utf8 INSERT INTO teacher(`id`, `name`) VALUES (1, '秦老師'); CREATE TABLE `student` ( `id` INT(10) NOT NULL, `name` VARCHAR(30) DEFAULT NULL, `tid` INT(10) DEFAULT NULL, PRIMARY KEY (`id`), KEY `fktid` (`tid`), CONSTRAINT `fktid` FOREIGN KEY (`tid`) REFERENCES `teacher` (`id`) ) ENGINE=INNODB DEFAULT CHARSET=utf8 INSERT INTO `student` (`id`, `name`, `tid`) VALUES ('1', '小明', '1'); INSERT INTO `student` (`id`, `name`, `tid`) VALUES ('2', '小紅', '1'); INSERT INTO `student` (`id`, `name`, `tid`) VALUES ('3', '小張', '1'); INSERT INTO `student` (`id`, `name`, `tid`) VALUES ('4', '小李', '1'); INSERT INTO `student` (`id`, `name`, `tid`) VALUES ('5', '小王', '1');
-
設計實體類
對於Student類,每一個Student對象,不僅有id和name屬性,還應該有一個teacher屬性,用來關聯對應的老師的信息(多對一是多的找少的,也就是學生找老師)
對於Teacher類,含有id和name兩個屬性
//Student類 import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; @Data @NoArgsConstructor @AllArgsConstructor public class Student { private int id; private String name; private Teacher teacher; } //Teacher類 import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; @Data @NoArgsConstructor @AllArgsConstructor public class Teacher { private int id; private String name; }
-
編寫接口
獲得所有的學生的信息,包括學生對應的老師的信息
package org.westos.mapper; import org.westos.pojo.Student; import java.util.List; public interface StudentMapper { //獲取所有的學生的及學生對應老師的信息 List<Student> getStudents(); }
-
編寫對應的mapper.xml
在student表中並不能查詢到teacher的信息,所以需要使用連表查詢
<?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="org.westos.mapper.StudentMapper"> <!--先寫好sql語句,運行無誤後粘貼過來--> <!--因爲無法直接拿到teacher的信息,所以需要使用resultMap映射--> <select id="getStudents" resultMap="StudentTeacher"> SELECT s.id sid,s.name sname,t.id tid,t.name tname FROM student s,teacher t WHERE s.`tid`=t.`id` </select> <!--student中的id和name可以直接從student表中獲得--> <resultMap id="StudentTeacher" type="org.westos.pojo.Student"> <id property="id" column="sid"/> <result property="name" column="sname"/> <!--teacher對象無法從student表中獲得,所以要連表查詢--> <association property="teacher" javaType="org.westos.pojo.Teacher"> <!--對應上面的sql語句,左邊爲在Java類中的屬性名,右邊爲表中字段名--> <result property="id" column="tid"/> <result property="name" column="tname"/> </association> </resultMap> </mapper>
-
註冊到MyBatis核心配置文檔
<mapper class="org.westos.mapper.StudentMapper"/>
-
使用junit測試工具進行測試
package org.westos.mapper; import org.junit.Test; import org.westos.pojo.Student; import org.westos.utils.MyBatisUtil; import java.util.List; public class StudentMapperTest { @Test public void testGetStudents() { StudentMapper mapper = MyBatisUtil.getSession().getMapper(StudentMapper.class); //得到student集合 List<Student> students = mapper.getStudents(); //遍歷打印 for (Student student : students) { System.out.println(student); } } }
5、resultMap小結
結果集映射
- 所有無法直接返回結果的(引用類型),都需要做結果集映射
- 當實體類的屬性名與表中的字段名不一致時,要使用結果集映射
- resultMap標籤中的標籤及屬性含義
- id,對應表的主鍵
- result,對應表中的普通字段
- property,實體類中的屬性名
- column,表中的字段名
- association,多對一情況使用,用來關聯其他對象
- collection,一對多情況使用,用來包含其他對象
- ofType,一對多情況使用,表示包含的對象的類型