導讀
官網地址
https://mybatis.org/mybatis-3/zh/index.html
架構原理圖
說明
mybatis配置文件
- SqlMapConfig.xml,此文件為mybatis的全局配置文件,配置了mybatis的運行環境等信息
- XXXMapper.xml,此文件作為mybatis的sql映射文件,文件中配置了操作數據庫的CRUD語句。需要在SqlMapConfig.xml中加載
SqlSessionFactory
- 通過mybatis環境等配置信息構造SqlSessionFactory,既會話工廠
***跟底層源碼查看創建SqlSessionFactory流程***
注:底層如何獲取標簽值,請自行研究(劇透:for循環遍歷XML獲取標簽中的值,然后放入Map)!~
SqlSession
- 通過會員工廠創建SqlSession即會話,程序通過SqlSession會話接口對數據庫進行CRUD操作。
Executor執行器
mybatis底層自定義了Executor執行器接口來具體操作數據庫,Executor接口有兩個實現,一個是基本執行器(默認),一個緩存執行器,SqlSession底層是通過executor接口操作數據庫
Mapped Statement
他是mybatis一個底層封裝對象,包裝了mybatis配置信息及XXXMapper.xml映射文件等。XXXMapper.xml文件中一個個select/insert/update/delete標簽對應一個Mapped Statement對象。
原始JDBC代碼
原始JDBC和mybatis操作數據庫數據,與上面架構圖流程相對應。
public class JDBCTest { public static void main(String[] args) { Connection connection = null; PreparedStatement preparedStatement = null; ResultSet resultSet = null; try { // 加載數據庫驅動 Class.forName("com.mysql.jdbc.Driver"); // 通過驅動管理類獲取數據庫鏈接connection = DriverManager connection = DriverManager.getConnection( "jdbc:mysql://localhost:3306/mybatis?characterEncoding=utf-8", "root", "root" ); // 定義sql語句 ?表示占位符 String sql = "select * from user where username = ?"; // 獲取預處理 statement preparedStatement = connection.prepareStatement(sql); // 設置參數,第一個參數為 sql 語句中參數的序號(從 1 開始),第二個參數為設置的 preparedStatement.setString(1, "王五"); // 向數據庫發出 sql 執行查詢,查詢出結果集 resultSet = preparedStatement.executeQuery(); // 遍歷查詢結果集 while (resultSet.next()) { System.out.println( resultSet.getString("id") + " " + resultSet.getString("username") ); } } catch (Exception e) { e.printStackTrace(); } finally { // 釋放資源 if (resultSet != null) { try { resultSet.close(); } catch (SQLException e) { e.printStackTrace(); } } if (preparedStatement != null) { try { preparedStatement.close(); } catch (SQLException e) { e.printStackTrace(); } } if (connection != null) { try { connection.close(); } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } } }
Mybatis 入門基礎
表結構
表數據
Mybatis環境搭建 

添加依賴
pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.cyb</groupId> <artifactId>mybatis</artifactId> <packaging>war</packaging> <version>0.0.1-SNAPSHOT</version> <name>mybatis Maven Webapp</name> <url>http://maven.apache.org</url> <dependencies> <!-- mybatis依賴 --> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.4.6</version> </dependency> <!-- mysql依賴 --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.20</version> </dependency> <!-- 單元測試 --> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> </dependency> </dependencies> <build> <finalName>mybatis</finalName> </build> </project>
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> <!-- 引入外部配置文件 --> <properties resource="db.properties"></properties> <!-- 數據庫鏈接相關 --> <environments default="development"> <environment id="development"> <transactionManager type="JDBC" /> <dataSource type="POOLED"> <property name="driver" value="${db.driver}" /> <property name="url" value="${db.url}" /> <property name="username" value="${db.username}" /> <property name="password" value="${db.password}" /> </dataSource> </environment> </environments> <mappers> <!-- 添加映射文件 --> <mapper resource="UserMapper.xml" /> </mappers> </configuration>
db.properties
db.driver=com.mysql.jdbc.Driver
db.url=jdbc:mysql://127.0.0.1:3306/cyb
db.username=root
db.password=root
UserMapper.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:為了分類管理映射文件中的MappedStatement --> <mapper namespace="test"> <select id="queryUserById" parameterType="int" resultType="com.cyb.mybatis.demo.User"> select * from user where id = #{id} </select> </mapper>
User.java
package com.cyb.mybatis.demo; import java.util.Date; public class User { private int id; private String username; private Date birthday; private int sex; private String address; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public Date getBirthday() { return birthday; } public void setBirthday(Date birthday) { this.birthday = birthday; } public int getSex() { return sex; } public void setSex(int sex) { this.sex = sex; } public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } @Override public String toString() { return "User [id=" + id + ", username=" + username + ", birthday=" + birthday + ", sex=" + sex + ", address=" + address + "]"; } }
MybatisDemo.java
package com.cyb.mybatis.demo; import java.io.InputStream; import org.apache.ibatis.io.Resources; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactoryBuilder; import org.junit.Before; import org.junit.Test; public class MybatisDemo { private SqlSessionFactory sqlSessionFactory; @Before public void init() throws Exception { //指定全局配置文件路徑 String resource = "SqlMapConfig.xml"; //加載資源文件(包括全局文件和映射文件) InputStream inputStream = Resources.getResourceAsStream(resource); //使用構建者模式創建SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); } @Test public void testSelect() { //由SqlSessionFactory工廠去創建SqlSession(會話) SqlSession sqlSession = sqlSessionFactory.openSession(); //調用SqlSession接口,去實現數據庫的CRUD User user = sqlSession.selectOne("test.queryUserById", 1); System.out.println(user); //釋放資源 sqlSession.close(); } }
項目結構
測試
功能實現
根據用戶id查詢一個用戶信息
根據用戶名稱模糊查詢用戶信息列表
添加用戶
主鍵返回
<!-- 主鍵返回 --> <insert id="insertUser" parameterType="com.cyb.mybatis.demo.User"> <!-- selectKey將主鍵返回,需要再返回 --> <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>
添加selectKey標簽實現主鍵返回。
* keyProperty:指定返回的主鍵,存儲在pojo中的哪個屬性
* order:selectKey標簽中的sql的執行順序,是相對與insert語句來說。由於mysql的自增原理,執行完insert語句之后才將主鍵生成,所以這里selectKey的執行順序為after。
* resultType:返回的主鍵對應的JAVA類型
* LAST_INSERT_ID():是mysql的函數,返回auto_increment自增列新記錄id值。
更新用戶
刪除用戶
Mybatis開發Dao層
mapper代理開發方式
XML方式
使用:只需要開發Mapper接口(Dao接口)和xxxMapper約束文件,不需要編寫實現類。
開發規范:
- Mapper接口的類路徑與xxxMapper.xml文件中的namespace相同
- Mapper接口方法名稱和xxxMapper.xml中定義的每個statement的id相同
- Mapper接口方法的輸入參數類型和xxxMapper.xml中定義的每個sql的parameterType的類型相同
- Mapper接口方法的返回值類型和xxxMapper.xml中定義的每個sql的resultType類型相同
pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.cyb</groupId> <artifactId>mybatis</artifactId> <packaging>war</packaging> <version>0.0.1-SNAPSHOT</version> <name>mybatis Maven Webapp</name> <url>http://maven.apache.org</url> <dependencies> <!-- mybatis依賴 --> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.4.6</version> </dependency> <!-- mysql依賴 --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.20</version> </dependency> <!-- 單元測試 --> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> </dependency> </dependencies> <build> <finalName>mybatis</finalName> </build> </project>
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> <!-- 引入外部配置文件 --> <properties resource="db.properties"></properties> <!-- 數據庫鏈接相關 --> <environments default="development"> <environment id="development"> <transactionManager type="JDBC" /> <dataSource type="POOLED"> <property name="driver" value="${db.driver}" /> <property name="url" value="${db.url}" /> <property name="username" value="${db.username}" /> <property name="password" value="${db.password}" /> </dataSource> </environment> </environments> <mappers> <!-- 添加映射文件 --> <mapper resource="mapper/UserMapper.xml" /> </mappers> </configuration>
db.properties
db.driver=com.mysql.jdbc.Driver
db.url=jdbc:mysql://127.0.0.1:3306/cyb
db.username=root
db.password=root
UserMapper.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:為了分類管理映射文件中的MappedStatement --> <mapper namespace="com.cyb.mybatis.mapper.UserMapper"> <!-- 通過ID查詢 --> <select id="queryUserById" parameterType="int" resultType="com.cyb.mybatis.demo.User"> select * from user where id = #{id} </select> </mapper>
Use.java
package com.cyb.mybatis.demo; import java.util.Date; public class User { private int id; private String username; private Date birthday; private int sex; private String address; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public Date getBirthday() { return birthday; } public void setBirthday(Date birthday) { this.birthday = birthday; } public int getSex() { return sex; } public void setSex(int sex) { this.sex = sex; } public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } @Override public String toString() { return "User [id=" + id + ", username=" + username + ", birthday=" + birthday + ", sex=" + sex + ", address=" + address + "]"; } }
UserMapper.java
package com.cyb.mybatis.mapper; import com.cyb.mybatis.demo.User; public interface UserMapper { /** * 根據用戶id查詢用戶信息 * @param id 內碼 * @return * @throws Exception */ public User queryUserById(int id) throws Exception; }
MybatisDemo.java
package com.cyb.mybatis.demo; import java.io.InputStream; import java.util.Date; import java.util.List; import org.apache.ibatis.io.Resources; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactoryBuilder; import org.junit.Before; import org.junit.Test; import com.cyb.mybatis.mapper.UserMapper; public class MybatisDemo { private SqlSessionFactory sqlSessionFactory; @Before public void init() throws Exception { // 指定全局配置文件路徑 String resource = "SqlMapConfig.xml"; // 加載資源文件(包括全局文件和映射文件) InputStream inputStream = Resources.getResourceAsStream(resource); // 使用構建者模式創建SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); } @Test public void testSelect() throws Exception { // 由SqlSessionFactory工廠去創建SqlSession(會話) SqlSession sqlSession = sqlSessionFactory.openSession(); // 調用SqlSession接口,去實現數據庫的CRUD UserMapper userMapper=sqlSession.getMapper(UserMapper.class); User user = userMapper.queryUserById(1); System.out.println(user); // 釋放資源 sqlSession.close(); } }
項目結構
測試
全局配置文件
properties標簽
可以引入java屬性文件中的配置信息
typeAlias標簽
別名的作用:為了簡化映射文件中parameterType和resultType中POJO類型的包名
默認支持別名
批量指定別名(推薦)
注:可以寫多個package,但是package和typeAlias不能一起用!!!
單個指定別名(typeAlias)
注:可以寫多個typeAlias,但是package和typeAlias不能一起用!!!
mappers標簽
<mapper resource="" /> (不推薦)
注:一次加載一個映射文件,相當於資源路徑
<package name="" />(推薦)
注冊指定包下的所有mapper接口,來加載mapper映射文件。
注:mapper接口和mapper映射文件名稱相同,且放到同一目錄下
關聯查詢
一對多
Collection標簽:定義了一對多關聯的結果映射。
property="orders":關聯查詢的結果集存儲在User對象的上哪個屬性。
ofType="orders":指定關聯查詢的結果集中的對象類型即List中的對象類型。此處可以使用別名,也可以使用全限定名。
源碼部分
構建SqlSessionFactory過程
下面我們具體的查看下是如何構建SqlSessionFactory對象
通過源碼可以看出,最終會生成一個DefaultSqlSessionFactory實例,這個不是我們關注的重點,我們核心是關注如何初始化對象的。
構建XMLConfigBuilder對象
build函數首先會構造一個XMLConfigBuilder對象,他是解析XML配置文件的。
- XMLxxxBuilder:解析XML配置文件的,不同類型的XMLxxxBuilder解析不同的部位
- XMLConfigBuilder:解析mybatis全局配置文件
- XMLMapperBuilder:解析mybatis映射文件
- XMLStatementBuilder:解析映射文件中statement語句(CRUD Sql語句)
- MapperBuilderAssistant:輔助解析映射文件並生成MappedStatement對象
這些XMLxxxBuilder都有一個共同的父類->BaseBuilder。這個父類維護了全局的Configuration對象,mybatis的配置文件解析后就以Configuration對象的形式存儲。
加載映射文件
底層代碼量較多,錄制的gif圖片較大不能上傳,分3段上傳的,內容都是連續的;這里拋塊磚,實際還是要自己打個斷點,跟蹤下底層源碼,根據自身愛好,研究相應內容,准備工作:了解知識點:XPath(如,解析XML配置文件,點我直達)、設計模式(如:構建者模式,點我直達)、面向對象等等,要不然跟蹤源碼是件很痛苦的事兒~~~
構建者模式使用地方
XPath使用地方
補充
注意:此處用到了遍歷list節點,因為映射文件有很多的select、update、delete標簽哦~
打開session會話
我們可以看到,SqlSessionFactory是一個接口,里面有很多重載方法
SqlSessionFactory接口重載說明
跟蹤到SqlSessionFactory的實現類DefaultSqlSessionFactory
通過跟蹤到DefaultSqlSessionFactory,我們可以看到,底層使用了重載,默認自動提交事務未false,從而解答了,我們使用默認無參構造函數時,對數據庫進行:插入、修改、刪除,需要手動提交事務
細心的小伙伴此時會問,問什么跟蹤源碼的時候,他不是有兩個實現類嗎,為什么要跟蹤上面一個,不跟蹤下面那個,是因為創建SqlSessionFactory的時候,默認返回的是DefaultSqlSessionFactory
跟蹤selectOne
selectList分析
我們跟蹤下,是如何從Configuration全局配置文件中(從Map中獲取),獲取MapperStatement的;注:這個全局配置文件(configuration)是在初始化SqlSessionFactory時加載的
接下來我們跟蹤下,是如何解析傳遞的參數的,分3種處理情況,分別為:集合、list、其他;注:參數類型與xxxMapper.xml的parameterType類型想對應,傳什么類型,走什么情況
跟蹤executor.query
先走下面那個實現類,在走上面那個實現類
為什么先走下面那個實現類呢?看gif,先委托下面那個實現類
設置參數綁定
打個斷點,調試下就知道了!!