1. 程式人生 > 實用技巧 >MyBatis介面代理方式實現Dao層

MyBatis介面代理方式實現Dao層

目錄

MyBatis介面代理方式實現Dao層

區別
1、selectlist和getMapper區別
//4.執行對映配置檔案中的sql語句,並接收結果
list = sqlSession.selectList("StudentMapper.selectAll");
//4.獲取StudentMapper介面的實現類物件
StudentMapper mapper = sqlSession.getMapper(StudentMapper.class); 
//5.通過實現類物件呼叫方法,接收結果
result = mapper.delete(id);

兩者其實並無區別,只不過StudentMapper利用代理物件操作sqlSession.selectList();

2、namespace的作用
<mapper namespace="StudentMapper">
<mapper namespace="com.zhu.mapper.StudentMapper">

1.完全限定名(比如“com.mypackage.MyMapper.selectAllThings”)將被直接查詢並且找到即用。

2.短名稱(比如“selectAllThings”)如果全域性唯一也可以作為一個單獨的引用。如果不唯一,有兩個或兩個以上的相同名稱(比如“com.foo.selectAllThings ”和“com.bar.selectAllThings”),那麼使用時就會收到錯誤報告說短名稱是不唯一的,這種情況下就必須使用完全限定名。

Mybatis中namespace用於繫結dao介面,dao介面的方法對應mapper中的sql語名

介面代理方式 - 實現規則

1)對映配置檔案中的名稱空間必須和Dao層介面的全類名相同

2)對映配置檔案中的增刪改查的標籤id屬性必須和Dao層介面層的方法名相同

3)對映配置檔案中的增刪改查標籤的parameterType屬性必須和Dao層介面方法的引數相同

4)對映配置檔案中的增刪改查標籤的resultType屬性必須和Dao層介面方法的返回值相同

介面代理方式 - 程式碼實現

1)刪除mapper層介面的實現類

2)修改對映配置檔案

3)修改service層介面的實現類,採用介面代理方式實現功能

在寫StudentMapper.java時

注意

1、resultType="student" 方法的返回值為List<Student>

2、parameterType="student"方法的引數為Student student

3、如何取出引數中的值 #{}

· MyBatisConfig.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!--MyBatis的DTD約束-->
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <properties resource="jdbc.properties"/>
    <settings>
        <setting name="logImpl" value="log4j"/>
    </settings>
  <!--預設名為類名首字母小寫-->
    <typeAliases>
        <package name="com.zhu.bean"/>
    </typeAliases>
    <environments default="mysql">
        <!--environment配置資料庫環境 id 屬性唯一標識 -->
        <environment id="mysql">
            <!-- transactionManager事務管理。type屬性,採用JDBC預設的事務-->
            <transactionManager type="JDBC"></transactionManager>
            <!--dataSource資料來源資訊 type屬性 連線池 MyBatis預設有三種資料來源-->
            <dataSource type="POOLED">
                <!--property獲取資料庫連線的配置資訊-->
                <property name="driver" value="${driver}"/>
                <property name="url" value="${url}"/>
                <property name="username" value="${username}"/>
                <property name="password" value="${password}"/>
            </dataSource>
        </environment>
    </environments>
    <!-- mappers引入對映配置檔案 -->
    <mappers>
        <!-- mapper 引入指定的對映配置檔案  resource屬性指定對映配置檔案的名稱 -->
        <mapper resource="StudentMapper.xml"/>
    </mappers>
</configuration>
· StudentMapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!--MyBatis的DTD約束-->
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--
    mapper: 核心根標籤
    namespace屬性: 名稱空間
-->
<mapper namespace="com.zhu.mapper.StudentMapper">
    <select id="selectAll" resultType="student">
        SELECT * FROM student
    </select>

    <insert id="insert" parameterType="student">
        INSERT INTO student VALUES (#{id},#{name},#{age});
    </insert>

    <delete id="delete" parameterType="int">
        DELETE FROM student WHERE id=#{id};
    </delete>
</mapper>
· controller - studentController.java
public class StudentController {
    private StudentService studentService = new StudentServiceImpl();

    @Test
    public void selectAll(){
        List<Student> list = studentService.selectAll();
        for (Student student : list) {
            System.out.println(student);
        }
    }
   @Test
    public void insert(){
        Student st = new Student(10,"路飛",23);
        Integer insert = studentService.insert(st);
        System.out.println(insert);
    }
    @Test
    public void delete(){
        Integer s = 3;
        Integer i = studentService.delete(3);
        System.out.println(i);
    }
}
· mapper - StudentMapper.java
public interface StudentMapper{
    /*
        注意: resultType 值為 student  指定結果對映的物件型別
                方法的返回值為   List<Student>
        resultType:
                1、基本型別  :resultType=基本型別
                2、List型別 :resultType=List中元素的型別
                3、Map型別  單條記錄:resultType =map
                          多條記錄:resultType =Map中value的型別
              方法的返回值可以為List<Student>
        parameterType:
                 基本資料型別:int,string,long,Date;
                 複雜資料型別:類和Map
                如何獲取引數中的值:
                               基本資料型別:#{value}或${value} 獲取引數中的值
                                複雜資料型別:#{屬性名}或${屬性名}  ,map中則是#{key}或${key}
     */
    public abstract List<Student> selectAll();

    Integer insert(Student student);

    Integer delete(Integer integer);
}
· service - StduentService.java - StudentServiceImpl.java
public interface StudentService {
    List<Student> selectAll();
    Integer insert(Student student);
    Integer delete(Integer integer);
}
public class StudentServiceImpl implements StudentService {
    @Override
    public List<Student> selectAll() {
        List<Student> list = null;
        SqlSession sqlSession = null;
        InputStream is = null;
        try {
            is = Resources.getResourceAsStream("MyBatisConfig.xml");
            SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(is);
            sqlSession = factory.openSession(true);

            //獲取StudentMapper介面的實現類物件
            //獲取對應的Mapper,讓對映器通過名稱空間和方法名稱找到對應的SQL,傳送給資料庫執行後返回結果。
            //操作資料庫主要是通過SQL語句,那麼只要找到SQL語句然後執行不就可以
            //sqlSession.getMapper()的內部產生了StudentMapper的實現類,那怎麼產生的呢?
            //動態代理
            /*
                被代理物件:真實的物件
                代理物件:記憶體中的一個物件
             */
            StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
            //StudentMapper studentMapper1 = new StudentServiceImpl();
            //return Proxy.newProxyInstance(this.mapperInterface.getClassLoader(), new Class[]{this.mapperInterface}, mapperProxy);
            /*
                類載入器:和被代理物件使用相同的類載入器
                介面型別Class陣列:和被代理物件使用相同介面
                代理規則:完成代理增強的功能
             */
            //通過代理物件呼叫方法,接收結果
            list = mapper.selectAll();
            
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            if(sqlSession != null) {
                sqlSession.close();
            }
            if(is != null) {
                try {
                    is.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return list;
    }
介面代理方式 - 問題
分析動態代理物件如何生成的?

通過動態代理開發模式,我們只編寫一個介面,不寫實現類,我們通過getMapper() 方法最終獲取到 org.apache.ibatis.binding.MapperProxy代理物件,然後執行功能,而這個代理物件正是 MyBatis使用了 JDK 的動態代理技術,幫助我們生成了代理實現類物件。從而可以進行相關持久化操作。

分析方法是如何執行的?

動態代理實現類物件在執行方法的時候最終呼叫了mapperMethod.execute() 方法,這個方法中通過 switch 語句根據操作型別來判斷是新增、修改、刪除、查詢操作,最後一步回到了MyBatis 最原生的 SqlSession方式來執行增刪改查。

小結
  • 原生:Dao ---> DaoImpl

  • 介面:Mapper ---> xxMapper.xml

  • 介面代理方式可以讓給我們只編寫介面即可,而實現類物件由MyBatis生成

  • 獲取動態代理物件

    SqlSession功能類中的getMapper(Mapper介面.class)方法。

MyBatis對映配置檔案 - 動態SQL

"if標籤" +
 <select id="selectCondition" resultType="student" parameterType="student">
        SELECT * FROM student
        <where>
                <if test="id != null">
                    id = #{id}
                </if>
                <if test="name != null">
                    AND name = #{name}
                </if>
                <if test="age != null">
                    AND age = #{age}
                </if>
        </where>
    </select>
"foreach"

foreach 用來迭代使用者傳過來的List或者Array

如:使用foreach元素來構建in子語句

collection : 指定要遍歷的集合名稱 list、array

item : 用來臨時存放迭代集合中當前元素的值,便於在foreach中使用

open :將該屬性指定的值新增到foreach迭代後拼出字串的開始

close :將該屬性指定的值新增到foreach迭代拼出字串的結尾

separator : 用來分割foreach元素迭代的每個元素

<!--SELECT * FROM student WHERE id IN (1,2);
-->
    <select id="selectByIds" resultType="student" parameterType="list">
        SELECT * from student
        <where>
            <foreach collection="list" open="id IN(" close=")" item="id" separator=",">
                #{id}
            </foreach>
        </where>
    </select>

    <select id="selectByIds2" resultType="student" parameterType="list">
        SELECT * from student where id IN
            <foreach collection="list" open="(" close=")" item="id" separator=",">
                #{id}
            </foreach>
    </select>

<!--如果是 selectById(List<Student> stus) 這種-->
    <select id="selectByIds3" resultType="student" parameterType="list">
        SELECT * from student
            <where>
                <foreach collection="list" open="id IN(" close=")" item="stu" separator=",">
                    #{stu.id}
                </foreach>
            </where>
    </select>

使用

StudentMapper.java

List<Student> selectByIds(List<Integer> list);

StudentService.java

List<Student> selectByIds(List<Integer> list);
sql片段抽取
<sql id="select">SELECT * FROM student</sql>

使用

<include refid="select"/>
小結
  • 動態 SQL 指的就是 SQL 語句可以根據條件或者引數的不同進行動態的變化。
  • :條件標籤。
  • :條件判斷的標籤。
  • :迴圈遍歷的標籤。
  • :抽取SQL 片段的標籤。
  • :引入SQL 片段的標籤。

MyBatis 核心配置檔案 - 分頁查詢

使用PageHelper

功能實現

(1) 匯入jar包 分頁外掛pagehelper-5.1.10.jar 和依賴的包 jsqlparser-3.1.jar

(2) 在核心配置檔案中整合分頁助手外掛

(3) 在測試類中使用分頁助手相關API實現分頁功能。

MyBatisConfig.xml

<!-- 環境配置順序
<!ELEMENT configuration (properties?, settings?, typeAliases?, typeHandlers?, objectFactory?,
objectWrapperFactory?, reflectorFactory?, plugins?, environments?, databaseIdProvider?, mappers?)>
-->
<plugins>
    <plugin interceptor="com.github.pagehelper.PageInterceptor"></plugin>
</plugins>
使用

StudentServiceImpl.java

PageHelper.startPage(1,3); 一定要寫在SQL執行之前

//通過分頁助手來實現分頁功能
// 第一頁:顯示3條資料
//PageHelper.startPage(1,3);
// 第二頁:顯示3條資料
//PageHelper.startPage(2,3);
// 第三頁:顯示3條資料
PageHelper.startPage(3,3);

//5.呼叫實現類的方法,接收結果
List<Student> list = mapper.selectAll();PageHelper.startPage(0,1);

相關API

返回值 方法名 說明
long getTotal() 獲取總條數
int getPages() 獲取總頁數
int getPageNum() 獲取當前頁
int getPageSize() 獲取每頁顯示條數
int getPrePage() 獲取上一頁
int getNextPage() 獲取下一頁
boolean isIsFirstPage() 獲取是否是第一頁
boolean isIsLastPage() 獲取是否是最後一頁

MyBatis 多表操作

外來鍵欄位的建立是體現表與表關係的所在

  • 一對一 : 在任意一方建立外來鍵,關聯對方的主鍵

使用者基本資訊表 和 使用者詳細資訊表

  • 一對多 : 在多的一方建立外來鍵,關聯一的一方主鍵

店鋪表 和 商品表

使用者表 和 訂單表

  • 多對多 : 藉助中間表,中間表至少兩個欄位,分別關聯兩張表的主鍵

學生表 和 課程表

使用者表 和 興趣愛好表

多表操作 一對一

模型 : 人和身份證 、 一個人只有一張身份證

資料準備

CREATE DATABASE db2;

CREATE TABLE person(
	id INT PRIMARY KEY AUTO_INCREMENT,
	NAME VARCHAR(20),
	age INT
);
INSERT INTO person VALUES (NULL,'張三',23);
INSERT INTO person VALUES (NULL,'李四',24);
INSERT INTO person VALUES (NULL,'王五',25);

CREATE TABLE card(
	id INT PRIMARY KEY AUTO_INCREMENT,
	number VARCHAR(30),
	pid INT,
	CONSTRAINT cp_fk FOREIGN KEY (pid) REFERENCES person(id)
)
INSERT INTO card VALUES (NULL,'12345',1);
INSERT INTO card VALUES (NULL,'23456',2);
INSERT INTO card VALUES (NULL,'34567',3);

bean

Student.java

public class Person {
    private Integer id;     //主鍵id
    private String name;    //人的姓名
    private Integer age;    //人的年齡
}

Card.java

public class Card {
    private Integer id;
    private String number;
    private Person p;
}

OneToOneMapper.xml

<mapper namespace="com.mybatis.oneToOne.OneToOneMapper">
<!--
<resultMap>是Mybatis的結果集封裝
-->
<!--通過 <resultMap>: 配置欄位和物件屬性的對映關係標籤
    屬性
       id 屬性:resultMap 的唯一標識,此 id 值用於 select 元素 resultMap 屬性的引用。
    type 屬性:表示該 resultMap 的對映結果型別(通常是 Java 實體類)。
 子節點
       id 子節點:一般對應資料庫中該行的主鍵 id,設定此項可以提升 MyBatis 效能。
       配置主鍵對映關係標籤
    result 子節點:對映到 JavaBean 的某個 “簡單型別” 屬性,如基礎資料型別、包裝類等。
       配置非主鍵對映關係標籤
  子節點屬性
    column 屬性:表示從資料庫中查詢的欄位名或別名。表中欄位名稱
    property 屬性:表示查詢出來的欄位對應的值賦給實體物件的哪個屬性。實體物件變數名稱
說明:子節點 id 和 result 均可實現最基本的結果集對映,將列對映到簡單資料型別的屬性。
這兩者唯一不同的是:在比較物件例項時 id 將作為結果集的標識屬性。
這有助於提高總體效能,特別是應用快取和巢狀結果對映的時候。
而若要實現高階結果對映,就需要學習下面兩個配置項: association 和 collection。
    association:對映到 JavaBean 的某個 “複雜型別” 屬性,比如 JavaBean 類,
    即 JavaBean 內部巢狀一個複雜資料型別(JavaBean)屬性,這種情況就屬於複雜型別的關聯。
    但是需要注意: association 僅處理一對一的關聯關係。
     association:配置被包含物件的對映關係
        property:被包含物件的變數名
        javaType:被包含物件的資料型別
-->

        <resultMap id="oneToOne" type="card">
            <id column="cid" property="id"/>
            <result column="number" property="number"/>

            <association property="p" javaType="person">
                <id column="pid" property="id"/>
                <result column="name" property="name"/>
                <result column="age" property="age"/>
            </association>
        </resultMap>

    <select id="selectAll" resultMap="oneToOne">
            SELECT
                c.id cid,
                number,
                pid,
                NAME,
                age
            FROM
                card c , person p
            WHERE
                c.pid = p.id
    </select>
</mapper>

MyBatisConfig.xml

    <mappers>
        <!-- mapper 引入指定的對映配置檔案  resource屬性指定對映配置檔案的名稱 -->
       <mapper resource="com/mybatis/oneToOne/OneToOneMapper.xml"></mapper>
    </mappers>

測試

  //4.獲取OneToOneMapper介面的實現類物件
        OneToOneMapper mapper = sqlSession.getMapper(OneToOneMapper.class);

        //5.呼叫實現類的方法,接收結果
        List<Card> list = mapper.selectAll();

        //6.處理結果
        for (Card c : list) {
            System.out.println(c);
        }
多表操作 一對多

資料準備

多表操作  一對多  班級對學生
CREATE TABLE classes(
	id INT PRIMARY KEY AUTO_INCREMENT,
	NAME VARBINARY(20)

);
INSERT INTO classes VALUES (NULL,'s一班');
INSERT INTO classes VALUES (NULL,'s二班');

CREATE TABLE student(
	id INT PRIMARY KEY AUTO_INCREMENT,
	NAME VARCHAR(30),
	age INT,
	cid INT,
	CONSTRAINT cs_fk FOREIGN KEY (cid) REFERENCES classes(id)
);
INSERT INTO student VALUES (NULL,'張三',23,1);
INSERT INTO student VALUES (NULL,'李四',24,1);
INSERT INTO student VALUES (NULL,'王五',25,2);
INSERT INTO student VALUES (NULL,'趙六',26,2);

bean

public class Student {
    private Integer id;
    private String name;
    private Integer age;
}
public class Classes {
    private Integer id;
    private String name;
    private List<Student> students; //班級中所有學生物件
}

OneToManyMapper.xml

<mapper namespace="com.mybatis.oneToMany.OneToManyMapper">

    <resultMap id="oneToMany" type="classes">
        <id column="cid" property="id"/>
        <result column="cname" property="name"/>
        <!--
           collection:配置被包含的集合物件對映關係
           property:被包含物件的變數名
           ofType:被包含物件的實際資料型別
        -->
       <collection property="students" ofType="student">
           <id column="sid" property="id"/>
           <result column="sname" property="name"/>
           <result column="sage" property="age"/>
       </collection>
    </resultMap>
    <select id="selectAll" resultMap="oneToMany">
            SELECT
                c.id cid,
                c.name cname,
                s.id sid,
                s.name sname,
                s.age sage
            FROM
                classes c,student s
            WHERE
                c.id=s.cid
    </select>

測試

       //4.獲取OneToManyMapper介面的實現類物件
        OneToManyMapper mapper = sqlSession.getMapper(OneToManyMapper.class);

        //5.呼叫實現類的方法,接收結果
        List<Classes> classes = mapper.selectAll();

        //6.處理結果
for (Classes cls : classes) {
    System.out.println(cls.getId() + "," + cls.getName());
    List<Student> students = cls.getStudents();
    for (Student student : students) {
        System.out.println("\t" + student);
    }
 }
多表操作 多對多

資料準備

學生和課程 一個學生可以選擇多門課程 一個課程可以被多個學生選擇

CREATE TABLE course(
	id INT PRIMARY KEY AUTO_INCREMENT,
	NAME VARCHAR(20)
);

INSERT INTO course VALUES (NULL,'語文');
INSERT INTO course VALUES (NULL,'數學');

CREATE TABLE stu_cr(
	id INT PRIMARY KEY AUTO_INCREMENT,
	sid INT,
	cid INT,
	CONSTRAINT sc_fk1 FOREIGN KEY (sid) REFERENCES student(id),
	CONSTRAINT sc_fk2 FOREIGN KEY (cid) REFERENCES course(id)
);

INSERT INTO stu_cr VALUES (NULL,1,1);
INSERT INTO stu_cr VALUES (NULL,1,2);
INSERT INTO stu_cr VALUES (NULL,2,1);
INSERT INTO stu_cr VALUES (NULL,2,2);

bean

Course.java

public class Course {
    private Integer id;
    private String name;
}

Student.java

public class Student {
    private Integer id;
    private String name;
    private Integer age;

    private List<Course> courses;  //學生所選的課程集合
}

ManyToManyMapper.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.mybatis.manyToMany.ManyToManyMapper">
    <resultMap id="ManyToMany" type="student">
        <id column="sid" property="id"/>
        <result column="sname" property="name"/>
        <result column="sage" property="age"/>
<!--    <collection>:配置被包含集合物件的對映關係標籤。
               屬性:
                   property 屬性:被包含集合物件的變數名
                   ofType 屬性:集合中儲存的物件資料型別-->
        <collection property="courses" ofType="course">
            <id column="cid" property="id"/>
            <result column="cname" property="name"/>
        </collection>
    </resultMap>
    <select id="selectAll" resultMap="ManyToMany">
        SELECT
            sc.sid,
            s.name sname,
            s.age sage,
            sc.cid,
            c.name cname
        FROM
            student s,course c,stu_cr sc
        WHERE
            sc.sid=s.id AND sc.cid=c.id
    </select>
</mapper>

處理結果

 //5.呼叫實現類的方法,接收結果
List<Student> students = mapper.selectAll();

        //6.處理結果
for (Student student : students) {
    System.out.println(student.getId() + "," + student.getName() + "," + student.getAge());
    List<Course> courses = student.getCourses();
    for (Course cours : courses) {
        System.out.println("\t" + cours);
    }
        }
小結
<!--一對一 所有標籤如下

            <resultMap>:配置欄位和物件屬性的對映關係標籤。 
               屬性:
                   id 屬性:唯一標識 
                   type 屬性:實體物件型別 

            <id>:配置主鍵對映關係標籤。 
            <result>:配置非主鍵對映關係標籤。 
               屬性:
                   column 屬性:表中欄位名稱 
                   property 屬性: 實體物件變數名稱
         (*)<association>:配置被包含物件的對映關係標籤。                            屬性:
                   property 屬性:被包含物件的變數名 
                   javaType 屬性:被包含物件的資料型別
-->


<!--多對多 & 一對多 所有標籤如下

            <resultMap>:配置欄位和物件屬性的對映關係標籤。 
               屬性:
                   id 屬性:唯一標識 
                   type 屬性:實體物件型別 

            <id>:配置主鍵對映關係標籤。 
            <result>:配置非主鍵對映關係標籤。 
               屬性:
                   column 屬性:表中欄位名稱 
                   property 屬性: 實體物件變數名稱
         (*)<collection>:配置被包含集合物件的對映關係標籤。 
               屬性:
                   property 屬性:被包含集合物件的變數名 
                   ofType 屬性:集合中儲存的物件資料型別
-->

擴充套件

針對結果集而建立對應的javabean類,來接收結果集資料,這種javabean在領域驅動模型中稱之為 : VO