MyBatis框架安排
目錄
1. 第一天:
1. MyBatis入門
- 什麼是框架?
答:它是我們軟件開發過程中的一套解決方案, 不同的框架解決不同的問題。
簡而言之,框架其實就是某種應用的半成品,就是一組組件,供你選用完成你自己的系統。簡單說就是使用別 人搭好的舞臺,你來做表演。而且,框架一般是成熟的,不斷升級的軟件。 - 三層框架
- 表現層:control
- 業務邏輯層:service
- 持久層:dao
可以看到不同的框架各司其職,在軟件開發中擔任不同的角色,MyBatis框架主要是與數據庫打交道,所以它工作在持久層。
- 持久層的傳統解決方案
- JDBC技術
- Connection:連接數據庫對象
- PrepareStatement:預處理對象,用於操作sql
- ResultSet:操作數據庫後生成的結果集
- Spring的SpringTemplate技術
Spring中對jdbc的簡單封裝 - Apache的DBUtils
和SpringTemplate類似,Apache中對jdbc的簡單封裝
- JDBC技術
以上所用都不是框架
JDBC是接口規範
SpringTemplate和DBUtils都是對jdbc接口規範的實現類,是一個工具類。
傳統的jdbc訪問數據庫:
public static void main(String[] args) {
Connection connection = null;
PreparedStatement preparedStatement = null;
ResultSet resultSet = null;
try {
//加載數據庫驅動
Class.forName("com.mysql.jdbc.Driver");
//通過驅動管理類獲取數據庫鏈接
connection =DriverManager.getConnection("jdbc:mysql://localhost:3306/mybatis?characterEncoding=utf-8","ro ot", "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();
}
}
}
}
上邊使用 jdbc 的原始方法(未經封裝)實現了查詢數據庫表記錄的操作。
PreparedStatement對象它的執行方法
- execute:可以執行任何CRUD語句,返回值是一個布爾值,表示是否有結果。
- excuteUpdate:只能執行CUD,查詢語句無法執行,返回值數據庫操作影響的行數。
- excuteQuery:只能執行Select 語句,無法進行增刪改操作,執行的結果集爲ResultSet對象
2. MyBatis概述
-
概念:mybatis是一個持久層框架,用Java編寫,它封裝了jdbc的很多細節,使開發者能夠只關注sql語句本身,無需關注註冊驅動,創建連接等繁瑣的過程,它使用了ORM思想實現了結果集的封裝。
ORM思想:
Object Relational Mapping對象關係映射
簡單的說就是實體類的屬性和數據庫表的字段產生了對應關係,使得我們操作實體類就可以操作數據庫表了。
例如:user表 User類 username varchar(32) String username id int(12) int id grade varchar(12) String grade
3. MyBatis環境搭建
-
環境搭建步驟:
- 創建maven工程並導入座標
- 創建實體類和dao接口
- 創建MyBatis的主配置文件:SqlMapConfig.xml
- 創建dao層映射配置文件:IUserDao.xml
-
注意事項:
- 映射配置文件還有一種寫法是Mapper.xml。我們寫成IUserDao.xml主要是爲了和之前寫過的dao做一個過渡。
- 在裏面創建目錄時,它和創建包是不一樣的。例如
創建目錄:com.liuzeyu.dao 它只是一級目錄,在硬盤上也是一級目錄
創建包:com.liuzeyu.dao 它只是三級目錄,分層顯示,在硬盤上也是三級目錄 - mybatis的映射配置文件必須和dao層的包結構相同,映射配置文件寫在resource資源文件下
- 映射配置文件的mapper標籤下的namespace屬性取值必須是dao層接口的全限定類名
- 映射配置文件的操作配置,如select的id屬性取值必須是dao層接口的方法名
當我們遵從了3,4,5點後,就可以不用像從前那樣寫dao接口的實現類了。
4. MyBatis入門案例
-
在搭建完環境後,完成入門案例。首選創建實體類和MySQL數據庫和表,並生成相應的getter和setter方法
sql代碼:DROP TABLE IF EXISTS `user`; CREATE TABLE `user` ( `id` int(11) NOT NULL auto_increment, `username` varchar(32) NOT NULL COMMENT '用戶名稱', `birthday` datetime default NULL COMMENT '生日', `sex` char(1) default NULL COMMENT '性別', `address` varchar(256) default NULL COMMENT '地址', PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; insert into `user`(`id`,`username`,`birthday`,`sex`,`address`) values (41,'老王','2018-02-27 17:47:08','男','北京'),(42,'小二王','2018-03-02 15:09:37','女','北京金燕龍'),(43,'小二王','2018-03-04 11:34:34','女','北京金燕龍'),(45,'傳智播客','2018-03-04 12:04:06','男','北京金燕龍'),(46,'老王','2018-03-07 17:37:26','男','北京'),(48,'小馬寶莉','2018-03-08 11:44:00','女','北京修正');
-
項目對象模型(pom.xml)
<?xml version="1.0" encoding="UTF-8"?> <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/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.liuzeyu</groupId> <artifactId>day01_mybatis</artifactId> <version>1.0-SNAPSHOT</version> <packaging>jar</packaging> <dependencies> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.4.5</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.6</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.10</version> </dependency> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.12</version> </dependency> </dependencies> </project>
-
dao層接口
package com.liuzeyu.dao; import com.liuzeyu.domain.User; import java.util.List; public interface IUserDao { public List<User> findAll(); }
-
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"> <!-- mybatis主配置文件 --> <configuration> <!-- 配置環境 --> <environments default="mysql"> <!-- 配置mysql環境 --> <environment id="mysql"> <!-- 配置事物類型 --> <transactionManager type="JDBC"></transactionManager> <!-- 配置數據庫源(連接池)--> <dataSource type="POOLED"> <!-- 配置連接數據庫的四個基本信息 --> <property name="driver" value="com.mysql.jdbc.Driver" /> <property name="url" value="jdbc:mysql://localhost:3306/eesy_mybatis" /> <property name="username" value="root" /> <property name="password" value="809080" /> </dataSource> </environment> </environments> <!-- 指定映射配置文件的位置,映射配置文件是指每一個獨立的dao接口配置--> <mappers> <mapper resource="com.liuzeyu.dao.IUserDao.xml"/> </mappers> </configuration>
-
映射文件IUserDao.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"> <mapper namespace="com.liuzeyu.dao.IUserDao"> <select id="findAll"> SELECT * from user </select> </mapper>
-
編寫測試類MyBatisTest.java
public class MyBatisTest { /** * mybatis入門案例測試 * @param args */ public static void main(String[] args) throws Exception{ //1.讀取配置文件SqlMapConfig.xml InputStream is = MyBatisTest.class.getClassLoader().getResourceAsStream("SqlMapConfig.xml"); //2.創建SqlSessionFactory工廠 SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder(); SqlSessionFactory factory = builder.build(is); //3.使用factory創建SqlSession SqlSession session = factory.openSession(); //4.使用SqlSessionFactory創建dao層接口的動態代理對象 IUserDao dao = session.getMapper(IUserDao.class); //5.使用代理對象執行方法 List<User> users = dao.findAll(); //6.遍歷輸出結果 for (User user : users) { System.out.println(user); } //釋放資源 is.close(); session.close(); } }
-
運行後出現
翻譯過來,沒有找到IUserDao.xml,考慮到映射文件是在SqlMapConfig.xml上配置的,故檢查SqlMapConfig.xml
發現:
改成:
再次運行又發現:
這個問題是未在接口的映射配置文件中定義resultType導致的,resultType是操作數據庫返回封裝結果集的對象
添加後:
-
測試輸出:
設計到的設計模式分析(涉及到構建者模式,工廠模式,代理模式簡單看一下,下面還會學習到) -
查看測試類方法,發現代碼也比較複雜,涉及到多個對象,如SqlSessionFactoryBuilder,SqlSessionFactory…其實MyBat也提供了更爲簡潔的實習方式,即,註解配置,這樣雖然不能減少測試類的代碼編寫,但是可以直接刪除掉映射接口的配置文件,採用註解配置,將原來的配置文件刪除
重新在接口的定義處添加
感覺簡單了很多,少了配置文件,但是需要在Mybatis的主配置文件中修改映射文件的配置,將resource屬性改爲class屬性,並添加接口的全限定類名
重新運行程序:
-
其實MyBatis也提供了接口實現類的方法來與數據庫交互,只需要對dao層添加接口實現類,可以在9的基礎上進行改造
添加dao層的接口實現:
測試函數部分就可以省去SqlSession對象的建立
小結:
通過學習mybatis的案例可以發現,比我們之前使用的jdbc簡單很多,可以省去接口實現類和jdbc初始化等一系列操作,添加兩個配置文件即可,再者可以通過註解配置,使得與數據庫的交互更加的簡單。
但是這裏麪包含了很多的細節,爲什麼會有工廠對象SqlSessionFactory,爲什麼有了工廠對象後還需要有構建者對象SqlSessionFactoryBuilder,爲什麼IUserDao.xml 在創建的時候有文件名和位置的規定。通過下一章的學習可以明白mybatis的內部執行流程,並且可以對這些設計模式有一個認識。
注意事項
- 不要忘記在接口的映射配置文件中告知mybatis要封裝到哪個實體類中,配置的方式:指定實體類的全限定類名。
- 如果使用註解配置,需要在主配置文件處修改mapper屬性爲class屬性
5. 自定義MyBatis框架(深入瞭解MyBatis執行細節)
-
分析在上述案例中的測試類selectList是如何運行的?
List< E > selectList(PreparedStatement statement)
當代理對象過程中,調用了Proxy.newInstanceProxy(類加載器,代理對象要實現的接口字節碼數組,增強代碼),在增強代碼部分進行selectList的調用。
先不談代理模式,來看看selectList都是怎麼進行的呢?- 首先程序通過xml解析技術對SqlMapConfig.xml進行讀取,獲取到4個連接數據庫的必備信息和映射接口配置文件的路徑
- 有了driver,url,username,password就可以在selectList中創建Connection對象
- 通過配置文件的映射關係可以得到真正執行的sql語句和結果集的全限定類名。
- 有了sql就可以獲取到PreparedStatement 的preparedStatement對象了,prepareStatement = connection.prepareStatement(sql);
- 有了結果集的全限定類名接口就可以通過反射機制ClassforName(“全限定類名”).newInstance()得到真實的返回值類型對象
- 有了4,5兩對象就可以通過遍歷結果集來將結果存入準備好的list集合中並返回
問題:
基於4,5,如何將<mapper namespace="com.liuzeyu.dao.IUserDao"> <select id="findAll" resultType="com.liuzeyu.domain.User"> SELECT * from user </select> </mapper>
發送到selectList,需要將映射信息封裝成一個Mapper對象,這是一個Map對象
key(String) value(Mapper) com.liuzeyu.dao.findAll 全限定類名+sql語句 這樣做的好處是,如果以後會有多個查詢findById,findByName等一系列方法可以直接和全限定類名找到對應關係。
-
問題2:內部的SqlSession創建代理對象的執行流程
//4.使用SqlSessionFactory創建dao層接口的動態代理對象 IUserDao dao = session.getMapper(IUserDao.class);
核心是通過getMapper(IUserDao.class);來獲取代理對象的
public <T> getMapper(Class<T> daoInterfaceClass){
/**
類加載器:它使用的和被代理對象相同的類加載器
代理對象要實現的接口:它使用的和被代理對象實現相同的接口
如何代理:增強方法,需要我們字節來提供
此處是一個InvocationHandler的接口,我們需要寫一個該接口的實現類,在實現類中調用selectList方法
*/
Proxy.newProxyInstance(類加載器,代理對象要實現的接口字節碼數組,如何代理);
}
- 自定義代理模式
-
分析:
- mybatis使用dao代理的方式實現增刪改操作時做什麼事?
兩件事:- 創建代理對象
- 在代理對象中調用selectList
- mybatis使用dao代理的方式實現增刪改操作時做什麼事?
-
自定義mybatis 所需要的類
1. Class Resource
2. Class SqlSessionFactoryBuilder
3. interface SqlSessionFactory
4. interface SqlSession -
由於涉及的代碼較爲複雜,故將項目up到git上面
https://github.com/liuzeyu12a/mybatis-
-