1. 程式人生 > >MyBatis學習總結(一)——ORM概要與MyBatis快速起步

MyBatis學習總結(一)——ORM概要與MyBatis快速起步

管理 stat prim aot 驅動 單元測試 build sta 環境

目錄

  • 一、ORM
    • 1.1、ORM簡介
    • 1.2、ORM的概念
    • 1.3、ORM的優缺點
  • 二、MyBatis
    • 2.1、MyBatis的特點
    • 2.2、MyBatis工作流程
    • 2.3、MyBatis架構
    • 2.4、MyBatis的主要成員如層次結構
    • 2.5、學習資源
  • 三、MyBatis快速入門示例
    • 3.1、在IDEA中創建項目
    • 3.2、添加依賴
    • 3.3、添加Mybatis配置文件
    • 3.4、定義表所對應的實體類
    • 3.5、定義操作Student表的sql映射文件
    • 3.6、在配置文件中註冊映射文件
    • 3.7、編寫數據訪問類
    • 3.8、編寫單元測試
    • 3.9、IDEA中Junit插件與生成測試類位置
  • 四、基於XML映射實現完整數據訪問
  • 五、基於註解映射實完整數據訪問
  • 六、說明與註意事項
    • 6.1、parameterType和resultType的區別
    • 6.2、#{}和${}的區別
    • 6.3、selectOne()和selectList()的區別
    • 6.4、映射器選擇XML還是註解
  • 七、視頻
  • 八、示例
  • 九、作業

程序員應該將核心關註點放在業務上,而不應該將時間過多的浪費在CRUD中,多數的ORM框架都把增加、修改與刪除做得非常不錯了,然後數據庫中查詢無疑是使用頻次最高、復雜度大、與性能密切相關的操作,我們希望得到一種使用方便,查詢靈活的ORM框架,MyBatis可以滿足這些要求,MyBatis是一個支持普通SQL查詢,存儲過程和高級映射的優秀持久層框架,它也是SSM框架集成中的重要組成部分。

技術分享圖片

一、ORM

1.1、ORM簡介

ORM可以解決數據庫與程序間的異構性,比如在Java中我們使用String表示字符串,而Oracle中可使用varchar2,MySQL中可使用varchar,SQLServer可使用nvarchar。

對象關系映射(英語:Object Relational Mapping,簡稱ORM,或O/RM,或O/R mapping),用於實現面向對象編程語言裏不同類型系統的數據之間的轉換。簡單的說,ORM是通過使用描述對象和數據庫之間映射的元數據,將程序中的對象與關系數據庫相互映射。

沒有ORM時我們是這樣完成對象與關系數據庫之間的映射的:

技術分享圖片
            //將執行的sql
            String sql = "SELECT name, id, age, password FROM users";
            //創建命令對象
            preparedStatement = connection.prepareStatement(sql);
            //執行並獲得結果集
            resultSet = preparedStatement.executeQuery();
            //遍歷結果集,將數據庫中的數據轉換成Java中的對象
            while(resultSet.next()){
                String name = resultSet.getString("name");
                int id = resultSet.getInt("id");
                int age = resultSet.getInt("age");
                String password = resultSet.getString("password");
                User entity= new User(name,id,age,password);
                Users.add(entity);
            }
技術分享圖片

這種方案存在以下不足:

持久化層缺乏彈性。一旦出現業務需求的變更,就必須修改持久化層的接口

持久化層同時與域模型與關系數據庫模型綁定,不管域模型還是關系數據庫模型發生變化,都要修改持久化曾的相關程序代碼,增加了軟件的維護難度。

將和數據庫交互(CRUD)的代碼硬編碼到JDBC程序中

實現見狀的持久化層需要高超的開發技巧,而且編程量很大

對象模型和關系模型的轉換非常麻煩

技術分享圖片

ORM(O/R Mapping:對象關系映射):

一種將內存中的對象保存到關系型數據庫中的技術

負責實體域對象的持久化,封裝數據庫訪問細節

ORM提供了實現持久化層的另一種模式,采用映射元數據(XML)來描述對象-關系的映射細節,使得ORM中間件能在任何一個Java應用的業務邏輯層和數據庫之間充當橋梁。

ORM提供了實現持久化層的另一種模式,它采用映射元數據來描述對象關系的映射,使得ORM中間件能在任何一個應用的業務邏輯層和數據庫層之間充當橋梁。

技術分享圖片

Java典型的ORM中有:

hibernate:全自動的框架,強大、復雜、笨重、學習成本較高

技術分享圖片

Mybatis:半自動的框架(懂數據庫的人 才能操作) 必須要自己寫sql

JPA:JPA全稱Java Persistence API、JPA通過JDK 5.0註解或XML描述對象-關系表的映射關系,是Java自帶的框架

ORM的方法論基於三個核心原則:

  · 簡單:以最基本的形式建模數據。

  · 傳達性:數據庫結構被任何人都能理解的語言文檔化。

  · 精確性:基於數據模型創建正確標準化了的結構。

1.2、ORM的概念

讓我們從O/R開始。字母O起源於"對象"(Object),而R則來自於"關系"(Relational)。幾乎所有的程序裏面,都存在對象和關系數據庫。在業務邏輯層和用戶界面層中,我們是面向對象的。當對象信息發生變化的時候,我們需要把對象的信息保存在關系數據庫中。

當你開發一個應用程序的時候(不使用O/R Mapping),你可能會寫不少數據訪問層的代碼,用來從數據庫保存,刪除,讀取對象信息,等等。你在DAL中寫了很多的方法來讀取對象數據,改變狀態對象等等任務。而這些代碼寫起來總是重復的。

技術分享圖片

ORM解決的主要問題是對象關系的映射。域模型和關系模型分別是建立在概念模型的基礎上的。域模型是面向對象的,而關系模型是面向關系的。一般情況下,一個持久化類和一個表對應,類的每個實例對應表中的一條記錄,類的每個屬性對應表的每個字段。

技術分享圖片

將關系數據庫中表中的記錄映射成為對象,以對象的形式展現,程序員可以把對數據庫的操作轉化為對對象的操作。

技術分享圖片

因此ORM的目的是為了方便開發人員以面向對象的思想來實現對數據庫的操作。

1.3、ORM的優缺點

優點:

1.提高了開發效率。由於ORM可以自動對Entity對象與數據庫中的Table進行字段與屬性的映射,所以我們實際可能已經不需要一個專用的、龐大的數據訪問層。

2.ORM提供了對數據庫的映射,不用sql直接編碼,能夠像操作對象一樣從數據庫獲取數據。

缺點:

犧牲程序的執行效率和會固定思維模式,降低了開發的靈活性。

從系統結構上來看,采用ORM的系統一般都是多層系統,系統的層次多了,效率就會降低。ORM是一種完全的面向對象的做法,而面向對象的做法也會對性能產生一定的影響。

在我們開發系統時,一般都有性能問題。性能問題主要產生在算法不正確和與數據庫不正確的使用上。ORM所生成的代碼一般不太可能寫出很高效的算法,在數據庫應用上更有可能會被誤用,主要體現在對持久對象的提取和和數據的加工處理上,如果用上了ORM,程序員很有可能將全部的數據提取到內存對象中,然後再進行過濾和加工處理,這樣就容易產生性能問題。

在對對象做持久化時,ORM一般會持久化所有的屬性,有時,這是不希望的。

但ORM是一種工具,工具確實能解決一些重復,簡單的勞動。這是不可否認的。但我們不能指望工具能一勞永逸的解決所有問題,有些問題還是需要特殊處理的,但需要特殊處理的部分對絕大多數的系統,應該是很少的。

二、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對象)映射成數據庫中的記錄。

2.1、MyBatis的特點

簡單易學:本身就很小且簡單。沒有任何第三方依賴,最簡單安裝只要兩個jar文件+配置幾個sql映射文件易於學習,易於使用,通過文檔和源代碼,可以比較完全的掌握它的設計思路和實現。

靈活:mybatis不會對應用程序或者數據庫的現有設計強加任何影響。 sql寫在xml裏,便於統一管理和優化。通過sql基本上可以實現我們不使用數據訪問框架可以實現的所有功能,或許更多。

解除sql與程序代碼的耦合:通過提供DAO層,將業務邏輯和數據訪問邏輯分離,使系統的設計更清晰,更易維護,更易單元測試。sql和代碼的分離,提高了可維護性。

提供映射標簽,支持對象與數據庫的ORM字段關系映射

提供對象關系映射標簽,支持對象關系組建維護

提供XML標簽,支持編寫動態sql。

2.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

2.3、MyBatis架構

Mybatis的功能架構分為三層:

技術分享圖片

API接口層:提供給外部使用的接口API,開發人員通過這些本地API來操縱數據庫。接口層一接收到調用請求就會調用數據處理層來完成具體的數據處理。

數據處理層:負責具體的SQL查找、SQL解析、SQL執行和執行結果映射處理等。它主要的目的是根據調用的請求完成一次數據庫操作。

基礎支撐層:負責最基礎的功能支撐,包括連接管理、事務管理、配置加載和緩存處理,這些都是共用的東西,將他們抽取出來作為最基礎的組件。為上層的數據處理層提供最基礎的支撐。

技術分享圖片

2.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語句以及相應的參數信息

層次結構:

技術分享圖片

更多請參考:《深入理解mybatis原理》 MyBatis的架構設計以及實例分析

2.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快速入門示例

3.1、在IDEA中創建項目

普通java項目或者是Maven項目都可以,如下圖所示:

  技術分享圖片

3.2、添加依賴

下載地址:https://github.com/mybatis/mybatis-3/releases

網盤下載:http://pan.baidu.com/s/1hrB1guo

技術分享圖片

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.zhangguo.mybatis01</groupId>
    <artifactId>MyBatis01</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>
        </dependency>
    </dependencies>
</project>
技術分享圖片

3、創建數據庫和表,針對MySQL數據庫

SQL腳本如下:

技術分享圖片
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數據庫中執行,完成創建數據庫和表的操作,如下:

  技術分享圖片

表中的數據如下:

技術分享圖片

3.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/nfmall"/>
                <property name="username" value="root"/>
                <property name="password" value="uchr@123"/>
            </dataSource>
        </environment>
    </environments>
    <mappers>
        <mapper resource="mapper/studentMapper.xml"/>
    </mappers>
</configuration>
技術分享圖片

解釋

技術分享圖片

3.4、定義表所對應的實體類

Student實體類的代碼如下:

技術分享圖片
package com.zhangguo.mybatis01.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 + ‘\‘‘ +
                ‘}‘;
    }
}
技術分享圖片

3.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.zhangguo.mybatis01.dao.studentMapper"就是com.zhangguo.mybatis01.dao(包名)+studentMapper(studentMapper.xml文件去除後綴)
-->
<mapper namespace="com.zhangguo.mybatis01.dao.studentMapper">
    <!-- 在select標簽中編寫查詢的SQL語句, 設置select標簽的id屬性為selectStudentById,id屬性值必須是唯一的,不能夠重復
 使用parameterType屬性指明查詢時使用的參數類型,resultType屬性指明查詢返回的結果集類型
resultType="com.zhangguo.mybatis01.entities.Student"就表示將查詢結果封裝成一個Student類的對象返回
Student類就是student表所對應的實體類
-->
    <!-- 
    根據id查詢得到一個user對象
 -->
    <select id="selectStudentById" resultType="com.zhangguo.mybatis01.entities.Student">
        select * from student where id = #{id}
    </select>
</mapper>
技術分享圖片

解釋

技術分享圖片

參考:https://www.cnblogs.com/hellokitty1/p/5216025.html

3.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/nfmall"/>
                <property name="username" value="root"/>
                <property name="password" value="uchr@123"/>
            </dataSource>
        </environment>
    </environments>
    <mappers>
        <!-- 註冊studentMapper.xml文件studentMapper.xml位於mapper這個目錄下,所以resource寫成mapper/studentMapper.xml-->
        <mapper resource="mapper/studentMapper.xml"/>
    </mappers>
</configuration>
技術分享圖片

3.7、編寫數據訪問類

StudentDao.java,執行定義的select語句

技術分享圖片
package com.zhangguo.mybatis01.dao;

import com.zhangguo.mybatis01.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.zhangguo.mybatis01.dao.studentMapper是studentMapper.xml文件中mapper標簽的namespace屬性的值,
         * selectStudentById是select標簽的id屬性值,通過select標簽的id屬性值就可以找到要執行的SQL
         */
        Student student=session.selectOne("com.zhangguo.mybatis01.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

3.8、編寫單元測試

技術分享圖片
import com.zhangguo.mybatis01.dao.StudentDao;
import com.zhangguo.mybatis01.entities.Student;
import org.junit.Test;
import org.junit.Before;
import org.junit.After;

/**
 * StudentDao Tester.
 *
 * @author <Authors name>
 * @version 1.0
 * @since <pre>09/24/2018</pre>
 */
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);
    }


} 
技術分享圖片

測試結果:

技術分享圖片

3.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.zhangguo.mybatis02.utils;

import com.zhangguo.mybatis02.dao.StudentDao;
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;

/**
 * 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.zhangguo.mybatis02.mapper.studentMapper">
    <select id="selectStudentById" resultType="com.zhangguo.mybatis02.entities.Student">
        SELECT id,name,sex from student where id=#{id}
    </select>

    <select id="selectStudentsByName" parameterType="String" resultType="com.zhangguo.mybatis02.entities.Student">
      SELECT id,name,sex from student where name like ‘%${value}%‘;
    </select>

    <insert id="insertStudent" parameterType="com.zhangguo.mybatis02.entities.Student">
        insert into student(name,sex) VALUES(#{name},‘${sex}‘)
    </insert>

    <update id="updateStudent" parameterType="com.zhangguo.mybatis02.entities.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.zhangguo.mybatis02.dao;

import com.zhangguo.mybatis02.entities.Student;
import com.zhangguo.mybatis02.utils.SqlSessionFactoryUtil;
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;
import java.util.List;

public class StudentDao {

    /**
     * 根據學生編號獲得學生對象
     */
    public Student selectStudentById(int id) {
        Student entity = null;
        //打開一個會話
        SqlSession session = SqlSessionFactoryUtil.openSession(true);

        //查詢單個對象,指定參數為3
        entity = session.selectOne("com.zhangguo.mybatis02.mapper.studentMapper.selectStudentById", id);

        //關閉
        SqlSessionFactoryUtil.closeSession(session);

        return entity;
    }


    /**
     * 根據學生姓名獲得學生集合
     */
    public List<Student> selectStudentsByName(String name) {
        List<Student> entities = null;
        //打開一個會話
        SqlSession session = SqlSessionFactoryUtil.openSession(true);
        //查詢多個對象,指定參數
        entities = session.selectList("com.zhangguo.mybatis02.mapper.studentMapper.selectStudentsByName", name);
        //關閉
        SqlSessionFactoryUtil.closeSession(session);
        return entities;
    }


    /**
     * 添加學生
     */
    public int insertStudent(Student entity) {
        //影響行數
        int rows=0;
        //打開一個會話
        SqlSession session = SqlSessionFactoryUtil.openSession(true);
        //執行添加
        rows = session.insert("com.zhangguo.mybatis02.mapper.studentMapper.insertStudent", entity);
        //關閉
        SqlSessionFactoryUtil.closeSession(session);
        return rows;
    }

    /**
     * 更新學生
     */
    public int updateStudent(Student entity) {
        //影響行數
        int rows=0;
        //打開一個會話
        SqlSession session = SqlSessionFactoryUtil.openSession(true);
        //執行更新
        rows = session.update("com.zhangguo.mybatis02.mapper.studentMapper.updateStudent", entity);
        //關閉
        SqlSessionFactoryUtil.closeSession(session);
        return rows;
    }

    /**
     * 刪除學生
     */
    public int deleteStudent(int id) {
        //影響行數
        int rows=0;
        //打開一個會話
        SqlSession session = SqlSessionFactoryUtil.openSession(true);
        //執行刪除
        rows = session.delete("com.zhangguo.mybatis02.mapper.studentMapper.deleteStudent", id);
        //關閉
        SqlSessionFactoryUtil.closeSession(session);
        return rows;
    }

}
技術分享圖片

單元測試:

技術分享圖片
package com.zhangguo.mybatis02.dao;

import com.zhangguo.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>
 * @version 1.0
 * @since <pre>09/26/2018</pre>
 */
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 {
        Student entity=dao.selectStudentById(1);
        System.out.println(entity);
        Assert.assertNotNull(entity);
    }

    /**
     * Method: selectStudentsByName(String name)
     */
    @Test
    public void testSelectStudentsByName() throws Exception {
        List<Student> students=dao.selectStudentsByName("i");
        System.out.println(students);
        Assert.assertNotNull(students);
    }

    /**
     * Method: insertStudent
     */
    @Test
    public void testInsertStudent() throws Exception {
        Student entity=new Student();
        entity.setName("瑪麗");
        entity.setSex("girl");

        Assert.assertEquals(1,dao.insertStudent(entity));
    }

    /**
     * Method: updateStudent
     */
    @Test
    public void testUpdateStudent() throws Exception {
        Student entity=dao.selectStudentById(3);
        entity.setName("馬力");
        entity.setSex("boy");

        Assert.assertEquals(1,dao.updateStudent(entity));
    }

    /**
     * Method: deleteStudent
     */
    @Test
    public void testDeleteStudent() throws Exception {
        Assert.assertEquals(1,dao.deleteStudent(2));
    }
} 
技術分享圖片

測試結果:

技術分享圖片

參考映射文件1:

技術分享圖片 View Code

參考映射文件2:

技術分享圖片 View Code

五、基於註解映射實完整數據訪問

映射器,StudentMapper接口:

技術分享圖片
package com.zhangguo.mybatis02.dao;

import com.zhangguo.mybatis02.entities.Student;
import org.apache.ibatis.annotations.Delete;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.annotations.Update;

import java.util.List;

public interface StudentMapper {
    /**
     * 根據學生編號獲得學生對象
     */
    @Select("select id,name,sex from student where id=#{id}")
    Student selectStudentById(int id);

    /**
     * 根據學生姓名獲得學生集合
     */
    @Select("SELECT id,name,sex from student where name like ‘%${value}%‘")
    List<Student> selectStudentsByName(String name);

    /**
     * 添加學生
     */
    @Insert("insert into student(name,sex) values(#{name},#{sex})")
    int insertStudent(Student entity);

    /**
     * 更新學生
     */
    @Update("update student set name=#{name},sex=#{sex} where id=#{id}")
    int updateStudent(Student entity);

    /**
     * 刪除學生
     */
    @Delete("delete from student where id=#{id}")
    int deleteStudent(int id);
}
技術分享圖片

MyBatis配置文件:

技術分享圖片
<?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://127.0.0.1:3306/nfmall?useUnicode=true&amp;characterEncoding=UTF-8"/>
                <property name="username" value="root"/>
                <property name="password" value="uchr@123"/>
            </dataSource>
        </environment>
    </environments>
    <mappers>
        <!--<mapper resource="mapper/studentMapper.xml"/>-->
        <mapper class="com.zhangguo.mybatis02.dao.StudentMapper"></mapper>
    </mappers>
</configuration>
技術分享圖片

StudentDaoAnno.java實現對student的數據訪問:

技術分享圖片
package com.zhangguo.mybatis02.dao;

import com.zhangguo.mybatis02.entities.Student;
import com.zhangguo.mybatis02.utils.SqlSessionFactoryUtil;
import org.apache.ibatis.session.SqlSession;

import java.util.List;

public class StudentDaoAnno implements StudentMapper {

    /**
     * 根據學生編號獲得學生對象
     */
    public Student selectStudentById(int id) {
        Student entity = null;
        //打開一個會話
        SqlSession session = SqlSessionFactoryUtil.openSession(true);

        //獲得一個映射器
        StudentMapper mapper=session.getMapper(StudentMapper.class);

        //查詢單個對象,指定參數為3
        entity = mapper.selectStudentById(id);

        //關閉
        SqlSessionFactoryUtil.closeSession(session);

        return entity;
    }


    /**
     * 根據學生姓名獲得學生集合
     */
    public List<Student> selectStudentsByName(String name) {
        List<Student> entities = null;
        //打開一個會話
        SqlSession session = SqlSessionFactoryUtil.openSession(true);

        //獲得一個映射器
        StudentMapper mapper=session.getMapper(StudentMapper.class);

        //查詢多個對象,指定參數
        entities =mapper.selectStudentsByName(name);
        //關閉
        SqlSessionFactoryUtil.closeSession(session);
        return entities;
    }


    /**
     * 添加學生
     */
    public int insertStudent(Student entity) {
        //影響行數
        int rows=0;
        //打開一個會話
        SqlSession session = SqlSessionFactoryUtil.openSession(true);

        //獲得一個映射器
        StudentMapper mapper=session.getMapper(StudentMapper.class);

        //執行添加
        rows = mapper.insertStudent(entity);
        //關閉
        SqlSessionFactoryUtil.closeSession(session);
        return rows;
    }

    /**
     * 更新學生
     */
    public int updateStudent(Student entity) {
        //影響行數
        int rows=0;
        //打開一個會話
        SqlSession session = SqlSessionFactoryUtil.openSession(true);

        //獲得一個映射器
        StudentMapper mapper=session.getMapper(StudentMapper.class);

        //執行更新
        rows =mapper.updateStudent(entity);
        //關閉
        SqlSessionFactoryUtil.closeSession(session);
        return rows;
    }

    /**
     * 刪除學生
     */
    public int deleteStudent(int id) {
        //影響行數
        int rows=0;
        //打開一個會話
        SqlSession session = SqlSessionFactoryUtil.openSession(true);

        //獲得一個映射器
        StudentMapper mapper=session.getMapper(StudentMapper.class);

        //執行刪除
        rows = mapper.deleteStudent(id);
        //關閉
        SqlSessionFactoryUtil.closeSession(session);
        return rows;
    }

}
技術分享圖片

單元測試:

技術分享圖片
package com.zhangguo.mybatis02.dao;

import com.zhangguo.mybatis02.entities.Student;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;

import java.util.List;

/**
 * StudentDao Tester.
 *
 * @author <Authors name>
 * @version 1.0
 * @since <pre>09/26/2018</pre>
 */
public class StudentDaoAnnoTest {
    StudentMapper dao;
    @Before
    public void before() throws Exception {
        dao=new StudentDaoAnno();
    }

    @After
    public void after() throws Exception {
    }

    /**
     * Method: selectStudentById(int id)
     */
    @Test
    public void testSelectStudentById() throws Exception {
        Student entity=dao.selectStudentById(1);
        System.out.println(entity);
        Assert.assertNotNull(entity);
    }

    /**
     * Method: selectStudentsByName(String name)
     */
    @Test
    public void testSelectStudentsByName() throws Exception {
        List<Student> students=dao.selectStudentsByName("e");
        System.out.println(students);
        Assert.assertNotNull(students);
    }

    /**
     * Method: insertStudent
     */
    @Test
    public void testInsertStudent() throws Exception {
        Student entity=new Student();
        entity.setName("張小強");
        entity.setSex("boy");

        Assert.assertEquals(1,dao.insertStudent(entity));
    }

    /**
     * Method: updateStudent
     */
    @Test
    public void testUpdateStudent() throws Exception {
        Student entity=dao.selectStudentById(7);
        entity.setName("張美麗");
        entity.setSex("girl");

        Assert.assertEquals(1,dao.updateStudent(entity));
    }

    /**
     * Method: deleteStudent
     */
    @Test
    public void testDeleteStudent() throws Exception {
        Assert.assertEquals(1,dao.deleteStudent(7));
    }
} 
技術分享圖片

測試結果:

技術分享圖片

參考映射文件:

技術分享圖片 View Code

六、說明與註意事項

6.1、parameterType和resultType的區別

parameterType:在映射文件中通過parameterType指定輸入參數的類型。

resultType:在映射文件中通過resultType指定輸出結果的類型

6.2、#{}和${}的區別

#{}

#{}表示一個占位符號,#{}接收輸入參數,類型可以是簡單類型,pojo、hashmap;

如果接收簡單類型,#{}中可以寫成value或其它名稱;

#{}接收pojo對象值,通過OGNL讀取對象中的屬性值,通過屬性.屬性.屬性...的方式獲取對象屬性值。使用#{}意味著使用的預編譯的語句,即在使用jdbc時的preparedStatement,sql語句中如果存在參數則會使用?作占位符,我們知道這種方式可以防止sql註入,並且在使用#{}時形成的sql語句,已經帶有引號,例,select? * from table1 where id=#{id}? 在調用這個語句時我們可以通過後臺看到打印出的sql為:select * from table1 where id=‘2‘ 加入傳的值為2.也就是說在組成sql語句的時候把參數默認為字符串。

${}

表示一個拼接符號,會引用sql註入,所以不建議使用${};

${}接收輸入參數,類型可以是簡單類型,pojo、hashmap;

如果接收簡單類型,${}中只能寫成value;

${}接收pojo對象值,通過OGNL讀取對象中的屬性值,通過屬性.屬性.屬性...的方式獲取對象屬性值。

使用${}時的sql不會當做字符串處理,是什麽就是什麽,如上邊的語句:select * from table1 where id=${id} 在調用這個語句時控制臺打印的為:select * from table1 where id=2 ,假設傳的參數值為2
從上邊的介紹可以看出這兩種方式的區別,我們最好是能用#{}則用它,因為它可以防止sql註入,且是預編譯的,在需要原樣輸出時才使用${},如,
select * from ${tableName} order by ${id} 這裏需要傳入表名和按照哪個列進行排序 ,加入傳入table1、id 則語句為:select * from table1 order by id
如果是使用#{} 則變成了select * from ‘table1‘ order by ‘id‘ 我們知道這樣就不對了。

6.3、selectOne()和selectList()的區別

selectOne表示查詢出一條記錄進行映射。如果使用selectOne可以實現使用selectList也可以實現(list中只有一個對象),如果查詢結果為多條則會報錯。

selectList表示查詢出一個列表(多條記錄)進行映射,可以是0到n條記錄返回。

6.4、映射器選擇XML還是註解

以下是MyBatis官網對Mapper Annotations的解釋:

Mapper Annotations

Since the very beginning, MyBatis has been an XML driven framework. The configuration is XML based, and the Mapped Statements are defined in XML. With MyBatis 3, there are new options available. MyBatis 3 builds on top of a comprehensive and powerful Java based Configuration API. This Configuration API is the foundation for the XML based MyBatis configuration, as well as the new Annotation based configuration. Annotations offer a simple way to implement simple mapped statements without introducing a lot of overhead.

NOTE : Java Annotations are unfortunately limited in their expressiveness and flexibility. Despite a lot of time spent in investigation, design and trials, the most powerful MyBatis mappings simply cannot be built with Annotations – without getting ridiculous that is. C# Attributes (for example) do not suffer from these limitations, and thus MyBatis.NET will enjoy a much richer alternative to XML. That said, the Java Annotation based configuration is not without its benefits.

翻譯:

(最初MyBatis是基於XML驅動的框架。MyBatis的配置是基於XML的,語句映射也是用XML定義的。對於MyBatis3,有了新的可選方案。MyBatis3 是建立在全面且強大的Java配置API之上的。 該配置API是MyBatis基於XML配置的基礎,也是基於註解配置的基礎。註解提供了簡單的方式去實現簡單的映射語句,不需要花費大量的開銷。

註意:很不幸的是,java註解在表現和靈活性上存在限制。雖然在調研、設計和測試上花費了很多時間,但是最強大的MyBatis映射功能卻無法用註解實現。這沒有什麽可笑的。舉例來說,C#的特性就沒有這個限制,所以MyBatis.NET 能擁有一個功能豐富的多的XML替代方案。所以,Java基於註解的配置是依賴於其自身特性的。)

長遠來看建議選擇XML作為映射器

http://www.mybatis.org/mybatis-3/java-api.html

七、視頻

https://www.bilibili.com/video/av32447485/

八、示例

https://git.coding.net/zhangguo5/MyBatis02.git

九、作業

1、請使用MyBatis完成一個用戶管理的數據訪問功能,要求實現根據用戶名查詢用戶對象(id,username,password,name,email,state)功能,表中至少5個字段。

2、請分別使用XML與註解兩種方式實現對象用戶表(Users)的單條記錄查詢、多條記錄查詢、增加、修改與刪除功能,要求單元測試通過。

3、添加用戶成功後返回用戶的編號,而不是影響行數。(選作)

4、實現多個條件組合查詢,類似在電商平臺購物可以選擇0-n個條件,且可以自由組合。(選作)

5、實現分頁功能。(選作)

MyBatis學習總結(一)——ORM概要與MyBatis快速起步