Mybatis



Mybatis 介紹

ORM 介紹

ORM(Object Relational Mapping,對象關系映射):指的是持久化數據和實體對象的映射模式,為了解決面向對象與關系型數據庫存在的互不匹配的現象的技術。

image

image

什么是 Mybatis ?

  1. Mybatis 是一個優秀的基於 Java 的持久層框架,它內部封裝了 JDBC,使開發者只需要關注 SQL 語句本身,而不需要花費精力去處理加載驅動、創建連接、創建 statement 等繁雜的過程。

  2. 具體地說,Hibernate 是一個完全的 ORM 框架,而 Mybatis 是一個不完全的 ORM 框架。

  3. Mybatis 會將輸入參數、輸出結果進行映射。

MyBatis 官網地址

原生態 JDBC 操作的分析

原生態 JDBC 操作:

public static void main(String[] args) {

           Connection connection = null;
           PreparedStatement preparedStatement = null;
           ResultSet resultSet = null;

           try {
              // 1、加載數據庫驅動
              Class.forName("com.mysql.jdbc.Driver");
              // 2、通過驅動管理類獲取數據庫鏈接
              connection =  DriverManager.getConnection("jdbc:mysql://localhost:3306/mybatis?characterEncoding=utf-8", "root", "root");
              // 3、定義sql語句 ?表示占位符
           String sql = "select * from user where username = ?";
              // 4、獲取預處理statement
              preparedStatement = connection.prepareStatement(sql);
              // 5、設置參數,第一個參數為sql語句中參數的序號(從1開始),第二個參數為設置的參數值
              preparedStatement.setString(1, "王五");
              // 6、向數據庫發出sql執行查詢,查詢出結果集
              resultSet =  preparedStatement.executeQuery();
              // 7、遍歷查詢結果集
              while(resultSet.next()){
                  User user
                  System.out.println(resultSet.getString("id")+"  "+resultSet.getString("username"));
              }
           } catch (Exception e) {
              e.printStackTrace();
           }finally{
              //8、釋放資源
              if(resultSet!=null){
                  try {
                     resultSet.close();
                  } catch (SQLException e) {
                     // TODO Auto-generated catch block
                     e.printStackTrace();
                  }
              }
              if(preparedStatement!=null){
                  try {
                     preparedStatement.close();
                  } catch (SQLException e) {
                     // TODO Auto-generated catch block
                     e.printStackTrace();
                  }
              }
              if(connection!=null){
                  try {
                     connection.close();
                  } catch (SQLException e) {
                     // TODO Auto-generated catch block
                     e.printStackTrace();
                  }
              }
           }
       }

原生態 JDBC 操作的問題與解決方案:

  • 問題:

    1. 頻繁創建和銷毀數據庫的連接會造成系統資源浪費從而影響系統性能。
    2. sql 語句在代碼中硬編碼,如果要修改 sql 語句,就需要修改 java 代碼,造成代碼不易維護。
    3. 查詢操作時,需要手動將結果集中的數據封裝到實體對象中。
    4. 增刪改查操作需要參數時,需要手動將實體對象的數據設置到 sql 語句的占位符。
  • 對應的解決方案:

    1. 使用數據庫連接池初始化連接資源。
    2. 將 sql 語句抽取到配置文件中。
    3. 使用反射、內省等底層技術,將實體與表進行屬性與字段的自動映射。

Mybatis 框架原理

Mybatis 通過 xml 或注解的方式將要執行的各種 statement 配置起來,並將 Java 對象和 statement 中 SQL 的動態參數進行映射,生成最終執行的 SQL 語句。最后 Mybatis 框架執行 SQL 並將結果映射為 Java 對象並返回。

image
image


MyBatis API

Resources(加載資源的工具類)

核心方法:

image

SqlSessionFactoryBuilder(構建器)

SqlSessionFactoryBuilder:獲取 SqlSessionFactory 工廠對象的功能類

核心方法:

image

SqlSessionFactory(工廠對象)

SqlSessionFactory:獲取 SqlSession 構建者對象的工廠接口

核心方法:

image

SqlSession 會話對象

SqlSession:構建者對象接口,用於執行 SQL、管理事務、接口代理

SqlSession 實例在 MyBatis 中是非常強大的一個類,在這里能看到所有執行語句、提交或回滾事務和獲取映射器實例的方法。

image

Mybatis 入門案例

MyBatis 開發步驟:

  1. 添加 MyBatis 的 jar 包
  2. 創建 Student 數據表
  3. 編寫 Student 實體類
  4. 編寫映射文件 StudentMapper.xml
  5. 編寫核心文件 MyBatisConfig.xml
  6. 編寫測試類

環境搭建

1)導入 MyBatis 的 jar 包

  • mysql-connector-java-5.1.37-bin.jar
  • mybatis-3.5.3.jar
  • log4j-1.2.17.jar

或 maven 依賴:

<!-- mybatis環境 -->
<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis</artifactId>
    <version>3.5.3</version>
</dependency>
<!-- mysql環境 -->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>8.0.11</version>
</dependency>

2)創建 student 數據表

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

INSERT INTO student VALUES (NULL, '張三', 23);
INSERT INTO student VALUES (NULL, '李四', 24);
INSERT INTO student VALUES (NULL, '王五', 25);

3)編寫 Student 實體類

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

    public Student() {
    }

    public Student(Integer id, String name, Integer age) {
        this.id = id;
        this.name = name;
        this.age = age;
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Student{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

4)JDBC 配置文件

Mysql 5.X:

db.driver=com.mysql.jdbc.Driver
db.url=jdbc:mysql://localhost:3306/world
db.username=root
db.password=admin

Mysql 8.X:

db.driver=com.mysql.cj.jdbc.Driver
db.url=jdbc:mysql://localhost:3306/world?serverTimezone=UTC
db.username=root
db.password=admin

Mybatis 全局配置文件

全局配置文件包含了 MyBatis 全局的設置和屬性信息,如數據庫的連接、事務、連接池信息等。

全局配置文件可自定義命名,其配置內容和順序如下(順序不能亂):

  • Properties(屬性)
  • Settings(全局參數設置)
  • typeAliases(類型別名)
  • typeHandlers(類型處理器)
  • objectFactory(對象工廠)
  • plugins(插件)
  • environments(環境信息集合)
    • environment(單個環境信息)
      • transactionManager(事務)
      • dataSource(數據源)
  • mappers(映射器)

示例:src 目錄下的 MyBatisConfig.xml

  • jdbc.properties
db.driver=com.mysql.cj.jdbc.Driver
db.url=jdbc:mysql://localhost:3306/world?serverTimezone=UTC
db.username=root
db.password=admin
  • 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"/>
    <!--
     <properties resource="db.properties">
           <property name="db.username" value="123" />
     </properties>
     -->

    <!-- 在日常開發過程中,排查問題時難免需要輸出 MyBatis 真正執行的 SQL 語句、參數、結果等信息,這時我們就可以借助 LOG4J 的功能來實現執行信息的輸出 -->
    <settings>
        <setting name="logImpl" value="log4j"/>
    </settings>

    <!-- 起別名:即全類名與別名的映射 -->
    <typeAliases>
        <!-- 單個別名定義 -->
        <!-- <typeAlias type="com.bean.Student" alias="student"/> -->
        <!-- 批量別名定義(推薦) -->
        <!-- package:為指定包下的所有類聲明別名,其默認別名就是類名(首字母大小寫都可) -->
        <package name="com.bean" />
    </typeAliases>

    <!-- environments 配置數據庫環境:環境可以有多個,default屬性指定使用的是哪個 -->
    <!-- 與spring整合后,該信息由spring來管理 -->
    <environments default="mysql">
        <!--environment 配置數據庫環境:id屬性唯一標識 -->
        <environment id="mysql">
            <!-- 配置JDBC事務控制,由mybatis進行管理 type屬性值表示采用JDBC默認的事務 -->
            <transactionManager type="JDBC"></transactionManager>
            <!-- dataSource 數據源信息:type屬性指表示采用mybatis連接池 -->
            <dataSource type="POOLED">
                <!-- property獲取數據庫連接的配置信息 -->
                <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引入映射配置文件 -->
    <mappers>
        <!-- mapper 引入指定的映射配置文件。resource屬性指定映射配置文件的名稱 -->
        <mapper resource="StudentMapper.xml"/>
    </mappers>
</configuration>

加載的順序:

  1. 先加載 properties 中 property 標簽聲明的屬性;
  2. 再加載 properties 標簽引入的 java 配置文件中的屬性;
  3. parameterType 的值和 properties 的屬性值會有沖突關系(因此最好給配置文件屬性加上前綴 db. 加以區分)。

Mybatis 的默認別名:

image

Mapper:

  • <mapper resource=''/>

    • 使用相對於類路徑的資源
    • 如:<mapper resource="sqlmap/User.xml" />
  • <mapper url=''/>

    • 使用完全限定路徑
    • 如:<mapper url="file:///D:\workspace_spingmvc\mybatis_01\config\sqlmap\User.xml" />
  • <mapper class=''/>

    • 使用mapper接口的全限定名
    • 如:<mapper class="com.mybatis.mapper.UserMapper"/>
    • 注意:此種方法要求 mapper 接口和 mapper 映射文件要名稱相同,且放到同一個目錄下;
  • <package name=''/>(推薦)

    • 注冊指定包下的所有映射文件
    • 如:<package name="com.mybatis.mapper"/>
    • 注意:此種方法要求 mapper 接口和 mapper 映射文件要名稱相同,且放到同一個目錄下。

Mybatis 映射配置文件

映射配置文件包含了數據和對象之間的映射關系以及要執行的 SQL 語句。

映射文件可自定義命名,一般按照規范實體類名Mapper.xml,這種命名規范是由 ibatis 遺留下來的。

示例:src 目錄下的 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:命名空間,對statement的信息進行分類管理(在mapper代理時,它具有特殊及重要的作用)
-->
<mapper namespace="StudentMapper">
    
    <!--
        select:查詢功能的標簽,表示一個MappedStatement對象
        id屬性:statement的唯一標識
        #{}:表示一個占位符(即?)
        #{id}:里面的id表示輸入參數的參數名稱
        resultType屬性:輸出結果的所映射的Java類型(單條結果所對應的Java類型)
            這里的student之所以不用寫全類名,是因為會在后面的全局配置文件中起別名
        parameterType屬性:輸入參數的所屬Java類型
    -->
    <!-- 查詢全量學生信息 -->
    <select id="selectAll" resultType="student">
        SELECT * FROM student
    </select>

    <!-- 根據id查詢指定學生信息 -->
    <select id="selectById" resultType="student" parameterType="int">
        SELECT * FROM student WHERE id = #{id}
    </select>

    <!-- 根據名稱模糊查詢學生列表 -->
    <!-- #{} 表示占位符 ?,#{} 接收簡單類型的參數時,里面的名稱可以任意 -->
    <!-- ${} 表示拼接符,${} 接收簡單類型的參數時,里面的名稱必須是 value -->
    <!-- ${} 里面的值會原樣輸出,不加解析(如果該參數值是字符串,也不會添加引號) -->
    <!-- ${} 存在sql注入的風險,但是有些場景下必須使用,比如排序后面需要動態傳入排序的列名 -->
    <select id="selectByName" parameterType="String" resultType="student">
        SELECT * FROM student WHERE name LIKE '%${value}%'
    </select>

    <!-- 添加學生信息 -->
    <!-- selectKey:查詢主鍵,在標簽內需要輸入查詢主鍵的sql -->
    <!-- order:指定查詢主鍵的sql和insert語句的執行順序,相當於insert語句來說 -->
    <!-- LAST_INSERT_ID:該函數是mysql的函數,獲取自增主鍵的ID,它必須配合insert語句一起使用 -->
    <insert id="insertBySelectLastId" parameterType="student">
        <selectKey keyProperty="id" resultType="int" order="AFTER">
            SELECT LAST_INSERT_ID()
        </selectKey>
        INSERT INTO student (name, age) VALUES (#{name},#{age})
    </insert>

    <!-- 添加學生信息 -->
    <!-- 只要不是主鍵自增,order都設置被before -->
    <insert id="insertByKeyproperty" parameterType="student" useGeneratedKeys="true" keyProperty="id">
        <!-- 需要顯式地給id賦值,因為該id的值不是通過自增主鍵來創建的 -->
        INSERT INTO student (id,name,age)
        VALUES(#{id}, #{name}, #{age})
    </insert>

    <!-- 修改學生信息 -->
    <update id="updateById" parameterType="student">
        UPDATE student SET name = #{name}, age = #{age} WHERE id = #{id}
    </update>

    <!-- 刪除學生信息 -->
    <delete id="deleteById" parameterType="int">
        DELETE FROM student WHERE id = #{id}
    </delete>

</mapper>

測試代碼

import com.bean.Student;
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.jupiter.api.Test;

import java.io.InputStream;
import java.util.List;

public class StudentTest {

    // 查詢全量結果
    @Test
    public void selectAll() {
        InputStream inputStream = null;
        SqlSession sqlSession = null;
        try {
            // 1. 加載核心配置文件
            inputStream = Resources.getResourceAsStream("MyBatisConfig.xml");
            // 2. 獲取SqlSession工廠對象
            SqlSessionFactory ssf = new SqlSessionFactoryBuilder().build(inputStream);
            // 3. 通過SqlSession工廠對象獲取SqlSession對象
            // sqlSession 內部的數據區域本身就是一級緩存,是通過 map 來存儲的
            sqlSession = ssf.openSession();
            // 4. 執行映射配置文件中的SQL,並獲取返回結果
            List<Student> students = sqlSession.selectList("StudentMapper.selectAll");  // 映射配置文件中的namespace屬性值.(SQL的)唯一標識
            // 5. 處理返回結果
            for (Student s : students) {
                System.out.println(s);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            sqlSession.close();
            try {
                inputStream.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    // 根據id查詢指定結果
    @Test
    public void selectById() {
        InputStream inputStream = null;
        SqlSession sqlSession = null;
        try {
            // 1. 加載核心配置文件
            inputStream = Resources.getResourceAsStream("MyBatisConfig.xml");
            // 2. 獲取SqlSession工廠對象
            SqlSessionFactory ssf = new SqlSessionFactoryBuilder().build(inputStream);
            // 3. 通過SqlSession工廠對象獲取SqlSession對象
            sqlSession = ssf.openSession();
            // 4. 執行映射配置文件中的SQL,並獲取返回結果
            Student student = sqlSession.selectOne("StudentMapper.selectById", 2);
            // 5. 處理返回結果
            System.out.println(student);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            sqlSession.close();
            try {
                inputStream.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    // 根據名稱模糊查詢結果
    @Test
    public void SelectByName() {
        InputStream inputStream = null;
        SqlSession sqlSession = null;
        try {
            // 1. 加載核心配置文件
            inputStream = Resources.getResourceAsStream("MyBatisConfig.xml");
            // 2. 獲取SqlSession工廠對象
            SqlSessionFactory ssf = new SqlSessionFactoryBuilder().build(inputStream);
            // 3. 通過SqlSession工廠對象獲取SqlSession對象
            sqlSession = ssf.openSession();
            // 4. 執行映射配置文件中的SQL,並獲取返回結果
            List<Student> students = sqlSession.selectList("StudentMapper.selectByName", "五");
            // 5. 處理返回結果
            for (Student s : students) {
                System.out.println(s);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            sqlSession.close();
            try {
                inputStream.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    // 新增數據:根據mysql函數自動獲取id
    @Test
    public void insertBySelectLastId() {
        InputStream inputStream = null;
        SqlSession sqlSession = null;
        try {
            // 1. 加載核心配置文件
            inputStream = Resources.getResourceAsStream("MyBatisConfig.xml");
            // 2. 獲取SqlSession工廠對象
            SqlSessionFactory ssf = new SqlSessionFactoryBuilder().build(inputStream);
            // 3. 通過SqlSession工廠對象獲取SqlSession對象
            sqlSession = ssf.openSession();
            // 4. 執行映射配置文件中的SQL,並獲取返回結果
            Student student = new Student(null, "王八", 29);  // id會自動填充
            int result = sqlSession.insert("StudentMapper.insertBySelectLastId", student);
            // 5. 處理返回結果
            if (result==1) {
                System.out.println("insertBySelectLastId 新增成功");
                // 需要手動提交事務
                sqlSession.commit();
            } else {
                System.out.println("insertBySelectLastId 新增失敗");
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            sqlSession.close();
            try {
                inputStream.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    // 新增數據:根據映射配置屬性,自動獲取id
    @Test
    public void insertByKeyproperty() {
        InputStream inputStream = null;
        SqlSession sqlSession = null;
        try {
            // 1. 加載核心配置文件
            inputStream = Resources.getResourceAsStream("MyBatisConfig.xml");
            // 2. 獲取SqlSession工廠對象
            SqlSessionFactory ssf = new SqlSessionFactoryBuilder().build(inputStream);
            // 3. 通過SqlSession工廠對象獲取SqlSession對象
            sqlSession = ssf.openSession();
            // 4. 執行映射配置文件中的SQL,並獲取返回結果
            Student student = new Student(null, "史十", 29);  // id會自動填充
            int result = sqlSession.insert("StudentMapper.insertByKeyproperty", student);
            // 5. 處理返回結果
            if (result==1) {
                System.out.println("insertByKeyproperty 新增成功");
                // 需要手動提交事務
                sqlSession.commit();
            } else {
                System.out.println("insertByKeyproperty 新增失敗");
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            sqlSession.close();
            try {
                inputStream.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    // 修改數據
    @Test
    public void updateById() {
        InputStream inputStream = null;
        SqlSession sqlSession = null;
        try {
            // 1. 加載核心配置文件
            inputStream = Resources.getResourceAsStream("MyBatisConfig.xml");
            // 2. 獲取SqlSession工廠對象
            SqlSessionFactory ssf = new SqlSessionFactoryBuilder().build(inputStream);
            // 3. 通過SqlSession工廠對象獲取SqlSession對象
            sqlSession = ssf.openSession();
            // 4. 執行映射配置文件中的SQL,並獲取返回結果
            Student student = new Student(2, "小二", 29);
            int result = sqlSession.update("StudentMapper.updateById", student);
            // 5. 處理返回結果
            if (result==1) {
                System.out.println("updateById 修改成功");
                // 需要手動提交事務
                sqlSession.commit();
            } else {
                System.out.println("updateById 修改失敗");
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            sqlSession.close();
            try {
                inputStream.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    // 刪除數據
    @Test
    public void deleteById() {
        InputStream inputStream = null;
        SqlSession sqlSession = null;
        try {
            // 1. 加載核心配置文件
            inputStream = Resources.getResourceAsStream("MyBatisConfig.xml");
            // 2. 獲取SqlSession工廠對象
            SqlSessionFactory ssf = new SqlSessionFactoryBuilder().build(inputStream);
            // 3. 通過SqlSession工廠對象獲取SqlSession對象
            sqlSession = ssf.openSession();
            // 4. 執行映射配置文件中的SQL,並獲取返回結果
            int result = sqlSession.delete("StudentMapper.deleteById", 2);
            // 5. 處理返回結果
            if (result==1) {
                System.out.println("updateById 刪除成功");
                // 需要手動提交事務
                sqlSession.commit();
            } else {
                System.out.println("updateById 刪除失敗");
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            sqlSession.close();
            try {
                inputStream.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}

輸入/輸出映射詳解

輸入映射:parameterType

1)簡單類型

如學生 id 等基本數據類型。

2)POJO 類型

POJO(Plain Ordinary Java Object)即簡單的 Java 對象,實際就是普通 Java Beans。

3)包裝 POJO 類型

包裝 POJO 類型,即 Java 對象中有其他 Java 對象的引用。

  1. 需求:
    綜合查詢時,可能會根據用戶信息、商品信息、訂單信息等作為條件進行查詢,用戶信息中的查詢條件由用戶的名稱和性別進行查詢。

  2. 創建包裝 POJO 類型:
    image

  3. 映射文件:
    image

  4. Mapper 接口:
    image

  5. 測試代碼:
    image

4)Map 類型

同傳遞 POJO 對象一樣,Map 的 key 相當於 POJO 的屬性。

  • 映射文件:
<!-- 傳遞 hashmap 綜合查詢用戶信息 -->
<select id="findUserByHashmap" parameterType="hashmap" resultType="user">
    <!-- username是hashmap的key -->
    select * from user where id=#{id} and username like '%${username}%'
</select>
  • 測試代碼:
Public void testFindUserByHashmap()throws Exception{
    // 獲取session
    SqlSession session = sqlSessionFactory.openSession();
    // 獲限mapper接口實例
    UserMapper userMapper = session.getMapper(UserMapper.class);
    // 構造查詢條件Hashmap對象
    HashMap<String, Object> map = new HashMap<String, Object>();
    map.put("id", 1);
    map.put("username", "管理員");
    
    // 傳遞Hashmap對象查詢用戶列表
    List<User>list = userMapper.findUserByHashmap(map);
    // 關閉session
    session.close();
    
}

注意:當傳遞的 map 中的 key 和 sql 中解析的 key 不一致時,程序不會報錯,只是通過 key 獲取的值為空。


輸出映射

resultType

使用要求:

  • 使用 resultType 進行結果映射時,需要查詢出的列名和映射的對象的屬性名一致,才能映射成功。
  • 如果查詢的列名和對象的屬性名全部不一致,那么映射的對象為空。
  • 如果查詢的列名和對象的屬性名有一個一致,那么映射的對象不為空,但是只有映射正確那一個屬性才有值。
  • 如果查詢的 SQL 的列名有別名,那么這個別名就需要是和屬性映射的列名。

resultMap

使用要求

  • 使用 resultMap 進行結果映射時,不需要查詢的列名和映射的屬性名必須一致,但是需要聲明一個 resultMap,來對列名和屬性名進行映射。

示例需求

對以下 SQL 查詢的結果集進行對象映射:Select id id_, username username_, sex sex_ from user where id = 1;

  • 映射文件:
<!-- resultMap入門 -->
<!-- id標簽:專門為查詢結果中唯一列映射 -->
<!-- result標簽:映射查詢結果中的普通列 -->
<resultMap type="user" id="UserRstMap">
    <id column="id_" property="id" />
    <result column="username_" property="username" />
    <result column="sex_" property="sex" />
</resultMap>
<select id="findUserRstMap" parameterType="int" resultMap="UserRstMap">
    Select id id_,username username_,sex sex_ from user where id = #{id}
</select>
  • Mapper 接口:
    image

  • 測試代碼:
    image


Mybatis Mapper 代理開發

傳統 Dao 開發方式的問題

原始 Dao 的開發方式,即開發 Dao 接口和 Dao 實現類。

Dao 接口:

import com.bean.Student;

import java.util.List;

public interface StudentDao {

    // 1. 根據學生ID查詢學生信息 
    public Student selectById(int id);

    // 2. 根據學生名稱模糊查詢學生列表 
    public List<Student> selectByName(String name);
	
    // 3. 添加學生 
    public void insertBySelectLastId(Student Student);

}

Dao 實現類:

  • SqlSessionFactory:它的生命周期,應該是應用范圍,即全局范圍只有一個工廠,因此使用單例模式來實現這個功能(與 Spring 集成之后,由 Spring 來對其進行單例管理)。
  • SqlSession:它內部含有一塊數據區域,存在線程不安全的問題,所以應該將 Sqlsession 聲明到方法內部。
import com.bean.Student;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;

import java.util.List;

public class StudentDaoImpl implements StudentDao {

    // 依賴注入
    private SqlSessionFactory sqlSessionFactory;

    public StudentDaoImpl(SqlSessionFactory sqlSessionFactory) {
        this.sqlSessionFactory = sqlSessionFactory;
    }

    @Override
    public Student selectById(int id) {
        // 創建SqlSession
        SqlSession sqlSession = sqlSessionFactory.openSession();
        // 調用SqlSession的增刪改查方法
        // 第一個參數:表示statement的唯一標示
        Student student = sqlSession.selectOne("StudentMapper.selectById", id);
        System.out.println(student);
        // 關閉資源
        sqlSession.close();
        return student;
    }

    @Override
    public List<Student> selectByName(String name) {
        // 創建SqlSession
        SqlSession sqlSession = sqlSessionFactory.openSession();
        // 調用SqlSession的增刪改查方法
        // 第一個參數:表示statement的唯一標示
        List<Student> list = sqlSession.selectList("StudentMapper.SelectByName", name);
        System.out.println(list.toString());
        // 關閉資源
        sqlSession.close();
        return list;
    }

    @Override
    public void insertBySelectLastId(Student Student) {
        // 創建SqlSession
        SqlSession sqlSession = sqlSessionFactory.openSession();
        // 調用SqlSession的增刪改查方法
        // 第一個參數:表示statement的唯一標示
        sqlSession.insert("StudentMapper.insertBySelectLastId", Student);
        System.out.println(Student.getId());
        // 提交事務
        sqlSession.commit();
        // 關閉資源
        sqlSession.close();
    }

}

測試類:

import com.bean.Student;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.Before;
import org.junit.Test;

import java.io.IOException;
import java.io.InputStream;

public class StudentDaoTest {

    private SqlSessionFactory sqlSessionFactory;

    @Before
    public void setUp() throws IOException {
        // 1. 加載核心配置文件
        InputStream inputStream = Resources.getResourceAsStream("MyBatisConfig.xml");
        // 2. 獲取SqlSession工廠對象
        sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
    }

    @Test
    public void testSelectStudentById() {
        StudentDao studentDao = new StudentDaoImpl(sqlSessionFactory);
        Student student = studentDao.selectById(1);
        System.out.println(student);
    }

}

思考存在的問題:

  • 有大量的重復的模板代碼。
  • 存在硬編碼。

Mapper 代理的開發方式

采用 Mybatis 的代理開發方式實現 Dao 層的開發,是企業的主流方式。

Mapper 接口開發方法只需要程序員編寫 Mapper 接口(相當於 Dao 接口),由 Mybatis 框架根據接口定義創建接口的動態代理對象,代理對象的方法體同上邊 Dao 接口實現類方法。

總結代理方式可以讓我們只編寫接口即可,而實現類對象由 MyBatis 生成

Mapper 代理的開發規范

  1. Mapper.xml(映射文件)文件中的 namespace 與 mapper 接口的全限定名相同。
  2. Mapper 接口方法名和 Mapper.xml 中定義的每個 statement 的 id 相同。
  3. Mapper 接口方法的輸入參數類型和 mapper.xml 中定義的每個 sql 的 parameterType 的類型相同。
  4. Mapper 接口方法的輸出參數類型和 mapper.xml 中定義的每個 sql 的 resultType 的類型相同。

image

總結

Mapper 接口開發的方式: 程序員只需定義接口就可以對數據庫進行操作。那么具體的對象是怎么創建的?

  1. 程序員負責定義接口;
  2. Mybatis 框架根據接口,通過動態代理的方式生成代理對象,負責數據庫的 crud 操作。

代碼示例:

  • Mapper 接口
import com.bean.Student;

import java.util.List;

public interface StudentMapper {
    // 查詢全部
    public abstract List<Student> selectAll();

    // 根據id查詢
    public abstract Student selectById(Integer id);

    // 新增數據
    public abstract Integer insert(Student stu);

    // 修改數據
    public abstract Integer update(Student stu);

    // 刪除數據
    public abstract Integer delete(Integer id);

    // 多條件查詢
    public abstract List<Student> selectCondition(Student stu);

    // 根據多個id查詢
    public abstract List<Student> selectByIds(List<Integer> ids);
}
  • 測試類
import com.bean.Student;

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 java.io.IOException;
import java.io.InputStream;

public class StudentMapperTest {

    private SqlSessionFactory sqlSessionFactory;

    @Before
    public void setUp() throws IOException {
        // 加載核心配置文件
        InputStream inputStream = Resources.getResourceAsStream("MyBatisConfig.xml");
        // 獲取SqlSession工廠對象
        sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
    }

    @Test
    public void testSelectStudentById() {
        SqlSession sqlSession = sqlSessionFactory.openSession();
        // 由mybatis通過sqlSession來創建代理對象
        // 創建StudentMapper對象
        StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
        Student student = mapper.selectById(1);
        System.out.println(student);
        // 關閉資源
        sqlSession.close();
        inputStream.close();
    }

}

動態代理方式源碼分析

分析動態代理對象如何生成的?

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

分析方法是如何執行的?

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


動態 SQL

在 Mybatis 中提供了一些動態 SQL 標簽,可以讓程序員更快的進行 Mybatis 的開發,這些動態 SQL 可以提高 SQL 的可重用性。

動態 SQL 指的就是 SQL 語句可以根據條件或者參數的不同,而進行動態的變化。

常用的動態 SQL 標簽有 if 標簽、where 標簽、SQL 片段、foreach 標簽。

if、where 標簽

if 和 where 標簽可用於根據實體類的不同取值,使用不同的 SQL 語句來進行查詢。

比如在 id 不為空時可以根據 id 查詢,在 username 不為空時還要加入用戶名作為條件等。這種情況在我們的多條件組合查詢中經常會碰到。

使用格式:

<!-- where:條件標簽。如果有動態條件,則使用該標簽代替 where 關鍵字 -->
<where>
    <!-- if:條件判斷標簽 -->
    <if test="條件判斷">
        查詢條件拼接
    </if>

示例:

  • 映射文件:
<select id="findByCondition" parameterType="student" resultType="student">
    select * from student
    <where>
        <if test="id!=0">
            and id=#{id}
        </if>
        <if test="username!=null">
            and username=#{username}
        </if>
    </where>
</select>
  • 當查詢條件 id 和 username 都存在時
     // 獲得MyBatis框架生成的StudentMapper接口的實現類
    StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
    Student condition = new Student();
    condition.setId(1);
    condition.setUsername("lucy");
    Student student = mapper.findByCondition(condition);

image

  • 當查詢條件只有 id 存在時:
// 獲得MyBatis框架生成的UserMapper接口的實現類
StudentMapper mapper = sqlSession.getMapper( StudentMapper.class);
    Student condition = new Student();
    condition.setId(1);
    Student student = mapper.findByCondition(condition);

image

foreach 標簽

<foreach> 即循環遍歷標簽。適用於多個參數或者的關系。

使用語法:

<foreach collection="" open="" close="" item="" separator="">
    獲取參數
</foreach>
  • collection:參數容器類型(list 集合、array 數組)
  • open:開始的 SQL 語句
  • close:結束的 SQL 語句
  • item:參數變量名
  • separator:分隔符

示例需求:

循環執行 SQL 的拼接操作,例如:SELECT * FROM student WHERE id IN (1, 2, 5);

  • 映射文件:
<select id="findByIds" parameterType="list" resultType="student">
    select * from student
    <where>
        <foreach collection="list" open="id in(" close=")" item="id" separator=",">
            #{id}
        </foreach>
    </where>
</select>
  • 測試代碼:
// 獲得MyBatis框架生成的UserMapper接口的實現類
StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
List<Integer> ids = new ArrayList<>();
ids.add(1);
ids.add(2);
List<Student> sList = mapper.findByIds(ids);
System.out.println(sList);

SQL 片段抽取

將重復的 SQL 提取出來,使用時用 include 引用即可,最終達到 SQL 重用的目的。

使用語法:

<!-- <sql>:抽取 SQL 語句標簽 -->
<sql id="片段唯一標識">需要抽取的 SQL 語句</sql>

<!-- <include>:引入 SQL 片段標簽 -->
<include refid="片段唯一標識id" />

使用示例:

<!-- 抽取sql片段 -->
<sql id="selectStudent" select * from student</sql>

<!-- 引入sql片段 -->
<select id="findById" parameterType="int" resultType="student">
    <include refid="selectStudent"></include> where id=#{id}
</select>

<!-- 引入sql片段 -->
<select id="findByIds" parameterType="list" resultType="student">
    <include refid="selectStudent"></include>
    <where>
        <foreach collection="array" open="id in(" close=")" item="id" separator=",">
            #{id}
        </foreach>
    </where>
</select>

分頁插件

分頁插件介紹

分頁功能介紹:

image

  • 分頁可以將很多條結果進行分頁顯示。
  • 如果當前在第一頁,則沒有上一頁。如果當前在最后一頁,則沒有下一頁。
  • 需要明確當前是第幾頁,這一頁中顯示多少條結果。

MyBatis 分頁插件:

  1. 在企業級開發中,分頁也是一種常見的技術。而目前使用的 MyBatis 是不帶分頁功能的,如果想實現分頁的功能,需要我們手動編寫 LIMIT 語句。但是不同的數據庫實現分頁的 SQL 語句也是不同的,所以手寫分頁的成本較高,這時就可以借助分頁插件來幫助我們實現分頁功能。
  2. MyBatis 可以使用第三方的插件來對功能進行擴展,如分頁插件 PageHelper 就將分頁的復雜操作進行了封裝,使用簡單的方式即可獲得分頁的相關數據,從而讓分頁功能變得非常簡單。

分頁插件使用

1)導入 jar 包

  • pagehelper-5.1.10.jar
  • jsqlparser-3.1.jar

2)在 Mybatis 全局配置文件中配置 PageHelper 插件

image

<!-- 注意:分頁插件的配置位置需在通用 mapper 之前 -->
<plugins>
    <!-- pageHelper 4.0 版本配置
        <plugin interceptor="com.github.pagehelper.PageHelper">
            <property name="dialect" value="mysql"/>
        </plugin>-->
    <!-- pageHelper 5.0 以上版本的配置 -->
    <plugin interceptor="com.github.pagehelper.PageInterceptor">
    </plugin>
</plugins>

3)測試分頁數據獲取

import com.bean.Student;
import com.github.pagehelper.PageHelper;

import com.github.pagehelper.PageInfo;
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 java.io.IOException;
import java.io.InputStream;
import java.util.List;

public class StudentMapperTest {

    private SqlSessionFactory sqlSessionFactory;

    @Before
    public void setUp() throws IOException {
        // 加載核心配置文件
        InputStream inputStream = Resources.getResourceAsStream("MyBatisConfig.xml");
        // 獲取SqlSession工廠對象
        sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
    }

    @Test
    public void testPaging() {
        SqlSession sqlSession = sqlSessionFactory.openSession();
        // 由mybatis通過sqlSession來創建代理對象
        // 創建StudentMapper對象
        StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);

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

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

        // 6. 處理結果
        for (Student student : list) {
            System.out.println(student);
        }

        // 獲取分頁的相關參數
        PageInfo<Student> info = new PageInfo<>(list);
        System.out.println("總條數:" + info.getTotal());
        System.out.println("總頁數:" + info.getPages());
        System.out.println("每頁顯示條數:" + info.getPageSize());
        System.out.println("當前頁數:" + info.getPageNum());
        System.out.println("上一頁數:" + info.getPrePage());
        System.out.println("下一頁數:" + info.getNextPage());
        System.out.println("是否是第一頁:" + info.isIsFirstPage());
        System.out.println("是否是最后一頁:" + info.isIsLastPage());

        // 關閉資源
        sqlSession.close();
    }
    
}

Mybatis 多表操作

多表模型介紹

  • 一對一:在任意一方建立外鍵,關聯對方的主鍵。
  • 一對多:在多的一方建立外鍵,關聯對方(一張表)的主鍵。
  • 多對多:借助中間表,中間表至少兩個字段,分別關聯兩張表的主鍵。

一對一

案例:人和身份證,一個人只有一個身份證

1)SQL 數據准備:

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);

2)實體類:

  • Person 類:
package com.bean;

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

    public Person() {
    }

    public Person(Integer id, String name, Integer age) {
        this.id = id;
        this.name = name;
        this.age = age;
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Person{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
  • Card 類:
package com.bean;

public class Card {
    private Integer id;
    private Integer number;
    private Person person;  // 所屬人的對象

    public Card() {
    }

    public Card(Integer id, Integer number, Person person) {
        this.id = id;
        this.number = number;
        this.person = person;
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public Integer getNumber() {
        return number;
    }

    public void setNumber(Integer number) {
        this.number = number;
    }

    public Person getPerson() {
        return person;
    }

    public void setPerson(Person person) {
        this.person = person;
    }

    @Override
    public String toString() {
        return "Card{" +
                "id=" + id +
                ", number=" + number +
                ", person=" + person +
                '}';
    }

}

3)配置文件:

  • 全局配置文件:
<?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 核心根標簽-->
<configuration>

    <!--引入數據庫連接的配置文件-->
    <properties resource="jdbc.properties"/>

    <!--配置LOG4J-->
    <settings>
        <setting name="logImpl" value="log4j"/>
    </settings>

    <!--起別名-->
    <typeAliases>
        <package name="com.bean"/>
    </typeAliases>

    <plugins>
        <!-- 注意:分頁插件的配置位置需在通用 mapper 之前 -->
<!--        <plugin interceptor="com.github.pagehelper.PageHelper">-->
<!--            &lt;!&ndash; 指定方言 &ndash;&gt;-->
<!--            <property name="dialect" value="mysql"/>-->
<!--        </plugin>-->
        <plugin interceptor="com.github.pagehelper.PageInterceptor">
        </plugin>
    </plugins>

    <!--environments配置數據庫環境,環境可以有多個。default屬性指定使用的是哪個-->
    <environments default="mysql">
        <!--environment配置數據庫環境  id屬性唯一標識-->
        <environment id="mysql">
            <!-- transactionManager事務管理。  type屬性,采用JDBC默認的事務-->
            <transactionManager type="JDBC"></transactionManager>
            <!-- dataSource數據源信息   type屬性 連接池-->
            <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="TableMapper.xml"/>
    </mappers>

</configuration>
  • 映射文件:
<?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.mapper.OneToOneMapper">
    <!--配置字段和實體對象屬性的映射關系-->
    <!-- 使用resultMap來進行一對一結果映射,它是將關聯對象添加到主信息的對象中,具體說是對象嵌套對象的一種映射方式 -->
    <resultMap id="oneToOne" type="card">
        <!-- id標簽:建議在關聯查詢時必須寫上,不寫不會報錯,但是會影響性能 -->
        <id column="cid" property="id" />
        <result column="number" property="number" />
        <!--
            association:配置被包含對象的映射關系
            property:被包含對象的變量名
            javaType:被包含對象的數據類型
        -->
        <association property="person" 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>

4)Mapper 接口類:

import com.bean.Card;

import java.util.List;

public interface OneToOneMapper {

    // 查詢全部
    public abstract List<Card> selectAll();

}

5)測試類:

import com.bean.Card;
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.jupiter.api.Test;

import java.io.IOException;
import java.io.InputStream;
import java.util.List;

public class OneToOneTest {

    @Test
    public void testSelectAll() throws IOException {
        // 1.加載核心配置文件
        InputStream is = Resources.getResourceAsStream("MyBatisConfig.xml");

        // 2.獲取SqlSession工廠對象
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);

        // 3.通過工廠對象獲取SqlSession對象
        SqlSession sqlSession = sqlSessionFactory.openSession(true);

        // 4.獲取OneToOneMapper接口的實現類對象
        OneToOneMapper mapper = sqlSession.getMapper(OneToOneMapper.class);

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

        // 6.處理結果
        for (Card c : list) {
            System.out.println(c);
        }

        // 7.釋放資源
        sqlSession.close();
        is.close();
    }
}

一對多

案例:班級和學生,一個班級可以有多個學生

1)SQL 准備:

CREATE TABLE classes(
	id INT PRIMARY KEY AUTO_INCREMENT,
	NAME VARCHAR(20)
);
INSERT INTO classes VALUES (NULL,'一班');
INSERT INTO classes VALUES (NULL,'二班');


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);

2)實體類:

  • 班級類:
package com.bean;

import java.util.List;

public class Classes {
    
    private Integer id;     // 主鍵id
    private String name;    // 班級名稱
    private List<Student> students;  // 班級中所有學生對象

    public Classes() {
    }

    public Classes(Integer id, String name, List<Student> students) {
        this.id = id;
        this.name = name;
        this.students = students;
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public List<Student> getStudents() {
        return students;
    }

    public void setStudents(List<Student> students) {
        this.students = students;
    }

    @Override
    public String toString() {
        return "Classes{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", students=" + students +
                '}';
    }
}
  • 學生類:
package com.bean;

import java.util.List;

public class Student {

    private Integer id;  // 主鍵id
    private String name;  // 學生姓名
    private Integer age;  // 學生年齡
    private Classes classes;  // 課程

    public Student() {
    }

    public Student(Integer id, String name, Integer age, Classes classes) {
        this.id = id;
        this.name = name;
        this.age = age;
        this.classes = classes;
    }

    public Classes getClasses() {
        return classes;
    }

    public void setClasses(Classes classes) {
        this.classes = classes;
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Student{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", age=" + age +
                ", classes=" + classes +
                '}';
    }
}

3)映射文件:

<?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.mapper.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>
</mapper>

4)Mapper 接口:

import com.bean.Classes;

import java.util.List;

public interface OneToManyMapper {

    // 查詢全部
    public abstract List<Classes> selectAll();

}

5)測試類:

package com.mapper;

import com.bean.Classes;
import com.bean.Student;
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.jupiter.api.Test;

import java.io.IOException;
import java.io.InputStream;
import java.util.List;

public class OneToManyTest {
    @Test
    public void testSelectAll() throws IOException {
        // 1.加載核心配置文件
        InputStream is = Resources.getResourceAsStream("MyBatisConfig.xml");

        // 2.獲取SqlSession工廠對象
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);

        // 3.通過工廠對象獲取SqlSession對象
        SqlSession sqlSession = sqlSessionFactory.openSession(true);

        // 4.獲取OneToOneMapper接口的實現類對象
        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);
            }
        }

        // 7.釋放資源
        sqlSession.close();
        is.close();
    }
}

多對多

案例:學生和課程,一個學生可以選擇多門課程、一個課程也可以被多個學生所選擇

1)SQL 准備:

CREATE TABLE course(
	id INT PRIMARY KEY AUTO_INCREMENT,
	NAME VARCHAR(20)
);
INSERT INTO course VALUES (NULL,'語文');
INSERT INTO course VALUES (NULL,'數學');

CREATE TABLE student_course(
	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 student_course VALUES (NULL,1,1);
INSERT INTO student_course VALUES (NULL,1,2);
INSERT INTO student_course VALUES (NULL,2,1);
INSERT INTO student_course VALUES (NULL,2,2);

2)實體類:

  • 學生類:
import java.util.List;

public class Student {
    private Integer id;    // 主鍵id
    private String name;   // 學生姓名
    private Integer age;   // 學生年齡

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

    public Student() {
    }

    public Student(Integer id, String name, Integer age, List<Course> courses) {
        this.id = id;
        this.name = name;
        this.age = age;
        this.courses = courses;
    }

    public List<Course> getCourses() {
        return courses;
    }

    public void setCourses(List<Course> courses) {
        this.courses = courses;
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Student{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
  • 課程類:
public class Course {
    private Integer id;   // 主鍵id
    private String name;  // 課程名稱

    public Course() {
    }

    public Course(Integer id, String name) {
        this.id = id;
        this.name = name;
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "Course{" +
                "id=" + id +
                ", name='" + name + '\'' +
                '}';
    }
}

3)映射文件:

<?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.mapper.ManyToManyMapper">
    <resultMap id="manyToMany" type="student">
        <id column="sid" property="id"/>
        <result column="sname" property="name"/>
        <result column="sage" property="age"/>

        <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, student_course sc 
        WHERE sc.sid=s.id AND sc.cid=c.id
    </select>
</mapper>

4)Mapper 接口:

import com.bean.Student;

import java.util.List;

public interface ManyToManyMapper {

    // 查詢全部
    public abstract List<Student> selectAll();

}

5)測試類:

package com.mapper;

import com.bean.Classes;
import com.bean.Course;
import com.bean.Student;
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.jupiter.api.Test;

import java.io.IOException;
import java.io.InputStream;
import java.util.List;

public class ManyToManyTest {
    @Test
    public void testSelectAll() throws IOException {
        // 1.加載核心配置文件
        InputStream is = Resources.getResourceAsStream("MyBatisConfig.xml");

        // 2.獲取SqlSession工廠對象
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);

        // 3.通過工廠對象獲取SqlSession對象
        SqlSession sqlSession = sqlSessionFactory.openSession(true);

        // 4.獲取OneToOneMapper接口的實現類對象
        ManyToManyMapper mapper = sqlSession.getMapper(ManyToManyMapper.class);

        //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);
            }
        }

        // 7.釋放資源
        sqlSession.close();
        is.close();
    }
}

Mybatis 注解開發

常用注解與案例

近幾年來,注解開發越來越流行,Mybatis 也可以使用注解開發方式,這樣我們就可以減少編寫 Mapper 映射文件了。

  • @Insert:實現新增
  • @Update:實現更新
  • @Delete:實現刪除
  • @Select:實現查詢
  • @Result:實現結果集的封裝
  • @Results:可以與 @Result 一起使用,以封裝多個結果集
  • @One:實現一對一結果集封裝
  • @Many:實現一對多結果集封裝

案例:student 表的 CRUD

  1. 創建 Mapper 接口:
package com.mapper;

import com.bean.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 * FROM student")
    public abstract List<Student> selectAll();

    //新增操作
    @Insert("INSERT INTO student VALUES (#{id},#{name},#{age})")
    public abstract Integer insert(Student stu);

    //修改操作
    @Update("UPDATE student SET name=#{name},age=#{age} WHERE id=#{id}")
    public abstract Integer update(Student stu);

    //刪除操作
    @Delete("DELETE FROM student WHERE id=#{id}")
    public abstract Integer delete(Integer id);
}
  1. 修改 Mybatis 全局配置文件:
    <mappers>
        <!--掃描使用注解的類-->
        <mapper class="com.itheima.mapper.UserMapper">
    </mappers>

或者:

    <mappers>
        <!--掃描使用注解的類所在的包-->
        <package name="com.mapper"></package>
    </mappers>
  1. 測試類:
package com.mapper;

import com.bean.Student;
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.jupiter.api.Test;

import java.io.InputStream;
import java.util.List;

public class AnnotationTest {

    @Test
    public void selectAll() throws Exception{
        //1.加載核心配置文件
        InputStream is = Resources.getResourceAsStream("MyBatisConfig.xml");

        //2.獲取SqlSession工廠對象
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);

        //3.通過工廠對象獲取SqlSession對象
        SqlSession sqlSession = sqlSessionFactory.openSession(true);

        //4.獲取StudentMapper接口的實現類對象
        StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);

        //5.調用實現類對象中的方法,接收結果
        List<Student> list = mapper.selectAll();

        //6.處理結果
        for (Student student : list) {
            System.out.println(student);
        }

        //7.釋放資源
        sqlSession.close();
        is.close();
    }

    @Test
    public void insert() throws Exception{
        //1.加載核心配置文件
        InputStream is = Resources.getResourceAsStream("MyBatisConfig.xml");

        //2.獲取SqlSession工廠對象
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);

        //3.通過工廠對象獲取SqlSession對象
        SqlSession sqlSession = sqlSessionFactory.openSession(true);

        //4.獲取StudentMapper接口的實現類對象
        StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);

        //5.調用實現類對象中的方法,接收結果
        Student stu = new Student(4, "趙六", 26);
        Integer result = mapper.insert(stu);

        //6.處理結果
        System.out.println(result);

        //7.釋放資源
        sqlSession.close();
        is.close();
    }

    @Test
    public void update() throws Exception{
        //1.加載核心配置文件
        InputStream is = Resources.getResourceAsStream("MyBatisConfig.xml");

        //2.獲取SqlSession工廠對象
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);

        //3.通過工廠對象獲取SqlSession對象
        SqlSession sqlSession = sqlSessionFactory.openSession(true);

        //4.獲取StudentMapper接口的實現類對象
        StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);

        //5.調用實現類對象中的方法,接收結果
        Student stu = new Student(4, "趙六", 36);
        Integer result = mapper.update(stu);

        //6.處理結果
        System.out.println(result);

        //7.釋放資源
        sqlSession.close();
        is.close();
    }

    @Test
    public void delete() throws Exception{
        //1.加載核心配置文件
        InputStream is = Resources.getResourceAsStream("MyBatisConfig.xml");

        //2.獲取SqlSession工廠對象
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);

        //3.通過工廠對象獲取SqlSession對象
        SqlSession sqlSession = sqlSessionFactory.openSession(true);

        //4.獲取StudentMapper接口的實現類對象
        StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);

        //5.調用實現類對象中的方法,接收結果
        Integer result = mapper.delete(4);

        //6.處理結果
        System.out.println(result);

        //7.釋放資源
        sqlSession.close();
        is.close();
    }

}

MyBatis 注解開發的多表操作

實現復雜關系映射之前我們可以在映射文件中通過配置 <resultMap> 來實現,使用注解開發后,我們可以使用 @Results、@Result、@One、@Many 注解組合來完成復雜關系的配置。

image

image

一對一查詢

需求:查詢一個用戶信息,與此同時查詢出該用戶對應的身份證信息

image

1)對應 SQL:

SELECT * FROM card;

SELECT * FROM person WHERE id=#{id};

2)創建 PersonMapper 接口:

import com.bean.Person;
import org.apache.ibatis.annotations.Select;

public interface PersonMapper {
    // 根據id查詢
    @Select("SELECT * FROM person WHERE id=#{id}")
    public abstract Person selectById(Integer id);
}

3)使用注解配置 CardMapper:

import com.bean.Card;
import com.bean.Person;
import org.apache.ibatis.annotations.One;
import org.apache.ibatis.annotations.Result;
import org.apache.ibatis.annotations.Results;
import org.apache.ibatis.annotations.Select;

import java.util.List;

public interface CardMapper {
    // 查詢全部
    @Select("SELECT * FROM card")
    @Results({
            @Result(column="id", property="id"),  // id 列
            @Result(column="number", property="number"),  // number 列
            @Result(  // Card表中的 person id 列
                    property = "person",        // 被包含對象的變量名
                    javaType = Person.class,    // 被包含對象的實際數據類型
                    column = "pid",             // 根據查詢出的card表中的pid字段來查詢person表
                    /*
                        one、@One 一對一固定寫法
                        select屬性:指定調用哪個接口中的哪個方法
                     */
                    one = @One(select="com.mapper.PersonMapper.selectById")
            )
    })
    public abstract List<Card> selectAll();
}

4)測試類:

import com.bean.Card;
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.jupiter.api.Test;

import java.io.IOException;
import java.io.InputStream;
import java.util.List;

public class OneToOneTest {

    @Test
    public void testSelectAll() throws IOException {
        // 1.加載核心配置文件
        InputStream is = Resources.getResourceAsStream("MyBatisConfig.xml");

        // 2.獲取SqlSession工廠對象
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);

        // 3.通過工廠對象獲取SqlSession對象
        SqlSession sqlSession = sqlSessionFactory.openSession(true);

        // 4.獲取OneToOneMapper接口的實現類對象
        CardMapper mapper = sqlSession.getMapper(CardMapper.class);

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

        // 6.處理結果
        for (Card c : list) {
            System.out.println(c);
        }

        // 7.釋放資源
        sqlSession.close();
        is.close();
    }
}

執行結果:

Card{id=1, number=12345, person=Person{id=1, name='張三', age=23}}
Card{id=2, number=23456, person=Person{id=2, name='李四', age=24}}
Card{id=3, number=34567, person=Person{id=3, name='王五', age=25}}

一對多

需求:查詢一個課程,與此同時查詢出該課程對應的學生信息

image

1)對應的 SQL:

SELECT * FROM classes

SELECT * FROM student WHERE cid=#{cid}

2)創建 StudentMapper 接口:

import com.bean.Student;
import org.apache.ibatis.annotations.Select;

import java.util.List;

public interface StudentMapper {
    //根據cid查詢student表
    @Select("SELECT * FROM student WHERE cid=#{cid}")
    public abstract List<Student> selectByCid(Integer cid);
}

3)使用注解配置 CardMapper:

package com.mapper;

import com.bean.Card;
import com.bean.Person;
import org.apache.ibatis.annotations.One;
import org.apache.ibatis.annotations.Result;
import org.apache.ibatis.annotations.Results;
import org.apache.ibatis.annotations.Select;

import java.util.List;

public interface CardMapper {
    // 查詢全部
    @Select("SELECT * FROM card")
    @Results({
            @Result(column="id", property="id"),  // id 列
            @Result(column="number", property="number"),  // number 列
            @Result(  // Card表中的 person id 列
                    property = "person",        // 被包含對象的變量名
                    javaType = Person.class,    // 被包含對象的實際數據類型
                    column = "pid",             // 根據查詢出的card表中的pid字段來查詢person表
                    /*
                        one、@One 一對一固定寫法
                        select屬性:指定調用哪個接口中的哪個方法
                     */
                    one = @One(select="com.mapper.PersonMapper.selectById")
            )
    })
    public abstract List<Card> selectAll();
}

4)測試類:

package com.mapper;

import com.bean.Classes;
import com.bean.Student;
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.jupiter.api.Test;

import java.io.IOException;
import java.io.InputStream;
import java.util.List;

public class OneToManyTest {
    @Test
    public void testSelectAll() throws IOException {
        // 1.加載核心配置文件
        InputStream is = Resources.getResourceAsStream("MyBatisConfig.xml");

        // 2.獲取SqlSession工廠對象
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);

        // 3.通過工廠對象獲取SqlSession對象
        SqlSession sqlSession = sqlSessionFactory.openSession(true);

        // 4.獲取OneToOneMapper接口的實現類對象
        ClassesMapper mapper = sqlSession.getMapper(ClassesMapper.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);
            }
        }

        // 7.釋放資源
        sqlSession.close();
        is.close();
    }
}

執行結果:

1,一班
	Student{id=1, name='張三', age=23}
	Student{id=2, name='李四', age=24}
2,二班
	Student{id=3, name='王五', age=25}

多對多

需求:查詢學生以及所對應的課程信息

image

1)對應的 SQL:

SELECT DISTINCT s.id,s.name,s.age FROM student s,stu_cr sc WHERE sc.sid=s.id
SELECT c.id,c.name FROM stu_cr sc,course c WHERE sc.cid=c.id AND sc.sid=#{id}

2)創建 CourseMapper 接口:

import com.bean.Course;
import org.apache.ibatis.annotations.Select;

import java.util.List;

public interface CourseMapper {
    // 根據學生id查詢所選課程
    @Select("SELECT c.id, c.name FROM stu_cr sc, course c WHERE sc.cid=c.id AND sc.sid=#{id}")
    public abstract List<Course> selectBySid(Integer id);
}

3)使用注解配置 StudentMapper 接口:

package com.mapper;

import com.bean.Student;
import org.apache.ibatis.annotations.Many;
import org.apache.ibatis.annotations.Result;
import org.apache.ibatis.annotations.Results;
import org.apache.ibatis.annotations.Select;

import java.util.List;

public interface StudentMapper {
    // 查詢全部
    @Select("SELECT DISTINCT s.id,s.name,s.age FROM student s,stu_cr sc WHERE sc.sid=s.id")
    @Results({
            @Result(column="id", property="id"),
            @Result(column="name", property="name"),
            @Result(column="age", property="age"),
            @Result(
                    property="courses",   // 被包含對象的變量名
                    javaType=List.class,  // 被包含對象的實際數據類型
                    column="id",          // 根據查詢出student表的id來作為關聯條件,去查詢中間表和課程表
                    /*
                        many、@Many 一對多查詢的固定寫法
                        select屬性:指定調用哪個接口中的哪個查詢方法
                     */
                    many = @Many(select="com.mapper.CourseMapper.selectBySid")
            )
    })
    public abstract List<Student> selectAll();
}

4)測試類:

package com.mapper;

import com.bean.Classes;
import com.bean.Course;
import com.bean.Student;
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.jupiter.api.Test;

import java.io.IOException;
import java.io.InputStream;
import java.util.List;

public class ManyToManyTest {
    @Test
    public void testSelectAll() throws IOException {
        // 1.加載核心配置文件
        InputStream is = Resources.getResourceAsStream("MyBatisConfig.xml");

        // 2.獲取SqlSession工廠對象
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);

        // 3.通過工廠對象獲取SqlSession對象
        SqlSession sqlSession = sqlSessionFactory.openSession(true);

        // 4.獲取OneToOneMapper接口的實現類對象
        StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);

        // 5.調用實現類對象中的方法,接收結果
        List<Student> list = mapper.selectAll();

        // 6.處理結果
        for (Student student : list) {
            System.out.println(student.getId() + "," + student.getName() + "," + student.getAge());
            List<Course> courses = student.getCourses();
            for (Course cours : courses) {
                System.out.println("\t" + cours);
            }
        }

        // 7.釋放資源
        sqlSession.close();
        is.close();
    }
}

執行結果:

1,張三,23
	Course{id=1, name='語文'}
	Course{id=2, name='數學'}
2,李四,24
	Course{id=1, name='語文'}
	Course{id=2, name='數學'}

構建 SQL

之前在通過注解開發時,相關 SQL 語句都是自己直接拼寫的,一些關鍵字寫起來比較麻煩、而且容易出錯。因此,MyBatis 給我們提供了 org.apache.ibatis.jdbc.SQL 功能類,專門用於構建 SQL 語句。

image

查詢功能的實現:

  • 定義功能類並提供獲取查詢的 SQL 語句的方法。
  • @SelectProvider:生成查詢用的 SQL 語句注解。
    • type 屬性:生成 SQL 語句功能類對象
    • method 屬性:指定調用方法

新增功能的實現:

  • 定義功能類並提供獲取新增的 SQL 語句的方法。
  • @InsertProvider:生成新增用的 SQL 語句注解。
    • type 屬性:生成 SQL 語句功能類對象
    • method 屬性:指定調用方法

修改功能的實現:

  • 定義功能類並提供獲取修改的 SQL 語句的方法。
  • @UpdateProvider:生成修改用的 SQL 語句注解。
    • type 屬性:生成 SQL 語句功能類對象
    • method 屬性:指定調用方法

刪除功能的實現:

  • 定義功能類並提供獲取刪除的 SQL 語句的方法。
  • @DeleteProvider:生成刪除用的 SQL 語句注解。
    • type 屬性:生成 SQL 語句功能類對象
    • method 屬性:指定調用方法

案例:

  • Dao 層:
import com.itheima.domain.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.ArrayList;

/*
    Dao層接口
 */
public interface StudentDao {
    //查詢所有學生信息
    @Select("SELECT * FROM student")
    public abstract ArrayList<Student> findAll();

    //條件查詢,根據id獲取學生信息
    @Select("SELECT * FROM student WHERE sid=#{sid}")
    public abstract Student findById(Integer sid);
    
    //新增學生信息
    @Insert("INSERT INTO student VALUES (#{sid},#{name},#{age},#{birthday})")
    public abstract int insert(Student stu);
    
    //修改學生信息
    @Update("UPDATE student SET name=#{name},age=#{age},birthday=#{birthday} WHERE sid=#{sid}")
    public abstract int update(Student stu);
    
    //刪除學生信息
    @Delete("DELETE FROM student WHERE sid=#{sid}")
    public abstract int delete(Integer sid);

}
  • Dao 實現類:
import com.itheima.dao.StudentDao;
import com.itheima.domain.Student;
import com.itheima.service.StudentService;
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 java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;

/**
 * 業務層實現類
 */
public class StudentServiceImpl implements StudentService {

    @Override
    public List<Student> findAll() {
        ArrayList<Student> list = null;
        SqlSession sqlSession = null;
        InputStream is = null;
        try{
            //1.加載核心配置文件
            is = Resources.getResourceAsStream("MyBatisConfig.xml");

            //2.獲取SqlSession工廠對象
            SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);

            //3.通過工廠對象獲取SqlSession對象
            sqlSession = sqlSessionFactory.openSession(true);

            //4.獲取StudentDao接口的實現類對象
            StudentDao mapper = sqlSession.getMapper(StudentDao.class);

            //5.調用實現類對象的方法,接收結果
            list = mapper.findAll();

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            //6.釋放資源
            if(sqlSession != null) {
                sqlSession.close();
            }
            if(is != null) {
                try {
                    is.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

        //7.返回結果
        return list;
    }

    @Override
    public Student findById(Integer sid) {
        Student stu = null;
        SqlSession sqlSession = null;
        InputStream is = null;
        try{
            //1.加載核心配置文件
            is = Resources.getResourceAsStream("MyBatisConfig.xml");

            //2.獲取SqlSession工廠對象
            SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);

            //3.通過工廠對象獲取SqlSession對象
            sqlSession = sqlSessionFactory.openSession(true);

            //4.獲取StudentDao接口的實現類對象
            StudentDao mapper = sqlSession.getMapper(StudentDao.class);

            //5.調用實現類對象的方法,接收結果
            stu = mapper.findById(sid);

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            //6.釋放資源
            if(sqlSession != null) {
                sqlSession.close();
            }
            if(is != null) {
                try {
                    is.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

        //7.返回結果
        return stu;
    }

    @Override
    public void save(Student student) {
        SqlSession sqlSession = null;
        InputStream is = null;
        try{
            //1.加載核心配置文件
            is = Resources.getResourceAsStream("MyBatisConfig.xml");

            //2.獲取SqlSession工廠對象
            SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);

            //3.通過工廠對象獲取SqlSession對象
            sqlSession = sqlSessionFactory.openSession(true);

            //4.獲取StudentDao接口的實現類對象
            StudentDao mapper = sqlSession.getMapper(StudentDao.class);

            //5.調用實現類對象的方法,接收結果
            mapper.insert(student);

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            //6.釋放資源
            if(sqlSession != null) {
                sqlSession.close();
            }
            if(is != null) {
                try {
                    is.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    @Override
    public void update(Student student) {
        SqlSession sqlSession = null;
        InputStream is = null;
        try{
            //1.加載核心配置文件
            is = Resources.getResourceAsStream("MyBatisConfig.xml");

            //2.獲取SqlSession工廠對象
            SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);

            //3.通過工廠對象獲取SqlSession對象
            sqlSession = sqlSessionFactory.openSession(true);

            //4.獲取StudentDao接口的實現類對象
            StudentDao mapper = sqlSession.getMapper(StudentDao.class);

            //5.調用實現類對象的方法,接收結果
            mapper.update(student);

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            //6.釋放資源
            if(sqlSession != null) {
                sqlSession.close();
            }
            if(is != null) {
                try {
                    is.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    @Override
    public void delete(Integer sid) {
        SqlSession sqlSession = null;
        InputStream is = null;
        try{
            //1.加載核心配置文件
            is = Resources.getResourceAsStream("MyBatisConfig.xml");

            //2.獲取SqlSession工廠對象
            SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);

            //3.通過工廠對象獲取SqlSession對象
            sqlSession = sqlSessionFactory.openSession(true);

            //4.獲取StudentDao接口的實現類對象
            StudentDao mapper = sqlSession.getMapper(StudentDao.class);

            //5.調用實現類對象的方法,接收結果
            mapper.delete(sid);

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            //6.釋放資源
            if(sqlSession != null) {
                sqlSession.close();
            }
            if(is != null) {
                try {
                    is.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM