一、MyBatis MyBatis 本是apache的一個開源項目iBatis, 2010年這個項目由apache software foundation 遷移到了google code,並且改名為MyBatis 。2013年11月遷移到Github。 iBATIS一詞來源於“internet”和“ ...
一、MyBatis
MyBatis 本是apache的一個開源項目iBatis, 2010年這個項目由apache software foundation 遷移到了google code,並且改名為MyBatis 。2013年11月遷移到Github。
iBATIS一詞來源於“internet”和“abatis”的組合,是一個基於Java的持久層框架。iBATIS提供的持久層框架包括SQL Maps和Data Access Objects(DAOs)
MyBatis 是一款優秀的持久層框架,它支持定製化 SQL、存儲過程以及高級映射。MyBatis 避免了幾乎所有的 JDBC 代碼和手動設置參數以及獲取結果集。MyBatis 可以使用簡單的 XML 或註解來配置和映射原生信息,將介面和 Java 的 POJOs(Plain Old Java Objects,普通的 Java對象)映射成資料庫中的記錄。
1.1、 MyBatis 的特點
簡單易學:本身就很小且簡單。沒有任何第三方依賴,最簡單安裝只有兩個jar文件+配置幾個sql映射文件易於學習,易於使用,通過文檔和源代碼,可以比較完全的掌握它的設計思路和實現。
靈活:mybatis不會對應用程式或者資料庫的現有設計加強任何影響。sql寫在xml里,便於統一管理和優化。通過sql基本上可以實現我們不使用數據訪問框架可以實現的所有功能,或許更多。
解除sql與程式代碼的耦合:通過提供DAO層,將業務邏輯和數據訪問邏輯分離,使系統的設計更清晰,更易維護,更易單元測試。sql和代碼的分離,提高了可維護性。
提供映射標簽,支持對象與資料庫的ORM欄位關係映射
提供對象關係映射標簽,支持對象關係組建維護
提供XML標簽,支持編寫動態sql。
1.2、MyBatis工作流程
(1)、載入配置並初始化
觸發條件:載入配置文件
配置來源於兩個地方,一處是配置文件,一處是Java代碼的註解,將SQL的配置信息載入成為一個個MappedStatement對象(包括了傳入參數映射配置、執行的SQL語句、結果映射配置),存儲在記憶體中。
(2)、接收調用請求
觸發條件:調用Mybatis提供的API
傳入參數:為SQL的ID和傳入參數對象
處理過程:將請求傳遞給下層的請求處理層進行處理。
(3)、處理操作請求 觸發條件:API介面層傳遞請求過來
傳入參數:為SQL的ID和傳入參數對象
處理過程:
(A)根據SQL的ID查找對應的MappedStatement對象。
(B)根據傳入參數對象解析MappedStatement對象,得到最終要執行的SQL和執行傳入參數。
(C)獲取資料庫連接,根據得到的最終SQL語句和執行傳入參數到資料庫執行,並得到執行結果。
(D)根據MappedStatement對象中的結果映射配置對得到的執行結果進行轉換處理,並得到最終的處理結果。
(E)釋放連接資源。
(4)、返回處理結果將最終的處理結果返回。
無論是用過的hibernate,mybatis,你都可以法相他們有一個共同點:
在java對象和資料庫之間有做mapping的配置文件,也通常是xml 文件
從配置文件(通常是XML配置文件中)得到 SessionFactory
由SessionFactory 產生 Session
在Session中完成對數據的增刪改查和事務提交等
在用完之後關閉Session
1.3、MyBatis架構
Mybatis的功能架構分為三層:
API介面層:提供給外部使用的介面API,開發人員通過這些本地API來操縱資料庫。介面層一接收到調用請求就會調用數據處理層來完成具體的數據處理。
數據處理層:負責具體的SQL查找、SQL解析、SQL執行和執行結果映射處理等。它主要的目的是根據調用的請求完成一次資料庫操作。
基礎支撐層:負責最基礎的功能支撐,包括連接管理、事務管理、配置載入和緩存處理,這些都是共用的東西,將他們抽取出來作為最基礎的組件。為上層的數據處理層提供最基礎的支撐。
1.4、MyBatis的主要成員如層次結構
主要成員:
Configuration:MyBatis所有的配置信息都保存在Configuration對象之中,配置文件中的大部分配置都會存儲到該類中
SqlSession:作為MyBatis工作的主要頂層API,表示和資料庫交互時的會話,完成必要資料庫增刪改查功能
Executor:MyBatis執行器,是MyBatis 調度的核心,負責SQL語句的生成和查詢緩存的維護
StatementHandler:封裝了JDBC Statement操作,負責對JDBC statement 的操作,如設置參數等
ParameterHandler:負責對用戶傳遞的參數轉換成JDBC Statement 所對應的數據類型
ResultSetHandler:負責將JDBC返回的ResultSet結果集對象轉換成List類型的集合
TypeHandler:負責java數據類型和jdbc數據類型(也可以說是數據表列類型)之間的映射和轉換
MappedStatement:MappedStatement維護一條<select|update|delete|insert>節點的封裝
SqlSource:負責根據用戶傳遞的parameterObject,動態地生成SQL語句,將信息封裝到BoundSql對象中,並返回
BoundSql:表示動態生成的SQL語句以及相應的參數信息
層次結構:
1.5、學習資源
mybatis3中文幫助:http://www.mybatis.org/mybatis-3/zh/index.html
mybatis-spring:http://www.mybatis.org/spring/zh/index.html
MyBatis中國分站:http://www.mybatis.cn/
源代碼:https://github.com/mybatis/mybatis-3/
二、MyBatis 快速入門示例
2.1、在IDEA中創建項目
普通java項目或者是Maven項目都可以,如下圖所示:
2.2、添加依賴
下載地址:https://github.com/mybatis/mybatis-3/releases
【MyBatis】
mybatis-3.4.6.jar
【MYSQL驅動包】
mysql-connector-java-5.1.38-bin.jar
Maven POM
<?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.hmy.mybatis02</groupId> <artifactId>Mybatis02</artifactId> <version>1.0-SNAPSHOT</version> <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>5.1.38</version> </dependency> <!-- JUnit單元測試工具 --> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.10</version> <scope>test</scope> </dependency> </dependencies> </project>
3、創建資料庫和表,針對MySQL資料庫
CREATE TABLE `student` ( `id` int(11) NOT NULL AUTO_INCREMENT, `name` varchar(10) NOT NULL, `sex` enum('boy','girl','secret') DEFAULT 'secret', PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8;
將SQL腳本在MySQL資料庫中執行,完成創建資料庫和表的操作,如下:
表中的數據如下:
2.3、添加Mybatis配置文件
在Resources目錄下創建一個conf.xml文件,如下圖所示:
conf.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"> <transactionManager type="JDBC"/> <dataSource type="POOLED"> <property name="driver" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/ssm"/> <property name="username" value="root"/> <property name="password" value="123456"/> </dataSource> </environment> </environments> <mappers> <mapper resource="mapper/studentMapper.xml"/> </mappers> </configuration>
解釋
2.4、定義表所對應的實體類
Student實體類的代碼如下:
package com.hanlu.mybatis02.entities; public class Student { private int id; private String name; private String sex; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getSex() { return sex; } public void setSex(String sex) { this.sex = sex; } @Override public String toString() { return "Student{" + "id=" + id + ", name='" + name + '\'' + ", sex='" + sex + '\'' + '}'; } }
2.5、定義操作Student表的sql映射文件
在resources目錄下創建一個mapper目錄,專門用於存放sql映射文件,在目錄中創建一個studentMapper.xml文件,如下圖所示:
studentMapper.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,namespace的值習慣上設置成包名+sql映射文件名,這樣就能夠保證namespace的值是唯一的 例如namespace="com.hanlu.mybatis02.dao.studentMapper"就是com.hanlu.mybatis02.dao(包名)+studentMapper(studentMapper.xml文件去除尾碼) --> <mapper namespace="com.hanlu.mybatis02.dao.studentMapper"> <!-- 在select標簽中編寫查詢的SQL語句, 設置select標簽的id屬性為selectStudentById,id屬性值必須是唯一的,不能夠重覆 使用parameterType屬性指明查詢時使用的參數類型,resultType屬性指明查詢返回的結果集類型 resultType="com.hanlu.mybatis02.entities.Student"就表示將查詢結果封裝成一個Student類的對象返回 Student類就是student表所對應的實體類 --> <!-- 根據id查詢得到一個user對象 --> <select id="selectStudentById" resultType="com.hanlu.mybatis02.entities.Student"> select * from student where id = #{id} </select> </mapper>
解釋
參考:https://www.cnblogs.com/hellokitty1/p/5216025.html
2.6、在配置文件中註冊映射文件
在配置文件conf.xml中註冊studentMapper.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"> <!-- 配置資料庫連接信息 --> <transactionManager type="JDBC"/> <dataSource type="POOLED"> <property name="driver" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/ssm"/> <property name="username" value="root"/> <property name="password" value="123456"/> </dataSource> </environment> </environments> <mappers> <!-- 註冊studentMapper.xml文件studentMapper.xml位於mapper這個目錄下,所以resource寫成mapper/studentMapper.xml--> <mapper resource="mapper/studentMapper.xml"/> </mappers> </configuration>
2.7、編寫數據訪問類
StudentDao.java,執行定義的select語句
package com.hanlu.mybatis02.dao; import com.hanlu.mybatis02.entities.Student; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactoryBuilder; import java.io.InputStream; public class StudentDao { public Student getStudentById(int id){ //使用類載入器載入mybatis的配置文件(它也載入關聯的映射文件) InputStream stream=StudentDao.class.getClassLoader().getResourceAsStream("conf.xml"); //構建sqlSession的工廠 SqlSessionFactory ssf=new SqlSessionFactoryBuilder().build(stream); //使用MyBatis提供的Resources類載入mybatis的配置文件(它也載入關聯的映射文件) //Reader reader = Resources.getResourceAsReader(resource); //構建sqlSession的工廠 //SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(reader); //創建能執行映射文件中sql的sqlSession SqlSession session=ssf.openSession(); /** * 映射sql的標識字元串, * com.hanlu.mybatis02.dao.studentMapper是studentMapper.xml文件中mapper標簽的namespace屬性的值, * selectStudentById是select標簽的id屬性值,通過select標簽的id屬性值就可以找到要執行的SQL */ Student student=session.selectOne("com.hanlu.mybatis02.dao.studentMapper.selectStudentById",1); return student; } public static void main(String[] args) { StudentDao dao=new StudentDao(); Student student=dao.getStudentById(1); System.out.println(student); } }
執行過程解釋:
1、mybatis配置
SqlMapConfig.xml,此文件作為mybatis的全局配置文件,配置了mybatis的運行環境等信息。
mapper.xml文件即sql映射文件,文件中配置了操作資料庫的sql語句。此文件需要在SqlMapConfig.xml中載入。
2、通過mybatis環境等配置信息構造SqlSessionFactory即會話工廠
3、由會話工廠創建sqlSession即會話,操作資料庫需要通過sqlSession進行。
4、mybatis底層自定義了Executor執行器介面操作資料庫,Executor介面有兩個實現,一個是基本執行器、一個是緩存執行器。
5、Mapped Statement也是mybatis一個底層封裝對象,它包裝了mybatis配置信息及sql映射信息等。mapper.xml文件中一個sql對應一個Mapped Statement對象,sql的id即是Mapped statement的id。
6、Mapped Statement對sql執行輸入參數進行定義,包括HashMap、基本類型、pojo,Executor通過Mapped Statement在執行sql前將輸入的java對象映射至sql中,輸入參數映射就是jdbc編程中對preparedStatement設置參數。
7、Mapped Statement對sql執行輸出結果進行定義,包括HashMap、基本類型、pojo,Executor通過Mapped Statement在執行sql後將輸出結果映射至java對象中,輸出結果映射過程相當於jdbc編程中對結果的解析處理過程。
參考:https://www.cnblogs.com/selene/p/4604605.html
2.8、編寫單元測試
import com.hanlu.mybatis02.dao.StudentDao; import com.hanlu.mybatis02.entities.Student; import org.junit.Test; import org.junit.Before; import org.junit.After; public class StudentDaoTest { @Before public void before() throws Exception { } @After public void after() throws Exception { } /** * Method: getStudentById(int id) */ @Test public void testGetStudentById() throws Exception { StudentDao dao=new StudentDao(); Student student=dao.getStudentById(1); System.out.println(student); } }
測試結果:
2.9、IDEA中Junit插件與生成測試類位置
打開IntelliJ IDEA工具,Alt+Ctrl+S,打開設置視窗,點擊進入Plugins。
從插件資源庫中搜索JunitGenerator V2.0版本
安裝此插件,重啟IDEA就可以了。
現在可通過此工具自動完成test類的生成了,在需要進行單元測試的類中按 Alt+Insert
選中你要創建測試用例的方法即可。
IntelliJ IDEA JUnit Generator自動創建測試用例到指定test目錄
1.打開File->Settings
2.搜索junit,找到JUnit Generator
3.Properties選項卡裡的Output Path為測試用例生成的目錄,修改為test目錄:
SOURCEPATH/../../test/java/SOURCEPATH/../../test/java/{PACKAGE}/${FILENAME}
4.切換到JUnit 4選項卡,可以修改生成測試用例的模板,比如類名、包名等
修改生成位置:
修改模板文件:
測試類生成目錄分析:
${SOURCEPATH}/test/${PACKAGE}/${FILENAME} $SOURCEPATH/../../test/java/{PACKAGE}/${FILENAME} |
對應的目錄結構為
${SOURCEPATH}是到src/main/java這一層
../是退到上一層目錄的意思,對著圖理解一下
三、基於XML映射實現完整數據訪問
MyBatis可以使用XML或註解作為映射器的描述,XML強大且可以解偶,註解方便且簡單。
因為每一個操作都需要先拿到會話,這裡先定義一個工具類以便復用:
會話工具類:
package com.hanlu.mybatis02.utils; import com.hanlu.mybatis02.dao.StudentDao; import com.hanlu.mybatis02.entities.Student; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactoryBuilder; import java.io.IOException; import java.io.InputStream; /** * @author HMY * @date 2018/10/17-17:07 */ /** * MyBatis 會話工具類 * */ public class SqlSessionFactoryUtil { /** * 獲得會話工廠 * * */ public static SqlSessionFactory getFactory(){ InputStream inputStream=null; SqlSessionFactory sqlSessionFactory=null; try { //載入conf.xml配置文件,轉換成輸入流 inputStream= StudentDao.class.getClassLoader().getResourceAsStream("conf.xml"); //根據配置文件的輸入流構造一個SQL會話工廠 sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); }finally { if (inputStream!=null){ try { inputStream.close(); } catch (IOException e) { e.printStackTrace(); } } } return sqlSessionFactory; } /** * 獲得sql會話,是否自動提交 * */ public static SqlSession openSession(boolean isAutoCommit){ return getFactory().openSession(isAutoCommit); } /** * 關閉會話 * */ public static void closeSession(SqlSession session){ if (session!=null){ session.close(); } } }
XML映射器studentMapper:
<?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.hanlu.mybatis02.studentMapper"> <select id="selectStudentById" resultType="student"> select id,name,sex from student where id = #{id} </select> <select id="selectStudentByName" parameterType="String" resultType="Student"> select id,name,sex from student where name LIKE '%${value}%' </select> <insert id="insertStudent" parameterType="student"> insert into student(name,sex) values (#{name},#{sex}) </insert> <update id="updateStudent" parameterType="student"> UPDATE student SET name=#{name},sex=#{sex} WHERE id=#{id} </update> <delete id="deleteStudent" parameterType="int"> DELETE FROM student WHERE id=#{id} </delete> </mapper>
數據訪問類StudentDao.java:
package com.hanlu.mybatis02.dao; import com.hanlu.mybatis02.entities.Student; import com.hanlu.mybatis02.utils.SqlSessionFactoryUtil; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactoryBuilder; import java.io.InputStream; import java.util.List; /** * @author HMY * @date 2018/10/17-10:20 */ public class StudentDao implements StudentMapper { public Student selectStudentById(int id) { Student entity = null; /* //載入conf.xml配置文件,轉換成輸入流 InputStream inputStream=StudentDao.class.getClassLoader().getResourceAsStream("conf.xml"); //根據配置文件的輸入流構造一個SQL會話工廠 SqlSessionFactory sqlSessionFactory=new SqlSessionFactoryBuilder().build(inputStream); //打開一個會話 SqlSession session = sqlSessionFactory.openSession();*/ //打開一個會話 SqlSession session = SqlSessionFactoryUtil.openSession(true); //查詢單個對象 entity = session.selectOne("com.hanlu.mybatis02.studentMapper.selectStudentById", id); //關閉會話 SqlSessionFactoryUtil.closeSession(session); return entity; } public List<Student> selectStudentByName(String name) { /*InputStream inputStream=StudentDao.class.getClassLoader().getResourceAsStream("conf.xml"); SqlSessionFactory sqlSessionFactory=new SqlSessionFactoryBuilder().build(inputStream); SqlSession session=sqlSessionFactory.openSession();*/ SqlSession session = SqlSessionFactoryUtil.openSession(true); List<Student> list = session.selectList("com.hanlu.mybatis02.studentMapper.selectStudentByName", name); SqlSessionFactoryUtil.closeSession(session); return list; } public int insertStudent(Student student) { int rows = 0; SqlSession session = SqlSessionFactoryUtil.openSession(true); rows = session.insert("com.hanlu.mybatis02.studentMapper.insertStudent", student); SqlSessionFactoryUtil.closeSession(session); return rows; } public int updateStudent(Student student) { int rows = 0; SqlSession session = SqlSessionFactoryUtil.openSession(true); rows = session.update("com.hanlu.mybatis02.studentMapper.updateStudent", student); SqlSessionFactoryUtil.closeSession(session); return rows; } public int deleteStudent(int id) { int rows = 0; SqlSession session = SqlSessionFactoryUtil.openSession(true); rows = session.delete("com.hanlu.mybatis02.studentMapper.updateStudent", id); SqlSessionFactoryUtil.closeSession(session); return rows; } }
單元測試:
package com.hanlu.mybatis02.dao; import com.hanlu.mybatis02.entities.Student; import org.junit.Assert; import org.junit.Test; import org.junit.Before; import org.junit.After; import java.util.List; /** * StudentDao Tester. * * @author <Authors name> * @since <pre>ʮ�� 17, 2018</pre> * @version 1.0 */ public class StudentDaoTest { StudentDao dao; @Before public void before() throws Exception { dao=new StudentDao(); } @After public void after() throws Exception { } /** * * Method: selectStudentById(int id) * */ @Test public void testSelectStudentById() throws Exception { //TODO: Test goes here... } /** * * Method: selectStudentByName(String name) * */ @Test public void testSelectStudentByName() throws Exception { List<Student> list=dao.selectStudentByName("i"); System.out.println(list); Assert.assertNotNull(list); } @Test public void testInsertStudent() throws Exception { Student student=new Student(); student.setName("瑪麗"); student.setSex("girl"); Assert.assertEquals(1,dao.insertStudent(student)); } @Test public void testUpdateStudent() throws Exception { Student student=dao.selectStudentById(5); student.setSex("boy"); student.setName("Mario"); Assert.assertEquals(1,dao.updateStudent(student)); } }
參考映射文件1:
<?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:需要和mapper介面的全限定名一致 --> <mapper namespace="com.san.mapper.UserMapper"> <!-- 通過ID查詢用戶 --> <select id="findUserById" parameterType="int" resultType="user"> select * from user where id=#{id} </select> <!-- 定義sql片段 --> <!-- sql片段內,可以定義sql語句中的任何內容 --> <!-- sql片段內,最好不要使用where和select關鍵字聲明在內 --> <sql id="whereClause"> <!-- if標簽:對輸入的參數進行判斷 --> <!-- test標簽:指定判斷的表達式 --> <if test="user!=null"> <!-- 判斷用戶名不為空 --> <if test="user.username!=null and user.username!=''"> and username like '%${user.username}%' </if> <!-- 判斷性別不為空 --> <if test="user.sex!=null and user.sex!=''"> and sex=#{user.sex} </if> </if> <!-- 判斷集合 --> <!-- collection:表示pojo中集合屬性的屬性名稱 --> <!-- item:為遍歷出的結果聲明一個變數名稱 --> <!-- open:遍歷開始時,需要拼接的字元串 --> <!-- close:遍歷結束時,需要拼接的字元串 --> <!-- separator:遍歷中間需要拼接的字元串 --> <if test="idList!=null"> and id in <