1. 程式人生 > 其它 >MyBatis學習大全(狂神秦疆版)

MyBatis學習大全(狂神秦疆版)

一、MyBatis

1.什麼是Mybaits

  • 概念:MyBatis 是一款優秀的持久層框架
  • 它支援自定義 SQL、儲存過程以及高階對映。
  • MyBatis 免除了幾乎所有的 JDBC 程式碼以及設定引數和獲取結果集的工作。
  • MyBatis 可以通過簡單的 XML 或註解來配置和對映原始型別、介面和 Java POJO(Plain Old Java Objects,普通老式 Java 物件)為資料庫中的記錄。

2.持久化

資料持久化

  • 持久化就是將程式的資料在持久狀態和瞬時狀態轉化的過程
  • 記憶體:斷電即失
  • 資料庫(Jdbc),io檔案持久化。
  • 生活:冷藏、罐頭。

為什麼需要需要持久化?

  • 有一些物件,不能讓他丟掉。
  • 記憶體太貴了

3.持久層

Dao層,Service層,Controller層....

  • 完成持久化工作的程式碼塊
  • 層界限十分明顯

4.為什麼需要Mybatis?

  • 方便
  • 傳統的JDBC程式碼太複雜了。簡化。框架。自動化。
  • 不用Mybatis也可以。更容易上手。技術沒有高低之分·
  • 優點:
    • 簡單易學
    • 靈活
    • sql和程式碼的分離,提高了可維護性。
    • 提供對映標籤,支援物件與資料庫的orm欄位關係對映。
    • 提供物件關係對映標籤,支援物件關係組建維護
    • 提供xml標籤,支援編寫動態sql。

二、第一個Mybatis程式

思路:搭建環境-->匯入Mybatis-->編寫程式-->測試!

1.搭建環境

搭建資料庫

新建專案

<?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.Aaron</groupId>
    <artifactId>MyBatis-Study</artifactId>
    <packaging>pom</packaging>
    <version>1.0-SNAPSHOT</version>
    <modules>
        <module>mybatis-01</module>
    </modules>
    <!--匯入依賴-->
<dependencies>
<!--資料庫驅動-->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>8.0.28</version>
    </dependency>
<!--MyBatis-->
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis</artifactId>
        <version>3.5.9</version>
    </dependency>
<!--junit-->
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
    </dependency>
</dependencies>
</project>

2.建立一個模組

  • 編寫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.cj.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=trie&amp;useUnicode=true&amp;characterEncoding=UTF-8&amp;serverTime=Asia/shanghai"/>
                <property name="username" value="root"/>
                <property name="password" value="root"/>
            </dataSource>
        </environment>
    </environments>
    <mappers>
        <mapper resource="org/mybatis/example/BlogMapper.xml"/>
    </mappers>
</configuration>
  • 編寫mybatis工具類
public class MyBatisUtils {

    private static SqlSessionFactory sqlSessionFactory;

    static {
        try {
            //獲取sqlSessionFactory物件
            String resource = "mybatis-config.xml";
            InputStream inputStream = Resources.getResourceAsStream(resource);
            sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }


    //既然有了 SqlSessionFactory,顧名思義,我們可以從中獲得 SqlSession 的例項。
    // SqlSession 提供了在資料庫執行 SQL 命令所需的所有方法。
    public static SqlSession getSqlSession() {
        return sqlSessionFactory.openSession();
    }
}

3.編寫程式碼

  • 實體類
public class user {
    private int id;
    private String name;
    private String password;

    public user() {
    }

    public user(int id, String name, String password) {
        this.id = id;
        this.name = name;
        this.password = password;
    }

    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 getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    @Override
    public String toString() {
        return "user{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", password='" + password + '\'' +
                '}';
    }
}
  • Dao介面
package com.Aaron.dao.User;

import java.util.List;
 import com.Aaron.pojo.User;
/**
 * @Author: Alskaboo
 * @Date: 2022-04-16 15:08
 **/
public interface UserDao {
    List<User> getUserList();
}
  • 介面實現類由原來的impl轉換為一個Mapper配置檔案
<?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">
<!--名稱空間=繫結一個對應的Dao/Mapper介面-->
<mapper namespace="com.Aaron.dao.User.UserDao">
    <select id="getUserList" resultType="com.Aaron.pojo.User">
        select * from mybatis.user;
    </select>
</mapper>

4.測試

註冊配置檔案mapper

junit測試

public class UserMapperTest {
    @Test
    public void test(){
        //第一步:獲取SqlSession物件
        SqlSession sqlSession = MyBatisUtils.getSqlSession();
        //方式一執行SQL
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        List<User> userList = mapper.getUserList();
        //遍歷表格
        for (User user : userList) {
            System.out.println(user);
        }
//關閉SqlSession
        sqlSession.close();
    }
}

遇到的問題:

  1. 配置檔案沒有註冊
  2. 繫結介面錯誤
  3. 方法名不對
  4. 返回型別不對
  5. Maven匯出資源
<!--在build中配置resources,來防止我們資源匯出失敗的問題-->
<build>
    <!--   **.xml寫在src找不到問題解決方案     -->
    <resources>
        <resource>
            <!-- directory:指定資原始檔的位置 -->
            <directory>src/main/java</directory>
            <includes>
                <!-- “**” 表示任意級目錄    “*”表示任意任意檔案 -->
                <!-- mvn resources:resources :對資源做出處理,先於compile階段  -->
                <include>**/*.properties</include>
                <include>**/*.xml</include>
            </includes>
            <!--  filtering:開啟過濾,用指定的引數替換directory下的檔案中的引數(eg. ${name}) -->
            <filtering>true</filtering>
        </resource>
        <resource>
            <directory>src/main/resources</directory>
            <includes>
                <include>**/*.properties</include>
                <include>**/*.xml</include>
            </includes>
            <filtering>true</filtering>
        </resource>
    </resources>
</build>

三、CRUD(增刪改查)

1、namespace

namespace中的包名和Mapper介面名字一致!

2、select

選擇,查詢語句;

  • id:就是對應的namespace中的方法名;
  • resultType: Sql語句執行的返回值!
  • parameterType :引數型別!
public interface UserMapper {
    List<User> getUserList();
}


<select id="getUserList" resultType="com.Aaron.pojo.User">
  1. 編寫介面
    /**
     * 獲取全部資訊
     * @return
     */
    List<User> getUserList();
  1. 編寫對應的mapper中的SQL語句
    <select id="getUserList" resultType="com.Aaron.pojo.User">
        select *
        from mybatis.user;
    </select>
  1. 測試
public class UserMapperTest {
    @Test
    public void test() {
        //第一步:獲取SqlSession物件
        SqlSession sqlSession = MyBatisUtils.getSqlSession();

        try{
            //方式一:getMapper
            UserMapper mapper = sqlSession.getMapper(UserMapper.class);
            List<User> userList = mapper.getUserList();

            //方式二:
//            List<User> userList = sqlSession.selectList("com.Aaron.dao.User.UserMapper.getUserList");

            //遍歷表格
            for (User user : userList) {
                System.out.println(user);
            }
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            //關閉SqlSession
            sqlSession.close();
        }
    }

3、Insert

  1. 編寫介面
    /**
     * insert一個使用者
     * @param user
     * @return
     */
    int addUser(User user);
  1. 編寫對應的mapper中的SQL語句
    <insert id="addUser" parameterType="com.Aaron.pojo.User">
        insert into mybatis.user(id, name, password) VALUE (#{id}, #{name}, #{password});
    </insert>
  1. 測試
    @Test
    public void addUser(){
        SqlSession sqlSession = MyBatisUtils.getSqlSession();

        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        int res = mapper.addUser(new User(4, "小黃", "1314"));

        if (res>0){
            System.out.println("插入成功");
        }else {
            System.out.println("插入失敗");
        }
        //提交事務
        sqlSession.commit();
        sqlSession.close();
    }

4、update

  1. 編寫介面
    /**
     * 修改使用者
     * @param user
     * @return
     */
    int updateUser(User user);
  1. 編寫對應的mapper中的SQL語句
    <update id="updateUser" parameterType="com.Aaron.pojo.User">
        update mybatis.user
        set name=#{name},
            password=#{password}
        where id = #{id};
    </update>
  1. 測試
   @Test
    public void updateUser(){
        SqlSession sqlSession = MyBatisUtils.getSqlSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);

        mapper.updateUser(new User(4,"小紅","2020211891"));

        sqlSession.commit();
        sqlSession.close();
    }

5、Delete

  1. 編寫介面
    /**
     *
     * @param id
     * @return
     */
    int deleteUser(int id);
  1. 編寫對應的mapper中的SQL語句
    <delete id="deleteUser" parameterType="int">
        delete
        from mybatis.user
        where id = #{id};
    </delete>
  1. 測試
    @Test
    public void deleteUser(){
        SqlSession sqlSession = MyBatisUtils.getSqlSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);

        mapper.deleteUser(4);

        sqlSession.commit();
        sqlSession.close();
    }

注意點:

  • 增刪改需要提交事務!

6、分析錯誤

  • 標籤不要匹配錯
  • resource繫結mapper,需要使用路徑!
  • 程式配置檔案必須符合規範!
  • NullPointerException,沒有註冊到資源!
  • 輸出的xml檔案中存在中文亂碼問題!
  • maven資源沒有匯出問題!

7、萬能Map

假設,我們的實體類,或者資料庫中的表,欄位或者引數過多,我們應當考慮使用Map!

    //萬能Map
    int addUser2(Map<String, Object> map);
    <insert id="addUser2" parameterType="map">
        insert into mybatis.user(id, name, password) VALUE (#{userid}, #{username}, #{password});
    </insert>
    @Test
    public void addUser2(){
        SqlSession sqlSession = MyBatisUtils.getSqlSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);

        Map<String, Object> map = new HashMap<String, Object>();
        map.put("userid",5);
        map.put("username","Aaron");
        map.put("password","171452");
        mapper.addUser2(map);

        sqlSession.commit();
        sqlSession.close();
    }

Map傳遞引數,直接在sql中取出key即可!【parameterType="map"】

物件傳遞引數,直接在sql中取物件的屬性即可!【parameterType="Object"】

只有一個基本型別引數的情況下,可以直接在sql中取到!

多個引數用Map,或者註解!

8、思考題

模糊查詢怎麼寫?

  1. Java程式碼執行的時候,傳遞萬用字元%%
List<User> userList = mapper.getUserLike("%李%");
  1. 在sql拼接中使用萬用字元!
select * from mybatis.user where name like "%"#{value}"%"

四、配置解析

1、核心配置檔案

  • mybatis-config.xml
  • MyBatis的配置檔案包含了會深深影響MyBatis行為的設定和屬性
configuration(配置)
properties(屬性)
settings(設定)
typeAliases(類型別名)
typeHandlers(型別處理器)
objectFactory(物件工廠)
plugins(外掛)
environments(環境配置)
environment(環境變數)
transactionManager(事務管理器)
dataSource(資料來源)
databaseIdProvider(資料庫廠商標識)
mappers(對映器)

2、環境配置(environments)

  1. MyBatis 可以配置成適應多種環境
  2. 不過要記住:儘管可以配置多個環境,但每個 SqlSessionFactory 例項只能選擇一種環境。

學會使用配置多套執行環境!

Mybatis預設的事務管理器就是JDBC ,連線池: POOLED

3、屬性(properties)

我們可以通過properties屬性來實現引用配置檔案

這些屬性可以在外部進行配置,並可以進行動態替換。你既可以在典型的 Java 屬性檔案中配置這些屬性,也可以在 properties 元素的子元素中設定。【db.properties】

編寫一個配置檔案

db.properties

driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=UTF-8&serverTime=Asia/shanghai
username=root
password=root

在核心配置檔案中映入

<!--引入外部配置檔案-->
<properties resource="db.properties ">
<property name="username" value="root" />
<property name="password" value="root"/>
</properties>
  • 可以直接引入外部檔案
  • 可以在其中增加一些屬性配置
  • 如果兩個檔案有同一個欄位,優先使用外部配置檔案的!

4、類型別名(typeAliases)

  • 類型別名可為 Java 型別設定一個縮寫名字。
  • 它僅用於 XML 配置,意在降低冗餘的全限定類名書寫。
<!--給實體類起別名-->
<typeAliases>
    <typeAlias type="com.Aaron.pojo.User" alias="User"/>
</typeAliases>

也可以指定一個包名,MyBatis 會在包名下面搜尋需要的 Java Bean

在沒有註解的情況下,會使用 Bean 的首字母小寫的非限定類名來作為它的別名

<typeAliases>
    <package name="com.Aaron.pojo"/>
</typeAliases>

在實體類比較少的時候,使用第一種方式。

如果實體類十分多,建議使用第二種。
第一種可以DIY別名,第二種則不行,如果非要改,需要在實體上增加註解

@Alias("user")
pub1ic class user {
    
}

5、對映器(mappers)

MapperRegistry:註冊我們的Mapper檔案

<!--每一個Mapper均需要註冊-->
<mappers>
    <mapper resource="com/Aaron/dao/UserMapper.xml"/>
    <!--        <package name="com.Aaron.dao"/>-->
    <!--        <mapper class="com.Aaron.dao"/>-->
</mappers>

6、生命週期和作用域

生命週期和作用域,是至關重要的,因為錯誤的使用會導致非常嚴重的併發問題

sqlSessionFactoryBuilder:

  • 一旦建立了SqlSessionFactory,就不再需要它了
  • 區域性變數

sqlSessionFactory:

  • 資料庫連線池
  • sqlSessionFactory一旦被建立就應該在應用的執行期間一直存在,沒有任何理由丟棄它或重新建立另一個例項。
  • 因此 SqlSessionFactory的最佳作用域是應用作用域
  • 單例模式或靜態單例模式

sqlSession

  • 連線到連線池的一個請求!
  • sqlSession的例項不是執行緒安全的,因此是不能被共享的,所以它的最佳的作用域是請求或方法作用域。
  • 用完之後需要趕緊關閉,否則資源被佔用!

這裡面的每一個Mapper,代表每一個業務!

五、ResultMap

1、資料庫中的欄位

新建一個專案,但是實體類欄位不一樣

public class User {
    private int id;
    private String name;
    private String pwd;
}

測試出現問題

select * from mybatis.user where id = #{id};
//型別處理器
select id,name,password from mybatis.user where id = #{id};

解決方法:

  • 起別名
<select id="getUserById" parameterType="int" resultType="User">
    select id, name, password as pwd
    from mybatis.user
    where id = #{id};
</select>

2、ResultMap

結果集對映

id  name  password
id  name  pwd
<!--結果集對映-->
<resultMap id="UserMap" type="User">
    <!--column資料庫中的欄位,property實體類中的欄位名-->
    <result column="password" property="pwd"/>
</resultMap>
<select id="getUserById" parameterType="int" resultMap="UserMap">
    select *
    from mybatis.user
    where id = #{id};
</select>
  • resultMap元素是MyBatis 中最重要最強大的元素
  • ResultMap的設計思想是,對於簡單的語句根本不需要配置顯式的結果對映,而對於複雜一點的語句只需要描述它們的關係就行了。

六、日誌

1、日誌工廠

如果資料庫出現了異常,我們需要排除,日誌就是最好的助手!

曾經:sout debug

現在:日誌工廠!

  • SLF4J
  • LOG4J(重點)
  • LOG4J2
  • JDK_LOGGING
  • COMMONS_LOGGING
  • STDOUT_LOGGING(重點)
  • NO_LoGGING

在Mybatis中具體使用哪個,在設定中實現!

STDOUT_LOGGING(重點)

在mybatis核心配種檔案中,配置我們的日誌!

<settings>
    <setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>

2、LOG4J

什麼是LOG4J?

  • Log4j是Apache的一個開源專案,通過使用Log4j,我們可以控制日誌資訊輸送的目的地是控制檯、檔案、GUI元件
  • 我們也可以控制每一條日誌的輸出格式;
  • 通過定義每一條日誌資訊的級別,我們能夠更加細緻地控制日誌的生成過程。
  • 通過一個配置檔案來靈活地進行配置,而不需要修改應用的程式碼。
  1. 匯入LOG4J的Jar包
<dependency>
    <groupId>log4j</groupId>
    <artifactId>log4j</artifactId>
    <version>1.2.17</version>
</dependency>
  1. log4j的properties

    ### 配置根 ###
    log4j.rootLogger=debug,console ,file
    ### 配置輸出到控制檯 ###
    log4j.appender.console=org.apache.log4j.ConsoleAppender
    log4j.appender.console.Target=System.out
    log4j.appender.console.layout=org.apache.log4j.PatternLayout
    log4j.appender.console.layout.ConversionPattern=[%c] - %m%n
    ### 配置輸出到檔案,且大小到達指定尺寸的時候產生一個新的檔案 ###
    log4j.appender.file=org.apache.log4j.RollingFileAppender
    log4j.appender.file.File=./log/Aaron.log
    log4j.appender.file.MaxFileSize=10KB
    log4j.appender.file.Threshold=DEBUG
    log4j.appender.file.layout=org.apache.log4j.PatternLayout 
    log4j.appender.file.layout.ConversionPattern=[%p][%d{yy-MM-dd}][%c]%m%n
    ### 配置輸出級別 ###
    log4j.logger.org.mybatis=DEBUG
    log4j.logger.java.sql=DEBUG
    log4j.logger.java.Statement=DEBUG
    log4j.logger.java.ResultSet=DEBUG
    log4j.logger.java.PreparedStatement=DEBUG
    
  2. log4j的配置實現

<settings>
    <setting name="logImpl" value="LOG4J"/>
</settings>
  1. log4j的使用
  2. 簡單測試
  • 匯入包import org.apache.log4j.Logger;
  • 日誌物件,引數為當前類的class
static Logger logger = Logger.getLogger(UserMapperTest.class);
  • 日誌級別
logger.info("info:進入了testLog4j");
logger.debug("debug:進入了testLog4j");
logger.error("error:進入了testLog4j");

七、分頁

​ 為什麼要分頁:減少資料的處理量

1、使用Limit分頁

語法:SELECT * from user limit startIndex,pageSize;
SELECT * from user limit 4; #[0,n]

使用Mybatis實現分頁,核心SQL

  1. 介面
List<User> getUserByLimit(HashMap<String, Integer> map);
  1. Mapper.XML
    <select id="getUserByLimit" parameterType="map" resultMap="UserMap">
        select * from mybatis.user limit #{startIndex},#{pageSize}
    </select>
  1. 測試
    static Logger logger = Logger.getLogger(UserMapperTest.class);
    @Test
    public  void getUserByLimit(){
        SqlSession sqlSession = MyBatisUtils.getSqlSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);

        HashMap<String, Integer> map = new HashMap<String, Integer>();
        map.put("startIndex",0);
        map.put("pageSize",2);

        List<User> userList = mapper.getUserByLimit(map);
        for (User user : userList) {
            System.out.println(user);
        }
    }

2、RowBounds分頁

不在使用SQL實現分頁

  1. 介面
    List<User> getUserByRowBounds();
  1. mapper.xml
    <select id="getUserByRowBounds" resultMap="UserMap">
        select *
        from mybatis.user
    </select>
  1. 測試

3、分頁外掛

Mybatis外掛

八、使用註解開發

1、面向介面程式設計

  1. 註解在介面上實現
@Select("select * from user")
List<User> getUserList();
  1. 需要核心配置檔案中繫結介面!
<!--繫結介面-->
<mappers>
    <mapper class="com.Aaron.dao.UserMapper"/>
</mappers>
  1. 測試

本質:反射機制

實現底層:動態代理!

2、Mybatis詳細的執行流程

狂神說Mybatis16集,底層原理

3、CRUD

我們可以在工具類建立的時候實現自動提交事務!

public static SqlSession getSqlSession() {
    return sqlSessionFactory.openSession(true);//true
}

編寫介面

@Select("select * from user")
List<User> getUserList();

@Select("select * from user where id = #{id}")
User getUserById(@Param("id")int id);

@Insert("insert into user(id,name,password) values (#{id}, #{name}, #{password})")
int addUser(User user);

@Update("update user set name=#{name},password=#{password} where id=#{id}")
int updateUser(User user);

@Delete("delete from user where id = #{id}")
int deleteUser(@Param("id") int id);

測試

        SqlSession sqlSession = MyBatisUtils.getSqlSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);

//        User userById = mapper.getUserById(1);
//        System.out.println(userById);
        
//        mapper.addUser(new User(12,"小周","1235"));
        
//        mapper.updateUser(new User(5,"小洋","10086"));
        
//        mapper.deleteUser(1);
        sqlSession.close();
    }

【注意:我們必須要講介面註冊繫結到我們的核心配置檔案中!】

    <!--繫結介面-->
    <mappers>
        <mapper class="com.Aaron.dao.UserMapper"/>
    </mappers>

4、關於@Param()註解

  • 基本型別的引數或者String型別,需要加上
  • 引用型別不需要加
  • 如果只有一個基本型別的話,可以忽略,但是建議大家都加上!
  • 我們在SQL中引用的就是我們這裡的@Param()中設定的屬性名!

#{} 和 ${}的區別

九、Lombok外掛

使用步驟:

  1. 在IDEA中安裝Lombok外掛!

  2. 在專案中匯入lombok的jar包

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.16</version>
        </dependency
  1. 使用註解
@Getter and @Setter
@FieldNameConstants
@ToString
@EqualsAndHashCode
@AllArgsConstructor, @RequiredArgsConstructor and @NoArgsConstructor
@Log, @Log4j, @Log4j2, @Slf4j, @XSlf4j, @CommonsLog, @JBossLog, @Flogger, @CustomLog
@Data
@Builder
@SuperBuilder
@Singular
@Delegate
@Value
@Accessors
@Wither
@With
@SneakyThrows
@val
@var
experimental @var
@UtilityClass
Lombok config system

解釋:

@Data 無參構造,get、set、tostring.hashcode,equals
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode
@ToString

十、多對一處理

1、測試環境搭建

  1. 匯入lombok

  2. 匯入實體類Teacher(id,name)、Student(id, name,tid)

  3. 建立Mapper介面

  4. 建立Mapper.xml檔案

  5. 在核心配置檔案中繫結註冊我們的Mapper介面或者檔案!

  6. 測試是否成功

2、按照查詢巢狀處理

    <!--
思路:
    1.查詢所有的學生資訊
    2.根據查詢出來的學生的tid.尋找對應的老師!
    map
    -->
    <resultMap id="StudentTeacher" type="Student">
        <!--複雜的屬性,單獨處理
        物件(老師):association
        集合(學生):collection
        -->
        <association property="teacher" column="tid" javaType="Teacher" select="getTeacherById"/>
    </resultMap>

    <select id="getStudent" resultMap="StudentTeacher">
        select *
        from mybatis.student;
    </select>



    <select id="getTeacherById" resultType="Teacher">
        select *
        from mybatis.teacher
        where id = #{id}
    </select>
    @Test
    public void testStudent(){
        SqlSession sqlSession = MyBatisUtils.getSqlSession();

        StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
        List<Student> studentList = mapper.getStudent();
        for (Student student : studentList) {
            System.out.println(student);
        }

        sqlSession.close();
    }

3、按照查詢巢狀處理

<!--按照結果巢狀處理-->
    <select id="getStudent2" resultMap="StudentTeacher2">
        select s.id sId, s.name sName, t.id tId,t.name tName
        from mybatis.student s,
             mybatis.teacher t
        where s.tid = t.id;
    </select>

    <resultMap id="StudentTeacher2" type="Student">
        <result property="id" column="sId"/>
        <result property="name" column="sName"/>
        <association property="teacher" javaType="Teacher">
            <result property="name" column="tName"/>
            <result property="id" column="tId"/>
        </association>
    </resultMap>
    @Test
    public void testStudent2(){
        SqlSession sqlSession = MyBatisUtils.getSqlSession();

        StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
        List<Student> studentList = mapper.getStudent2();
        for (Student student : studentList) {
            System.out.println(student);
        }

        sqlSession.close();
    }

十一、一對多處理

比如:一個老師擁有多個學生!

1、環境搭建

實體類

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Student {
    private int id;
    private String name;

    //學生需要關聯一個老師
    private int tid;
}

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Teacher {
    private int id;
    private String name;

    private List<Student> students;
}

2、按照結果巢狀處理

    <select id="getTeacher" resultMap="TeacherStudent">
        select s.id sid, s.name sname, t.name tname, t.id tid
        from mybatis.teacher t,
             mybatis.student s
        where s.tid = t.id
          and t.id = #{tid}
    </select>

    <resultMap id="TeacherStudent" type="Teacher">
        <result property="id" column="tid"/>
        <result property="name" column="tname"/>

        <!--複雜的屬性,我們需要單獨處理
        物件:association   集合:collection
        javaType="”指定屬性的型別!
        集合中的泛型資訊,我們使用ofType獲取
        -->
        <collection property="students" ofType="Student">
            <result property="id" column="sid"/>
            <result property="name" column="sname"/>
            <result property="tid" column="tid"/>
        </collection>
    </resultMap>

3、按照查詢巢狀處理

    <select id="getTeacher2" resultMap="TeacherStudent2">
         select * from mybatis.teacher where id = #{tid}
    </select>

    <resultMap id="TeacherStudent2" type="Teacher">
        <collection property="students" javaType="ArrayList" ofType="Student" select="getStudentByTeacherId" column="id"/>
    </resultMap>

    <select id="getStudentByTeacherId" resultType="Student">
        select  * from mybatis.student where id = #{tid}
    </select>

小結

  1. 關聯– association【多對一】

  2. 集合- collection【一對多】

  3. javaType & ofType

  4. JavaType 用來指定實體類中屬性的型別

  5. ofType 用來指定對映到List或者集合中的pojo型別,泛型中的約束型別!

注意點:

  • 保證SQL的可讀性,儘量保證通俗易懂
  • 注意一對多和多對一中,屬性名和欄位的問題!
  • 如果問題不好排查錯誤,可以使用日誌,建議使用Log4j

面試高頻

  • Mysql引擎:Innodb MyISAM MEMORY MERGE
  • lnnoDB底層原理
  • 索引
  • 索引優化!

十二、動態SQL

什麼是動態SQL:動態SQL就是指根據不同的條件生成不同的SQL語句

利用動態SQL這一特性可以徹底擺脫這種痛苦。

動態sQL元素和JSTL或基於類似 XML的文字處理器相似。在MyBatis之前的版本中,有很多元素需要花時間瞭解。MyBatis 3大大精簡了元素種類,現在只需學習原來一半的元素便可。MyBatis採用功能強大的基於OGNL的表示式來淘汰其它大部分元素。

if
choose (when,otherwise)
trim (where,set)
foreach

1、搭建環境

CREATE TABLE `blog`(
`id` VARCHAR(50) NOT NULL COMMENT '部落格id',
`title` VARCHAR(100) NOT NULL COMMENT '部落格標題',
`author` VARCHAR(30) NOT NULL COMMENT '部落格作者',
`create_time` datetime NOT NULL COMMENT '建立時間',
`views` INT(30) NOT NULL COMMENT '瀏覽量'
)ENGINE=INNODB DEFAULT CHARSET=UTF8

建立一個基礎工程

  1. 導包
  2. 編寫配置檔案
  3. 編寫實體類
@Data
@AllArgsConstructor
@NoArgsConstructor
public class blog {
    private int id;
    private String title;
    private String author;
    private Date createTime;
    private int views;
}
  1. 編寫實體類對應Mapper介面和Mapper.XML檔案

插入資料

    //插入資料
    int addBolg(Blog blog);
    <insert id="addBolg" parameterType="blog">
        insert into mybatis.blog(id, title, author, create_time, views)
            VALUE (#{id}, #{title}, #{author}, #{createTime}, #{views});
    </insert>
    @Test
    public void addBlogTest(){
        SqlSession sqlSession = MyBatisUtils.getSqlSession();
        BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);

        Blog blog = new Blog();
        blog.setId(IDUtils.getId());
        blog.setTitle("Mybatis如此簡單");
        blog.setAuthor("小菜");
        blog.setCreateTime(new Date());
        blog.setViews(2020211891);

        mapper.addBolg(blog);

        blog.setId(IDUtils.getId());
        blog.setTitle("Java如此簡單");
        mapper.addBolg(blog);

        blog.setId(IDUtils.getId());
        blog.setTitle("Spring如此簡單");
        mapper.addBolg(blog);

        blog.setId(IDUtils.getId());
        blog.setTitle("微服務如此簡單");
        mapper.addBolg(blog);


        sqlSession.close();
    }

2、If

    List<Blog> queryBlogIf(Map map);
    <select id="queryBlogIf" parameterType="map" resultType="blog">
        select * from mybatis.blog where 1=1
        <if test="title != null">
            and title = #{title}
        </if>
        <if test="author != null">
            and title = #{author}
        </if>
    </select>

test

    @Test
    public void queryBlogIf(){
        SqlSession sqlSession = MyBatisUtils.getSqlSession();
        BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);

        HashMap map = new HashMap();
        map.put("title","Java如此簡單");
        map.put("author","小菜");

        List<Blog> blogs = mapper.queryBlogIf(map);
        for (Blog blog : blogs) {
            System.out.println(blog);
        }

        sqlSession.close();
    }

3、choose (when,otherwise)

<select id="queryBlogChoose" resultType="blog" parameterType="map">
    select * from mybatis.blog
    <where>

        <choose>
            <when test="title != null">
                title = #{title}
            </when>
            <when test="author != null">
                and author = #{author}
            </when>

            <otherwise>
                and views = #{views}
            </otherwise>
        </choose>
    </where>
</select>
    @Test
    public void queryBlogChoose(){
        SqlSession sqlSession = MyBatisUtils.getSqlSession();
        BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);

        HashMap map = new HashMap();
        map.put("title","Java如此簡單");
        map.put("views",2020211890);

        List<Blog> blogs = mapper.queryBlogChoose(map);
        for (Blog blog : blogs) {
            System.out.println(blog);
        }

        sqlSession.close();
    }

4、trim (where,set)

where

<select id="queryBlogIf" parameterType="map" resultType="blog">
    select * from mybatis.blog
    <where>
        <if test="title != null">
            title = #{title}
        </if>
        <if test="author != null">
            and author = #{author}
        </if>
    </where>
</select>
    @Test
    public void queryBlogIf(){
        SqlSession sqlSession = MyBatisUtils.getSqlSession();
        BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);

        HashMap map = new HashMap();
        map.put("title","Java如此簡單");
        map.put("author","小菜");

        List<Blog> blogs = mapper.queryBlogIf(map);
        for (Blog blog : blogs) {
            System.out.println(blog);
        }

        sqlSession.close();
    }

set

    //修改
    int updateBlog(Map map);
<update id="updateBlog" parameterType="map">
    update mybatis.blog
    <set>
        <if test="title != null">
            title = #{title},
        </if>
        <if test="author != null">
            author = #{author}
        </if>
    </set>
    where id = #{id}
</update>
    @Test
    public void updateBlog(){
        SqlSession sqlSession = MyBatisUtils.getSqlSession();
        BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);

        HashMap map = new HashMap();
        //map.put("title","微服務如此簡單");
        map.put("author","小賀");
        map.put("id", "53f0d40c77ca4b679711a0950d910591");
        //map.put("views",2020211890);
        mapper.updateBlog(map);

        sqlSession.close();
    }

5、sql語句

有的時候,我們可能會將一些功能的部分抽取出來,方便複用!

  1. 使用SQL標籤抽取公共的部分
    <sql id="queryBlogIf_Head">
        <if test="title != null">
            title = #{title}
        </if>
        <if test="author != null">
            and author = #{author}
        </if>
    </sql>
  1. 在需要使用的地方使用Include標籤引用即可
	<select id="queryBlogIf" parameterType="map" resultType="blog">
        select * from mybatis.blog
        <where>
            <include refid="queryBlogIf_Head"></include>
        </where>
    </select>

注意事項:

  • 最好基於單表來定義SQL片段!
  • 不要存在where標籤

所謂的動態SQL,本質還是SQL語句,只是我們可以在SQL層面,去執行一個邏輯程式碼

6、foreach

    List<Blog> queryBlogForeach(Map map);
   <!--
   select * from mybatis.blog where 1=1 and (id=1 or id=2 or id=3)
   -->
   <select id="queryBlogForeach" parameterType="map" resultType="blog">
       select * from mybatis.blog
       <where>
           <foreach collection="ids" item="id" open="and (" close=")" separator="or">
               id = #{id}
           </foreach>
       </where>
   </select>
    @Test
    public void queryBlogForeach(){
        SqlSession sqlSession = MyBatisUtils.getSqlSession();
        BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);

        HashMap map = new HashMap();

        ArrayList<Integer> ids = new ArrayList<>();
        ids.add(1);
        ids.add(2);
        ids.add(3);

        map.put("ids",ids);

        List<Blog> blogs = mapper.queryBlogForeach(map);
        for (Blog blog : blogs) {
            System.out.println(blog);
        }

        sqlSession.close();
    }

動態SQL就是在拼接SQL語句,我們只要保證SQL的正確性,按照SQL的格式,去排列組合就可以了

十三、快取

1、簡介

  1. 什麼是快取[Cache]?

    • 存在記憶體中的臨時資料。
    • 將使用者經常查詢的資料放在快取(記憶體)中,使用者去查詢資料就不用從磁碟上(關係型資料庫資料檔案)查詢,從快取中查詢,從而提高查詢效率,解決了高併發(高併發,高可用,高效能)系統的效能問題。
  2. 為什麼使用快取?

    • 減少和資料庫的互動次數,減少系統開銷,提高系統效率。
  3. 什麼樣的資料能使用快取?

    • 經常查詢並且不經常改變的資料。【使用快取】

2、Mybatis快取

  • MyBatis包含一個非常強大的查詢快取特性,它可以非常方便地定製和配置快取。快取可以極大的提升查詢效率。
  • MyBatis系統中預設定義了兩級快取:一級快取二級快取
    • 預設情況下,只有一級快取開啟。(SqlSession級別的快取,也稱為本地快取)。
    • 二級快取需要手動開啟和配置,他是基於namespace級別的快取。
    • 為了提高擴充套件性,MyBatis定義了快取介面Cache。我們可以通過實現Cache介面來自定義二級快取

3、一級快取

  • 一級快取也叫本地快取:
    • 與資料庫同一次會話期間查詢到的資料會放在本地快取中。
    • 以後如果需要獲取相同的資料,直接從快取中拿,沒必須再去查詢資料庫;

測試步驟:

  1. 開啟日誌!
  2. 測試在一個Sesion中查詢兩次相同記錄
  3. 檢視日誌輸出

4、二級快取

  • 二級快取也叫全域性快取,一級快取作用域太低了,所以誕生了二級快取
  • 基於namespace級別的快取,一個名稱空間,對應一個二級快取;
  • 工作機制
    • 一個會話查詢一條資料,這個資料就會被放在當前會話的一級快取中;
    • 如果當前會話關閉了,這個會話對應的一級快取就沒了;但是我們想要的是,會話關閉了,一級快取中的資料被儲存到二級快取中;
    • 新的會話查詢資訊,就可以從二級快取中獲取內容;
    • 不同的mapper查出的資料會放在自己對應的快取(map)中;

步驟:

  1. 開啟全域性快取
        <!--顯示開啟的全域性快取-->
        <setting name="cacheEnabled" value="true"/>
  1. 在要使用二級快取的Mapper中開啟
<!--在當前的Mapper中開啟二級快取-->
    <cache eviction="FIFO" 
           flushInterval="6_0000" 
           size="512" 
           readOnly="true"/>
  1. 測試

    1. 問題:我們需要將實體類序列化!否則就會報錯!

      caused by: java.io.Notserializab1eException: com.kuang.pojo.user
      

小結:

  • 只要開啟了二級快取,在同一個Mapper下就有效
  • 所有的資料都會先放在一級快取中;
  • 只有當會話提交,或者關閉的時候,才會提交到二級快取中!

5、快取原理

6、自定義快取-ehcache

導包!

<!--快取-->
        <dependency>
            <groupId>org.mybatis.caches</groupId>
            <artifactId>mybatis-ehcache</artifactId>
            <version>1.1.0</version>
        </dependency>

在mapper中指定我們的快取實現

    <cache type="org.mybatis.caches.ehcache.EhcacheCache"/>

resources檔案下ehcache.xml

<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd"
         updateCheck="false">
    <!--
       diskStore:為快取路徑,ehcache分為記憶體和磁碟兩級,此屬性定義磁碟的快取位置。引數解釋如下:
       user.home – 使用者主目錄
       user.dir  – 使用者當前工作目錄
       java.io.tmpdir – 預設臨時檔案路徑
     -->
    <diskStore path="./tmpdir/Tmp_EhCache"/>
    <!--
       defaultCache:預設快取策略,當ehcache找不到定義的快取時,則使用這個快取策略。只能定義一個。
     -->

    <defaultCache
            eternal="false"
            maxElementsInMemory="10000"
            overflowToDisk="false"
            diskPersistent="false"
            timeToIdleSeconds="1800"
            timeToLiveSeconds="259200"
            memoryStoreEvictionPolicy="LRU"/>

    <!--
     name:快取名稱。
     maxElementsInMemory:快取最大數目
     maxElementsOnDisk:硬碟最大快取個數。
     eternal:物件是否永久有效,一但設定了,timeout將不起作用。
     overflowToDisk:是否儲存到磁碟,當系統宕機時
     timeToIdleSeconds:設定物件在失效前的允許閒置時間(單位:秒)。僅當eternal=false物件不是永久有效時使用,可選屬性,預設值是0,也就是可閒置時間無窮大。
     timeToLiveSeconds:設定物件在失效前允許存活時間(單位:秒)。最大時間介於建立時間和失效時間之間。僅當eternal=false物件不是永久有效時使用,預設是0.,也就是物件存活時間無窮大。
     diskPersistent:是否快取虛擬機器重啟期資料 Whether the disk store persists between restarts of the Virtual Machine. The default value is false.
     diskSpoolBufferSizeMB:這個引數設定DiskStore(磁碟快取)的快取區大小。預設是30MB。每個Cache都應該有自己的一個緩衝區。
     diskExpiryThreadIntervalSeconds:磁碟失效執行緒執行時間間隔,預設是120秒。
     memoryStoreEvictionPolicy:當達到maxElementsInMemory限制時,Ehcache將會根據指定的策略去清理記憶體。預設策略是LRU(最近最少使用)。你可以設定為FIFO(先進先出)或是LFU(較少使用)。
     clearOnFlush:記憶體數量最大時是否清除。
     memoryStoreEvictionPolicy:可選策略有:LRU(最近最少使用,預設策略)、FIFO(先進先出)、LFU(最少訪問次數)。
     FIFO,first in first out,這個是大家最熟的,先進先出。
     LFU, Less Frequently Used,就是上面例子中使用的策略,直白一點就是講一直以來最少被使用的。如上面所講,快取的元素有一個hit屬性,hit值最小的將會被清出快取。
     LRU,Least Recently Used,最近最少使用的,快取的元素有一個時間戳,當快取容量滿了,而又需要騰出地方來快取新的元素的時候,那麼現有快取元素中時間戳離當前時間最遠的元素將被清出快取。
  -->
    <cache
            name="cloud_user"
            eternal="false"
            maxElementsInMemory="5000"
            overflowToDisk="false"
            diskPersistent="false"
            timeToIdleSeconds="1800"
            timeToLiveSeconds="1800"
            memoryStoreEvictionPolicy="LRU"/>

</ehcache>

Redis資料庫來做快取!