1.MyBatis概述
- MyBatis是一個優秀的持久層框架,它對jdbc的操作數據庫的過程進行封裝,使開發者只需要關注 SQL 本身,而不需要花費精力去處理例如注冊驅動、創建connection、創建statement、手動設置參數、結果集檢索等jdbc繁雜的過程代碼。
- Mybatis通過xml或注解的方式將要執行的各種statement(statement、preparedStatement、CallableStatement)配置起來,並通過java對象和statement中的sql進行映射生成最終執行的sql語句,最后由mybatis框架執行sql並將結果映射成java對象並返回。
2.為什么要使用MyBatis(使用JDBC編程有哪些問題)?
-
數據庫鏈接創建、釋放頻繁造成系統資源浪費從而影響系統性能,如果使用數據庫鏈接池可解決此問題。
-
Sql語句在代碼中硬編碼,造成代碼不易維護,實際應用sql變化的可能較大,sql變動需要改變java代碼。
- 使用preparedStatement向占有位符號傳參數存在硬編碼,因為sql語句的where條件不一定,可能多也可能少,修改sql還要修改代碼,系統不易維護。
- 對結果集解析存在硬編碼(查詢列名),sql變化導致解析代碼變化,系統不易維護,如果能將數據庫記錄封裝成pojo對象解析比較方便。
3.MyBatis架構
- MyBatis配置
-
SqlMapConfig.xml,此文件作為mybatis的全局配置文件,配置了mybatis的運行環境等信息。
- mapper.xml文件即sql映射文件,文件中配置了操作數據庫的sql語句。此文件需要在SqlMapConfig.xml中加載。
-
-
通過mybatis環境等配置信息構造SqlSessionFactory即會話工廠
-
由會話工廠創建sqlSession即會話,操作數據庫需要通過sqlSession進行。
- mybatis底層自定義了Executor執行器接口操作數據庫,Executor接口有兩個實現,一個是基本執行器、一個是緩存執行器。
-
Mapped Statement也是mybatis一個底層封裝對象,它包裝了mybatis配置信息及sql映射信息等。mapper.xml文件中一個sql對應一個Mapped Statement對象,sql的id即是Mapped statement的id。
-
Mapped Statement對sql執行輸入參數進行定義,包括HashMap、基本類型、pojo,Executor通過Mapped Statement在執行sql前將輸入的java對象映射至sql中,輸入參數映射就是jdbc編程中對preparedStatement設置參數。
- Mapped Statement對sql執行輸出結果進行定義,包括HashMap、基本類型、pojo,Executor
4.MyBatis入門
創建Maven工程
修改pom.xml導入MyBatis以及Junit和MySql驅動的jar包
<dependencies> <!-- MySQL驅動 --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.29</version> </dependency> <!--junit --> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> <scope>test</scope> </dependency> <!-- mybatis --> <!-- https://mvnrepository.com/artifact/org.mybatis/mybatis --> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.4.5</version> </dependency>
</dependencies>
classpath目錄下創建MyBatis的核心配置文件SqlMapConfig.xml
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"> <configuration> <environments default="development"> <environment id="development"> <!-- 使用jdbc事務管理 --> <transactionManager type="JDBC" /> <!-- 數據庫連接池 --> <dataSource type="POOLED"> <property name="driver" value="com.mysql.jdbc.Driver" /> <property name="url" value="jdbc:mysql://localhost:3306/mybatis?characterEncoding=utf-8" /> <property name="username" value="root" /> <property name="password" value="root" /> </dataSource> </environment> </environments> <!-- 加載映射文件 --> <mappers> <mapper resource="User.xml" /> </mappers> </configuration>
創建測試數據庫
創建po類 ---Po類作為mybatis進行sql映射使用,po類通常與數據庫表對應
package pojo;
import java.util.Date;
public class User {
private int id;
private String username;// 用戶姓名
private String sex;// 性別
private Date birthday;// 生日
private String address;// 地址
getter and setter方法......
@Override
public String toString() {
return "User [id=" + id + ", username=" + username + ", sex=" + sex + ", birthday=" + birthday + ", address="
+ address + "]";
}
}
創建sql映射文件User.xml
<?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"> <!-- namespace:命名空間,做sql隔離 --> <mapper namespace="test"> <!-- id:sql語句唯一標識 parameterType:指定傳入參數類型 resultType:返回結果集類型 #{}占位符:起到占位作用,如果傳入的是基本類型(string,long,double,int,boolean,float等),那么#{}中的變量名稱可以隨意寫. --> <select id="findUserById" parameterType="java.lang.Integer" resultType="cn.itheima.pojo.User"> select * from user where id=#{id} </select> <!-- 如果返回結果為集合,可以調用selectList方法,這個方法返回的結果就是一個集合,所以映射文件中應該配置成集合泛型的類型 ${}拼接符:字符串原樣拼接,如果傳入的參數是基本類型(string,long,double,int,boolean,float等),那么${}中的變量名稱必須是value 注意:拼接符有sql注入的風險,所以慎重使用 --> <select id="findUserByUserName" parameterType="java.lang.String" resultType="cn.itheima.pojo.User"> select * from user where username like '%${value}%' </select> <!-- #{}:如果傳入的是pojo類型,那么#{}中的變量名稱必須是pojo中對應的屬性.屬性.屬性..... 如果要返回數據庫自增主鍵:可以使用select LAST_INSERT_ID() --> <insert id="insertUser" parameterType="cn.itheima.pojo.User" > <!-- 執行 select LAST_INSERT_ID()數據庫函數,返回自增的主鍵 keyProperty:將返回的主鍵放入傳入參數的Id中保存. order:當前函數相對於insert語句的執行順序,在insert前執行是before,在insert后執行是AFTER resultType:id的類型,也就是keyproperties中屬性的類型 --> <selectKey keyProperty="id" order="AFTER" resultType="java.lang.Integer"> select LAST_INSERT_ID() </selectKey> insert into user (username,birthday,sex,address) values(#{username},#{birthday},#{sex},#{address}) </insert> <delete id="delUserById" parameterType="int"> delete from user where id=#{id} </delete> <update id="updateUserById" parameterType="cn.itheima.pojo.User"> update user set username=#{username} where id=#{id} </update> </mapper>
加載映射文件 --mybatis框架需要加載映射文件,將User.xml添加在SqlMapConfig.xml
<!-- 加載映射文件 --> <mappers> <mapper resource="User.xml" /> </mappers>
創建測試類UserTest進行測試
查詢單個User對象
@Test public void test1() throws IOException { // 核心配置文件 String resource = "SqlMapConfig.xml"; // 通過流將核心配置文件加載進來 InputStream inputStream = Resources.getResourceAsStream(resource); // 通過配置文件創建會話工廠 SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(inputStream); // 通過會話工廠獲取會話 SqlSession openSession = factory.openSession(); // 通過會話執行sql 第一個參數是名稱空間+SqlID 第二個參數表示sql執行需要的參數 User user = openSession.selectOne("test.findUserById", 1); System.out.println(user.toString()); // 關閉會話 openSession.close(); }
通過username進行模糊查詢
@Test public void test2() throws IOException { // 核心配置文件 String resource = "SqlMapConfig.xml"; // 通過流將核心配置文件加載進來 InputStream inputStream = Resources.getResourceAsStream(resource); // 通過配置文件創建會話工廠 SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(inputStream); // 通過會話工廠獲取會話 SqlSession openSession = factory.openSession(); // 調用User.xml中的魔化查詢方法 返回集合 List<User> selectList = openSession.selectList("test.findUserByName", "張"); // 循環結果 System.out.println(selectList.size()); for (User user : selectList) { System.out.println(user.toString()); } // 關閉會話 openSession.close(); }
添加一條User用戶到數據庫
@Test public void test3() throws IOException { // 核心配置文件 String resource = "SqlMapConfig.xml"; // 通過流將核心配置文件加載進來 InputStream inputStream = Resources.getResourceAsStream(resource); // 通過配置文件創建會話工廠 SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(inputStream); // 通過會話工廠獲取會話 SqlSession openSession = factory.openSession(); // 創建需要插入的User對象 User user = new User(); user.setUsername("Jimisun"); user.setSex("1"); user.setAddress("北京"); System.out.println("====插入前的User的id=" + user.getId()); // 會話調用插入的sql openSession.insert("test.insertUser", user); // 默認mybatis自動開啟事務,需要手動提交事務 openSession.commit(); System.out.println("====插入后的User的id=" + user.getId()); // 關閉會話 openSession.close(); }
刪除一條記錄
@Test public void test4() throws IOException { // 核心配置文件 String resource = "SqlMapConfig.xml"; // 通過流將核心配置文件加載進來 InputStream inputStream = Resources.getResourceAsStream(resource); // 通過配置文件創建會話工廠 SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(inputStream); // 通過會話工廠獲取會話 SqlSession openSession = factory.openSession(); // 會話執行sql操作 openSession.delete("test.deleteUserById", 1); // 提交事務 openSession.commit(); // 關閉會話 openSession.close(); }
更新一條記錄
@Test public void test5() throws Exception { // 核心配置文件 String resource = "SqlMapConfig.xml"; // 通過流將核心配置文件加載進來 InputStream inputStream = Resources.getResourceAsStream(resource); // 通過配置文件創建會話工廠 SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(inputStream); // 通過會話工廠獲取會話 SqlSession openSession = factory.openSession(); //創建User對象 User user = new User (); user.setId(1); user.setUsername("王麻子"); openSession.update("test.updateByUserId", user); //提交事務 openSession.commit(); //關閉會話 openSession.close(); }
5.使用MyBatis的開發方法
- 原生Dao方法
- UserDao 接口
- UserDaoImpl 實現類
- findUserById() -----方法內使用MyBatis框架進行操作
// 核心配置文件 String resource = "SqlMapConfig.xml"; // 通過流將核心配置文件加載進來 InputStream inputStream = Resources.getResourceAsStream(resource); // 通過配置文件創建會話工廠 SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(inputStream); // 通過會話工廠獲取會話 SqlSession openSession = factory.openSession(); // 通過會話執行sql 第一個參數是名稱空間+SqlID 第二個參數表示sql執行需要的參數 User user = openSession.selectOne("test.findUserById", 1); System.out.println(user.toString()); // 關閉會話 openSession.close();
- findUserById() -----方法內使用MyBatis框架進行操作
- Mapper接口開發 ----Mapper接口開發方法只需要程序員編寫Mapper接口(相當於Dao接口),由Mybatis框架根據接口定義創建接口的動態代理對象,代理對象的方法體同上邊Dao接口實現類方法。
- 開發規范
-
Mapper.xml文件中的namespace與mapper接口的類路徑相同。
- Mapper接口方法名和Mapper.xml中定義的每個statement的id相同
-
Mapper接口方法的輸入參數類型和mapper.xml中定義的每個sql 的parameterType的類型相同
-
Mapper接口方法的輸出參數類型和mapper.xml中定義的每個sql的resultType的類型相同
-
- 開發目錄
- UserDao 接口 遵循上面規則
- UserServiceImpl直接調用
@Test public void testFindUserById() throws Exception{ SqlSession openSession = factory.openSession(); //通過getMapper方法來實例化接口 UserMapper mapper = openSession.getMapper(UserMapper.class); User user = mapper.findUserById(1); System.out.println(user); }
- 開發規范
6.SqlMapConfig.xml配置文件
- properties(屬性) 常用於加載配置文件
<properties resource="db.properties"></properties> <environments default="development"> <environment id="development"> <!-- 使用jdbc事務管理--> <transactionManager type="JDBC" /> <!-- 數據庫連接池--> <dataSource type="POOLED"> <property name="driver" value="${jdbc.driver}" /> <property name="url" value="${jdbc.url}" /> <property name="username" value="${jdbc.username}" /> <property name="password" value="${jdbc.password}" /> </dataSource> </environment> </environments>
-
typeAliases(類型別名)
<typeAliases> <!-- 定義單個pojo類別名 type:類的全路勁名稱 alias:別名 --> <typeAlias type="cn.itheima.pojo.User" alias="user"/> <!-- 使用包掃描的方式批量定義別名 定以后別名等於類名,不區分大小寫,但是建議按照java命名規則來,首字母小寫,以后每個單詞的首字母大寫 --> <package name="cn.itheima.pojo"/> </typeAliases>
-
mappers(映射器)
- 相對於類路徑的資源
<mapper resource="sqlmap/User.xml" />
- 使用mapper接口開發
<mapper class="cn.redrat.mybatis.mapper.UserMapper"/>
- 注冊指定包下所有的mapper接口
<package name="cn.redrat.mybatis.mapper"/>
- 注意:此種方法要求mapper接口名稱和mapper映射文件名稱相同,且放在同一個目錄中。
- 相對於類路徑的資源
7.輸入映射和輸出映射
- Mapper.xml映射文件中定義了操作數據庫的sql,每個sql是一個statement,映射文件是mybatis的核心。
-
parameterType(輸入類型)
- 傳遞基本類型包含String
- 傳遞pojo對象
- 傳遞vo對象
-
resultType(輸出類型)
- 返回pojo類型
- 返回集合
- 返回基本類型包含String
-

8.動態Sql --通過mybatis提供的各種標簽方法實現動態拼接sql
- if
<!-- 傳遞pojo綜合查詢用戶信息 注意要做不等於空字符串校驗--> <select id="findUserList" parameterType="user" resultType="user"> select * from user where 1=1 <if test="id!=null"> and id=#{id} </if> <if test="username!=null and username!=''"> and username like '%${username}%' </if> </select>
- where
<select id="findUserList" parameterType="user" resultType="user"> select * from user <where> <if test="id!=null and id!=''"> and id=#{id} </if> <if test="username!=null and username!=''"> and username like '%${username}%' </if> </where> </select
where標簽的作用可以去掉sql語句中的where 1=1 並自動處理第一個 and
- foreach
<select id="findUserByIds" parameterType="cn.redrat.pojo.QueryVo" resultType="cn.redrat.pojo.User"> select * from user <where> <if test="ids != null"> <!-- foreach:循環傳入的集合參數 collection:傳入的集合的變量名稱 item:每次循環將循環出的數據放入這個變量中 open:循環開始拼接的字符串 close:循環結束拼接的字符串 separator:循環中拼接的分隔符 --> <foreach collection="ids" item="id" open="id in (" close=")" separator=","> #{id} </foreach> </if> </where> </select>
9.MyBatis關聯查詢核心示例
- 一對一查詢
<!-- 一對一:自動映射 --> <select id="findOrdersAndUser1" resultType="cn.redrat.pojo.CustomOrders"> select a.*, b.id uid, username, birthday, sex, address from orders a, user b where a.user_id = b.id </select> <!-- 一對一:手動映射 --> <!-- id:resultMap的唯一標識 type:將查詢出的數據放入這個指定的對象中 注意:手動映射需要指定數據庫中表的字段名與java中pojo類的屬性名稱的對應關系 --> <resultMap type="cn.redrat.pojo.Orders" id="orderAndUserResultMap"> <!-- id標簽指定主鍵字段對應關系 column:列,數據庫中的字段名稱 property:屬性,java中pojo中的屬性名稱 --> <id column="id" property="id"/> <!-- result:標簽指定非主鍵字段的對應關系 --> <result column="user_id" property="userId"/> <result column="number" property="number"/> <result column="createtime" property="createtime"/> <result column="note" property="note"/> <!-- 這個標簽指定單個對象的對應關系 property:指定將數據放入Orders中的user屬性中 javaType:user屬性的類型 --> <association property="user" javaType="cn.redrat.pojo.User"> <id column="uid" property="id"/> <result column="username" property="username"/> <result column="birthday" property="birthday"/> <result column="sex" property="sex"/> <result column="address" property="address"/> </association> </resultMap> <select id="findOrdersAndUser2" resultMap="orderAndUserResultMap"> select a.*, b.id uid, username, birthday, sex, address from orders a, user b where a.user_id = b.id </select>
- 一對多
<resultMap type="cn.redrat.pojo.User" id="userAndOrdersResultMap"> <id column="id" property="id"/> <result column="username" property="username"/> <result column="birthday" property="birthday"/> <result column="sex" property="sex"/> <result column="address" property="address"/> <!-- 指定對應的集合對象關系映射 property:將數據放入User對象中的ordersList屬性中 ofType:指定ordersList屬性的泛型類型 --> <collection property="ordersList" ofType="cn.redrat.pojo.Orders"> <id column="oid" property="id"/> <result column="user_id" property="userId"/> <result column="number" property="number"/> <result column="createtime" property="createtime"/> </collection> </resultMap> <select id="findUserAndOrders" resultMap="userAndOrdersResultMap"> select a.*, b.id oid ,user_id, number, createtime from user a, orders b where a.id = b.user_id </select>
