MyBatis 的在使用上的注意事項及其辨析

Rainbow-Sea發表於2024-06-13

1. MyBatis 的在使用上的注意事項及其辨析

@

目錄
  • 1. MyBatis 的在使用上的注意事項及其辨析
  • 2. 準備工作
  • 3. #{ } 與 ${ } 的區別和使用
  • {}
    • 3.1 什麼情況下必須使用 $
      • 3.1.1 拼接表名
      • 3.1.2 批次刪除
      • 3.1.3 模糊查詢
        • 3.1.3.1 使用 ${ }的方式
        • 3.1.3.2 使用 #{ } 的方式
  • 4. typeAliases 別名定義的使用
    • 4.1 typeAliases 的第一種方式:typeAlias
    • 4.2 typeAliases 的第二種方式:package
  • 5. mappers 路徑設定的使用
    • 5.1 mapper 標籤下的 resource 屬性的使用
    • 5.2 mapper 標籤下的 url 屬性的使用
    • 5.3 mapper 標籤下的 class 屬性的使用
    • 5.4 package 標籤的使用
  • 6. 在 IDEA 中自定義配置檔案模板
  • 7. 補充:插入資料時獲取自動生成的主鍵的值
  • 8. 總結:
  • { } 與 ${ } 的區別和使用
  • 9. 最後:


2. 準備工作

資料表結構的設計,資料表名為:t_car

在這裡插入圖片描述

t_car 表中的資料資訊:

在這裡插入圖片描述

pom.xml 檔案當中配置相關的依賴的 jar 包如下:

在這裡插入圖片描述

<?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.rainbowsea</groupId>
    <artifactId>mybatis-005-crud-blog</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>17</maven.compiler.source>
        <maven.compiler.target>17</maven.compiler.target>
    </properties>

    <dependencies>
        <!--        mybatis 的依賴-->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.5.10</version>
        </dependency>

        <!--        mysql -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.30</version>
        </dependency>

        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.13.2</version>
            <scope>test</scope>
        </dependency>

        <!--        引入 logback的依賴,這個日誌框架實現了slf4j 規範-->
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-classic</artifactId>
            <version>1.2.11</version>
        </dependency>
    </dependencies>

</project>

配置 logback 的配置檔案,用於列印顯示,我們的日誌資訊,方便我們檢視我們的執行過程,效果。

在這裡插入圖片描述

<?xml version="1.0" encoding="UTF-8"?>

<configuration debug="false">
    <!-- 控制檯輸出 -->
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <!--格式化輸出:%d表示日期,%thread表示執行緒名,%-5level:級別從左顯示5個字元寬度%msg:日誌訊息,%n是換行符-->
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
        </encoder>
    </appender>

    <!--mybatis log configure-->
    <logger name="com.apache.ibatis" level="TRACE"/>
    <logger name="java.sql.Connection" level="DEBUG"/>
    <logger name="java.sql.Statement" level="DEBUG"/>
    <logger name="java.sql.PreparedStatement" level="DEBUG"/>

    <!-- 日誌輸出級別,logback日誌級別包括五個:TRACE < DEBUG < INFO < WARN < ERROR -->
    <root level="DEBUG">
        <appender-ref ref="STDOUT"/>
        <appender-ref ref="FILE"/>
    </root>

</configuration>

配置 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>
    <!--    起別名-->
    <typeAliases>
        <!--  使用 <package>	還可以將這個包下的所有的類的全部自動起別名,別名就是簡名,不區分大小寫 -->
        <package name="com.rainbowsea.mybatis.pojo"/>
    </typeAliases>
    <environments default="mybatis">

        <environment id="mybatis">
            <!--            MANAGED 沒有用第三框架管理的話,都是會被提交的,沒有事務上的管理了 -->
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/mybatis"/>
                <property name="username" value="root"/>
                <property name="password" value="MySQL123"/>
            </dataSource>
        </environment>
    </environments>
    <mappers>
        <!-- 這裡也是可以使用 package 包名掃描,但是同樣的:對應介面路徑要一致,介面名一致-->
        <mapper resource="CarMapper.xml"></mapper>
    </mappers>
</configuration>

對照 t_car 建立的ORM 對映的 Car 類

注意:在MyBatis 當中對應的ORM ,一般在框架裡對應的 Bean實體類,一定要實現該 set 和 get 方法以及無引數構造方法,無法框架無法使用反射機制,進行操作

建議用包裝類,這樣可以防止 Null的問題,因為(簡單型別 int num = null ,是不可以賦值為 null)的編譯無法透過

在這裡插入圖片描述

package com.rainbowsea.mybatis.pojo;

public class Car {
    // 資料庫表當中的欄位應該和pojo類的屬性一一對應
    // 建議使用包裝類,這樣可以防止null的問題
    private Long id;
    private String carNum;
    private String brand;
    private Double guidePrice;
    private String produceTime;
    private String carType;

    public Car() {
    }

    public Car(Long id, String carNum, String brand, Double guidePrice, String produceTime, String carType) {
        this.id = id;
        this.carNum = carNum;
        this.brand = brand;
        this.guidePrice = guidePrice;
        this.produceTime = produceTime;
        this.carType = carType;
    }

    @Override
    public String toString() {
        return "Car{" +
                "id=" + id +
                ", carNum='" + carNum + '\'' +
                ", brand='" + brand + '\'' +
                ", guidePrice=" + guidePrice +
                ", produceTime='" + produceTime + '\'' +
                ", catType='" + carType + '\'' +
                '}';
    }

    public Long getId() {
        return id;
    }

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

    public String getCarNum() {
        return carNum;
    }

    public void setCarNum(String carNum) {
        this.carNum = carNum;
    }

    public String getBrand() {
        return brand;
    }

    public void setBrand(String brand) {
        this.brand = brand;
    }

    public Double getGuidePrice() {
        return guidePrice;
    }

    public void setGuidePrice(Double guidePrice) {
        this.guidePrice = guidePrice;
    }

    public String getProduceTime() {
        return produceTime;
    }

    public void setProduceTime(String produceTime) {
        this.produceTime = produceTime;
    }

    public String getcarType() {
        return carType;
    }

    public void setcarType(String catType) {
        this.carType = catType;
    }
}

對應操作實現CRUD(增刪改查)的介面(這裡是:CarMapper介面),在MyBtis 當中 ,關於 CRUD(增刪改查)操作的介面/實現類,都是 mapper 結尾的作為持久層,而在 MVC的三層架構中,則是以 dao 為字尾作為CRUD(增刪改查)操作的介面/實現類。

在這裡插入圖片描述

package com.rainbowsea.mybatis.mapper;

import com.rainbowsea.mybatis.pojo.Car;

import java.util.List;

public interface CarMapper {


    /**
     * 新增 Car
     * @param car
     * @return
     */
    int insert(Car car);


    /**
     * 根據id 刪除 Car
     * @param id
     * @return
     */
    int deleteById(Long id);


    /**
     * 修改汽車資訊
     * @param car
     * @return
     */
    int update(Car car);


    /**
     * 根據id查詢汽車資訊
     * @param id
     * @return
     */
    Car selectById(Long id);


    /**
     * 獲取所有的汽車資訊
     * @return
     */
    List<Car> selectAll();
}

獲取 Sqlsession 物件的工具類的編寫。

在這裡插入圖片描述

package com.rainbowsea.mybatis.utils;


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;

public class SqlSessionUtil {
    // 工具類的構造方法一般都是私有話化的
    // 工具類中所有的方法都是靜態的,直接類名即可呼叫,不需要 new 物件
    // 為了防止new物件,構造方法私有化。

    private SqlSessionUtil() {

    }



    private static SqlSessionFactory sessionFactory = null;

    // 靜態程式碼塊,類載入時執行
    // SqlSessionUtil 工具類在進行第一次載入的時候,解析mybatis-config.xml 檔案,建立SqlSessionFactory物件。
    static {
        // 獲取到  SqlSessionFactoryBuilder 物件
        SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();

        // 獲取到SqlSessionFactory 物件
        // SQlsessionFactory物件,一個SqlSessionFactory對應一個 environment, 一個environment通常是一個資料庫
        try {
            sessionFactory = sqlSessionFactoryBuilder.build(Resources.getResourceAsStream("mybatis-config.xml"),
                    "mybatis");
        } catch (IOException e) {
            throw new RuntimeException(e);
        }

    }

    /**
     * 獲取會話物件
     * @return SqlSession
     */
    public static SqlSession openSession() {
        // 獲取到 SqlSession 物件
        SqlSession sqlSession = sessionFactory.openSession();
        return sqlSession;
    }
}

3. #{ } 與 ${ } 的區別和使用

  • #{ } :先編譯 SQL 語句,再對佔位符傳值,底層是 PrepareedStatement 實現,可以防止 SQL 注入,比較常用
  • ${ }:先進行SQL語句的拼接,然後再對SQL語句進行編譯,底層是 Statement 實現,這種方式存在 SQL隱碼攻擊現象,SQL隱碼攻擊的風險,簡單的說就是,直接將傳入的值拼接為了SQL語句,然後再執行的)。只有在需要進行SQL語句關鍵字拼接的情況下才會用到。
  • 簡單的說一個區別就是:#{} 傳的值是帶有 '' 單引號的,而 ${} 傳的值是(直接就是值,沒有單引號,或者是雙引號,兩個都沒有)

檢驗一下。

這裡我們根據汽車的 car_type 查詢,為新能源汽車的。多條記錄

首先這裡我們先使用 #{} ,傳的是帶有 '' 單引號的值 ——> '新能源'

在這裡插入圖片描述

在這裡插入圖片描述

package com.rainbowsea.mybatis.mapper;

import com.rainbowsea.mybatis.pojo.Car;

import java.util.List;

/**
 * 封裝汽車相關資訊的pojo類,普通的Java類
 */
public interface CarMapper {

    /**
     * 根據汽車型別獲取汽車資訊
     * @param carType
     * @return
     */
    List<Car> selectByCarType(String carType);

}

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<!--namespace 一定要是:對應的介面的全限定類名-->
<mapper namespace="com.rainbowsea.mybatis.mapper.CarMapper">

    <!--	id 要是 namespace 對應介面上的方法名: -->
    <!--    這樣因為:資料表的欄位名和我們定義POJO類的屬性名不一致,要將二者保持一致,用 關鍵字 as 定義別名的方式保持一致-->
    <select id="selectByCarType" resultType="com.rainbowsea.mybatis.pojo.Car">
        select id,
               car_num      as carNum,
               brand,
               guide_price  as guidePrice,
               produce_time as produceTime,
               car_type     as carType
        from t_car
        where car_type = #{carType}
    </select>

</mapper>

執行Java程式:

在這裡插入圖片描述

在這裡插入圖片描述

查詢成功。

下面我們再實驗用 ${} ,直接就是新能源的值,沒有單引號,也沒有雙引號,就是直接就是——》新能源的值。執行結果如下:

在這裡插入圖片描述

比較上述#{}和 ?{} 的執行結果:

{}

在這裡插入圖片描述

${}

在這裡插入圖片描述

透過執行可以清楚的看到,sql 語句中是帶有 ? 的,這個 ? 就是大家在JDBC中所學的佔位符,專門用來接收值的。

把“新能源”以 String 型別的值,傳遞給 ? ,加上 '' 單引號,作為字串傳入進行,執行SQL語句

這就是 #{},它會先進行sql語句的預編譯,然後再給佔位符傳值。

而 ${} 是直接將我們的 新能源 作為值,傳入給SQL語句的,注意 ${} 的方式是不會加單引號/雙引號的,而是作為值,直接拼接到 SQL語句當中去了,但是,在我們這個查詢的SQL當中,新能源就是必須要為字串才行的,不然是無法執行SQL語句,是無法識別出來的。

在這裡插入圖片描述

3.1 什麼情況下必須使用 $

當需要進行 sql 語句關鍵字拼接的時候,簡單的說就是當我們要使用 SQL語句當中的關鍵字的時候,以及要傳的值,不要單引號/雙引號的值的時候,就必須使用${ }

需求:透過向sql語句中注入asc或desc關鍵字,來完成資料的升序或降序排列。

根據資料表中的 produce_time日期時間進行排序。

因為這裡我們使用到了 ASC / DESC 這兩者都是 SQL語句當中的關鍵字,所以我們需要使用 ${} 的方式。

在這裡插入圖片描述

package com.rainbowsea.mybatis.mapper;

import com.rainbowsea.mybatis.pojo.Car;

import java.util.List;

/**
 * 封裝汽車相關資訊的pojo類,普通的Java類
 */
public interface CarMapper {

    /**
     * 查詢所有的汽車資訊,然後透過 asc 升序,desc 降序
     * @param ascOrDesc
     * @return
     */
    List<Car> selectAllByAscOrDesc(String ascOrDesc);


}

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<!--namespace 一定要是:對應的介面的全限定類名-->
<mapper namespace="com.rainbowsea.mybatis.mapper.CarMapper">

    <!--	id 要是 namespace 對應介面上的方法名: -->
    <!--    這樣因為:資料表的欄位名和我們定義POJO類的屬性名不一致,要將二者保持一致,用 關鍵字 as 定義別名的方式保持一致-->
    <select id="selectAllByAscOrDesc" resultType="com.rainbowsea.mybatis.pojo.Car">
        select id,
               car_num      as carNum,
               brand,
               guide_price  as guidePrice,
               produce_time as produceTime,
               car_type     as carType
        from t_car
        order by produce_time ${ascOrDesc}
    </select>

</mapper>

Java程式執行測試:

在這裡插入圖片描述

public class TestCarMapper {

    @Test
    public void testSelectAllByAscOrDesc() {
        SqlSession sqlSession = SqlSessionUtil.openSession();
        CarMapper mapper = sqlSession.getMapper(CarMapper.class);
        List<Car> cars = mapper.selectAllByAscOrDesc("asc");
        cars.forEach(car -> {
            System.out.println(car);
        });

        sqlSession.close();
    }

}

如果換成 #{} ,將SQL 語句的關鍵字 asc 作為字串的形式拼接到 SQL語句當中是會編譯失敗的了。

在這裡插入圖片描述

3.1.1 拼接表名

業務背景:實際開發中,有的表資料量非常龐大,可能會採用分表方式進行儲存,比如每天生成一張表,表的名字與日期掛鉤,例如:2022年8月1日生成的表:t_user20220108。2000年1月1日生成的表:t_user20000101。此時前端在進行查詢的時候會提交一個具體的日期,比如前端提交的日期為:2000年1月1日,那麼後端就會根據這個日期動態拼接表名為:t_user20000101。有了這個表名之後,將表名拼接到sql語句當中,返回查詢結果。那麼大家思考一下,拼接表名到sql語句當中應該使用#{} 還是 ${} 呢?

使用#{}會是這樣:select * from 't_car'

使用${}會是這樣:select * from t_car

向SQL語句當中拼接表名,就需要使用${}
現實業務當中,可能存在分表儲存的資料的情況,因為一張表存的話,資料量太大了,查詢效率比較低。
可以將這些資料有規律的分表儲存,這樣在查詢的時候效率就比較高,因為掃描的資料量變小了
日誌表,專門儲存日誌資訊,如果t_long 只有一張表,這張表中每一天都會產生很多的log,慢慢的,這個表中資料會很多,
怎麼解決呢
可以每天生成一個新表,每張表以當天日期作為名稱,例如:
t_log_202209801
t_log_20220902
你想知道某一天的日誌資訊怎麼辦呢?
假設今天20220901,那麼直接查:t_log-20220901的表即可。

下面是我們的t_log_20220902 的資料表資訊

在這裡插入圖片描述

在這裡插入圖片描述

對應資料表的 POJO 類設計:

在這裡插入圖片描述

package com.rainbowsea.mybatis.pojo;


public class Log {
    private Integer id;
    private String log;
    private String time;

    public Log() {
    }


    public Log(Integer id, String log, String time) {
        this.id = id;
        this.log = log;
        this.time = time;
    }


    @Override
    public String toString() {
        return "Log{" +
                "id=" + id +
                ", log='" + log + '\'' +
                ", time='" + time + '\'' +
                '}';
    }


    public Integer getId() {
        return id;
    }

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

    public String getLog() {
        return log;
    }

    public void setLog(String log) {
        this.log = log;
    }

    public String getTime() {
        return time;
    }

    public void setTime(String time) {
        this.time = time;
    }
}

對應的LogMapper 的介面資訊編寫。

首先我們使用 ${} 的方式測試執行。

在這裡插入圖片描述

在這裡插入圖片描述

package com.rainbowsea.mybatis.mapper;


import com.rainbowsea.mybatis.pojo.Log;

import java.util.List;

public interface LogMapper {

    /**
     * 根據日期查詢不同的表,獲取表中所有的日誌
     *
     * @param date
     * @return
     */
    List<Log> selectAllByTable(String date);
}
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<!--namespace 一定要是:對應的介面的全限定類名-->
<mapper namespace="com.rainbowsea.mybatis.mapper.LogMapper">

    <!--	id 要是 namespace 對應介面上的方法名: -->
    <!--    <select id="selectAllByTable" resultType="com.rainbowsea.mybatis.pojo.Log">-->
    <select id="selectAllByTable" resultType="com.rainbowsea.mybatis.pojo.Log">
        select * from t_log_${date};
    </select>

</mapper>

在這裡插入圖片描述

package com.rainbowsea.test;

import com.rainbowsea.mybatis.mapper.LogMapper;
import com.rainbowsea.mybatis.pojo.Log;
import com.rainbowsea.mybatis.utils.SqlSessionUtil;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;

import java.util.List;

public class LogMapperTest {

    @Test
    public void testSelectAllByTable() {
        SqlSession sqlSession = SqlSessionUtil.openSession();
        LogMapper mapper = sqlSession.getMapper(LogMapper.class);
        List<Log> logs = mapper.selectAllByTable("20220902");
        logs.forEach(log -> {
            System.out.println(log);
        });
    }
}

使用 ${} 拼接上的結果為:select * from t_log_20220902; 直接就是 20220902 的值,沒有加單引號/雙引號。

使用 #{ } 的方式是會帶上 ‘’ 單引號的。

在這裡插入圖片描述

使用 #{} 拼接上的結果為:select * from t_log_‘20220902’; 加上的單引號後拼接上去,t_log_20220902是一個資料表名的,加了單引號後,MySQL 就找不到該資料表了,編譯無法透過了。

3.1.2 批次刪除

業務背景:一次刪除多條記錄。

在這裡插入圖片描述

批次刪除:一次刪除多條記錄
批次刪除的SQL語句有兩種寫法:

第一種:or: delete form t_car where id =1 or id = 2 or id = 3
第二種:delete from t_car where id in(1,2,3)

假設現在使用in的方式處理,前端傳過來的字串:1, 2, 3

如果使用mybatis處理,應該使用#{} 還是 ${}

使用#{} :delete from t_user where id in('1,2,3') 會加上單引號, 執行錯誤:1292 - Truncated incorrect DOUBLE value: '1,2,3'

使用${} :delete from t_user where id in(1, 2, 3),執行成功

所以我們要採用 ${} ,不加單引號的形式。

在這裡插入圖片描述

在這裡插入圖片描述

package com.rainbowsea.mybatis.mapper;

import com.rainbowsea.mybatis.pojo.Car;

import java.util.List;

/**
 * 封裝汽車相關資訊的pojo類,普通的Java類
 */
public interface CarMapper {

    /**
     * 批次刪除,根據id
     * @param ids
     * @return
     */
    int deleteBath(String ids);
}

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<!--namespace 一定要是:對應的介面的全限定類名-->
<mapper namespace="com.rainbowsea.mybatis.mapper.CarMapper">

    <!--	id 要是 namespace 對應介面上的方法名: -->
    <delete id="deleteBath">
        delete from t_car where id in(${ids})
    </delete>

</mapper>

Java程式測試,

注意因為我們這裡是對資料表進行的修改操作的,所以需要提交一下資料給資料庫。

這裡我們刪除 id為(121,122,123) 三條記錄。

在這裡插入圖片描述

在這裡插入圖片描述

package com.rainbowsea.test;

import com.rainbowsea.mybatis.mapper.CarMapper;
import com.rainbowsea.mybatis.pojo.Car;
import com.rainbowsea.mybatis.utils.SqlSessionUtil;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;

import java.util.List;

public class TestCarMapper {

    @Test
    public void testDeleteBath() {
        SqlSession sqlSession = SqlSessionUtil.openSession();
        CarMapper mapper = sqlSession.getMapper(CarMapper.class);
        int count = mapper.deleteBath("121,122,123");
        sqlSession.commit();
        sqlSession.close();

    }

}

如果使用 #{} 是將 使用#{} :delete from t_user where id in('121,122,123') 會加上單引號, 執行錯誤:1292 - Truncated incorrect DOUBLE value: '121,122,123'

在這裡插入圖片描述

3.1.3 模糊查詢

需求:查詢小米系列的汽車。【只要品牌brand中含有小米兩個字的都查詢出來。】

關於模糊查詢有四種方案:

  1. 方案一:'%${brand}%'
  2. 方案二: concat('%','${brand}','%')
  3. 方案三: count函式,這個是mysql資料庫當中的一個函式,專門進行字串拼接的 concat('%',#{brand},'%')
  4. 方案四: "%"#{brand}"%", 這種方式比較常用,也避免了SQL隱碼攻擊的問題。
方案一:'%${brand}%'
方案二: concat('%','${brand}','%')
方案三: count函式,這個是mysql資料庫當中的一個函式,專門進行字串拼接的
        concat('%',#{brand},'%')
方案四: "%"#{brand}"%"

四種方式:總的來說,都是為了拼接成字串,把不是字串的想辦法拼接成字串

3.1.3.1 使用 ${ }的方式

方案一:'%${brand}%'

在這裡插入圖片描述

在這裡插入圖片描述

package com.rainbowsea.mybatis.mapper;

import com.rainbowsea.mybatis.pojo.Car;

import java.util.List;

/**
 * 封裝汽車相關資訊的pojo類,普通的Java類
 */
public interface CarMapper {




    /**
     * 根據汽車品牌進行模糊查詢
     * @param brand
     * @return
     */
    List<Car> selectByBrandLike(String brand);



}

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<!--namespace 一定要是:對應的介面的全限定類名-->
<mapper namespace="com.rainbowsea.mybatis.mapper.CarMapper">
    <!--	id 要是 namespace 對應介面上的方法名: -->
    <!--    這樣因為:資料表的欄位名和我們定義POJO類的屬性名不一致,要將二者保持一致,用 關鍵字 as 定義別名的方式保持一致-->
    <select id="selectByBrandLike" resultType="com.rainbowsea.mybatis.pojo.Car">
        select
            id,
            car_num as carNum,
            brand,
            guide_price as guidePrice,
            produce_time as produceTime,
            car_type as carType
        from t_car
        where brand like '%${brand}%'
    </select>

</mapper>

Java程式執行結果:

在這裡插入圖片描述

方案二: concat('%','${brand}','%')

在這裡插入圖片描述

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<!--namespace 一定要是:對應的介面的全限定類名-->
<mapper namespace="com.rainbowsea.mybatis.mapper.CarMapper">
    <!--	id 要是 namespace 對應介面上的方法名: -->
    <!--    這樣因為:資料表的欄位名和我們定義POJO類的屬性名不一致,要將二者保持一致,用 關鍵字 as 定義別名的方式保持一致-->
    <select id="selectByBrandLike" resultType="com.rainbowsea.mybatis.pojo.Car">
        select
            id,
            car_num as carNum,
            brand,
            guide_price as guidePrice,
            produce_time as produceTime,
            car_type as carType
        from t_car
        where brand like concat('%','${brand}','%')
    </select>
</mapper>

在這裡插入圖片描述

3.1.3.2 使用 #{ } 的方式

方案三: count函式,這個是mysql資料庫當中的一個函式,專門進行字串拼接的 concat('%',#{brand},'%')

在這裡插入圖片描述

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<!--namespace 一定要是:對應的介面的全限定類名-->
<mapper namespace="com.rainbowsea.mybatis.mapper.CarMapper">
    <!--	id 要是 namespace 對應介面上的方法名: -->
    <!--    這樣因為:資料表的欄位名和我們定義POJO類的屬性名不一致,要將二者保持一致,用 關鍵字 as 定義別名的方式保持一致-->
    <select id="selectByBrandLike" resultType="com.rainbowsea.mybatis.pojo.Car">
        select
            id,
            car_num as carNum,
            brand,
            guide_price as guidePrice,
            produce_time as produceTime,
            car_type as carType
        from t_car
        where brand like concat('%',#{brand},'%')
    </select>
</mapper>

在這裡插入圖片描述

方案四: "%"#{brand}"%", 這種方式比較常用,也避免了SQL隱碼攻擊的問題。

在這裡插入圖片描述

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<!--namespace 一定要是:對應的介面的全限定類名-->
<mapper namespace="com.rainbowsea.mybatis.mapper.CarMapper">
    <!--	id 要是 namespace 對應介面上的方法名: -->
    <!--    這樣因為:資料表的欄位名和我們定義POJO類的屬性名不一致,要將二者保持一致,用 關鍵字 as 定義別名的方式保持一致-->
    <select id="selectByBrandLike" resultType="com.rainbowsea.mybatis.pojo.Car">
        select
            id,
            car_num as carNum,
            brand,
            guide_price as guidePrice,
            produce_time as produceTime,
            car_type as carType
        from t_car
        where brand like "%"#{brand}"%"
    </select>
</mapper>

測試執行:

在這裡插入圖片描述

4. typeAliases 別名定義的使用

我們來觀察一下CarMapper.xml中的配置資訊:

在這裡插入圖片描述

resultType 屬性用來指定查詢結果集的封裝型別,這個名字太長,可以起別名嗎?可以。

mybatis-config.xml 檔案中使用 typeAliases 標籤來起別名,包括兩種方式:

4.1 typeAliases 的第一種方式:typeAlias

在這裡插入圖片描述

    <!--    起別名-->
    <typeAliases>
        <typeAlias type="com.rainbowsea.mybatis.pojo.Car" alias="Car"></typeAlias>
        <typeAlias type="com.rainbowsea.mybatis.pojo.Log" alias="Log"></typeAlias>
    </typeAliases>
<?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>
    <!--    起別名-->
    <typeAliases>
        <typeAlias type="com.rainbowsea.mybatis.pojo.Car" alias="Car"></typeAlias>
        <typeAlias type="com.rainbowsea.mybatis.pojo.Log" alias="Log"></typeAlias>
    </typeAliases>

    <environments default="mybatis">

        <environment id="mybatis">
            <!--            MANAGED 沒有用第三框架管理的話,都是會被提交的,沒有事務上的管理了 -->
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/mybatis"/>
                <property name="username" value="root"/>
                <property name="password" value="MySQL123"/>
            </dataSource>
        </environment>
    </environments>
    <mappers>
        <!-- 這裡也是可以使用 package 包名掃描,但是同樣的:對應介面路徑要一致,介面名一致-->
        <mapper resource="CarMapper.xml"></mapper>
        <mapper resource="LogMapper.xml"></mapper>
    </mappers>
</configuration>
  • 首先要注意typeAliases標籤的放置位置,如果不清楚的話,可以看看錯誤提示資訊。

  • typeAliases標籤中的typeAlias可以寫多個。

  • typeAlias:

    • type屬性:指定給哪個類起別名
    • alias屬性:別名。
      • alias屬性不是必須的,如果預設的話,type屬性指定的型別名的簡類名作為別名。
      • alias是大小寫不敏感的。也就是說假設alias="Car",再用的時候,可以CAR,也可以car,也可以Car,都行、

注意:<namespace = “”>介面,一定要為全限定類名(帶有包名),不可以用別名機制

測試:

在這裡插入圖片描述

在這裡插入圖片描述

特別的: 省略 alias 屬性之後,別名就是類的簡名了,比如 :com.rainbowsea.mybatis.pojo.Car 的別名就是 Car/CAR/cAR,不區分大小寫的。

在這裡插入圖片描述

    <!--    起別名-->
    <!--            省略alias 屬性之後,別名就是類的簡名了,比如:com.rainbowsea.mybatis.pojo.Car 的別名就是 Car/CAR/cAR
     不區分大小寫的。-->
    <typeAliases>
        <typeAlias type="com.rainbowsea.mybatis.pojo.Car" ></typeAlias>
        <typeAlias type="com.rainbowsea.mybatis.pojo.Log"></typeAlias>
    </typeAliases>

4.2 typeAliases 的第二種方式:package

如果一個包下的類太多,每個類都要起別名,會導致 typeAlias 標籤配置較多,所以mybatis用提供package的配置方式,只需要指定包名,該包下的所有類都自動起別名,別名就是簡類名。並且別名不區分大小寫。

注意:<namespace = “”>介面,一定要為全限定類名(帶有包名),不可以用別名機制

這種方式是最常用的。

在這裡插入圖片描述

<!--    別名 -->
    <typeAliases>
        <!--使用 <package>	還可以將這個包下的所有的類的全部自動起別名,別名就是簡名,不區分大小寫-->
        <package name="com.rainbowsea.mybatis.pojo"/>
    </typeAliases>

測試:

在這裡插入圖片描述

注意:

使用 <package> 還可以將這個包下的所有的類的全部自動起別名,別名就是簡名,不區分大小寫

,所有的別名不區分大小寫。但是:namespace 不能使用別名機制

同時需要按照一定的順序放置,放到指定的順序當中去

5. mappers 路徑設定的使用

SQL對映檔案的配置方式包括四種:

  • resource:從類路徑中載入
  • url:從指定的全限定資源路徑中載入
  • class:使用對映器介面實現類的完全限定類名
  • package:將包內的對映器介面實現全部註冊為對映器

5.1 mapper 標籤下的 resource 屬性的使用

這種方式是從類路徑中載入配置檔案,所以這種方式要求SQL對映檔案必須放在resources目錄下或其子目錄下。

<mappers>
  <mapper resource="org/mybatis/builder/AuthorMapper.xml"/>
  <mapper resource="org/mybatis/builder/BlogMapper.xml"/>
  <mapper resource="org/mybatis/builder/PostMapper.xml"/>
</mappers>

在這裡插入圖片描述

5.2 mapper 標籤下的 url 屬性的使用

這種方式是一種絕對路徑的方式,這種方式不要求配置檔案必須放到類的路徑當中,哪裡都行,只要提供了一個絕對路徑就行,這種方式使用極少,因為移植性太差了(並不是所以的系統都有磁碟機代號的說法的)。

在這裡插入圖片描述

**需要注意的是:要三個\\\ ,才表示兩個 \\ **

<mappers>
  <mapper url="file:///var/mappers/AuthorMapper.xml"/>
  <mapper url="file:///var/mappers/BlogMapper.xml"/>
  <mapper url="file:///var/mappers/PostMapper.xml"/>
</mappers>

5.3 mapper 標籤下的 class 屬性的使用

Class: 這位置提供的是 mapper 介面的全限定介面名,必須帶有包名(就是要一定要和對應介面的路徑是一致的,一致的,一致的)

如果使用這種方式必須滿足以下條件:

  • SQL對映檔案和mapper介面放在同一個目錄下。
  • SQL對映檔案的名字也必須和mapper介面名一致。
        <mapper class="com.rainbowsea.mybatis.mapper.CarMapper"><mapper>
        如果你class指定是:com.rainbowsea.mybatis.mapper.CarMapper
        那麼mybatis框架會自動去com/rainbowsea/mybatis/mapper/CarMapper/的目錄下找,注意是 / 
注意:也就是說,如果你採用這種方式,那麼你必須保證:CarMapper.xml檔案和CarMapper介面必須在同一個目錄下,並且名字也是一致的

CarMapper介面——》CarMapper.xml
LogMapper介面——> LogMapper.xml

提醒:
在IDEA的resources 目錄下新建多重目錄的話,必須是這樣建立:
com/rianbowsea/mybatis/mapper/
不然是
com.rianbowsea.mybatis.mapper 這是建包了

在這裡插入圖片描述

在這裡插入圖片描述

保持一致的同時,名稱也要是一致的

在這裡插入圖片描述

    <mappers>
        <!-- 這裡也是可以使用 package 包名掃描,但是同樣的:對應介面路徑要一致,介面名一致-->
       <mapper class="com.rainbowsea.mybatis.mapper.CarMapper"></mapper>
       <mapper class="com.rainbowsea.mybatis.mapper.LogMapper"></mapper>
    </mappers>

測試執行:

執行程式:正常!!!

在這裡插入圖片描述

5.4 package 標籤的使用

如果class較多,可以使用這種package的方式,但前提條件和上一種(mapper 標籤下的class 屬性的使用)方式一樣。

如果使用這種方式必須滿足以下條件:

  • SQL對映檔案和mapper介面放在同一個目錄下。
  • SQL對映檔案的名字也必須和mapper介面名一致。

這種方式是最常用的。

    <!-- 這裡也是可以使用 package 包名掃描,但是同樣的:對應介面路徑要一致,介面名一致-->
    <package name="com.rainbowsea.mybatis.mapper"></package>

在這裡插入圖片描述

執行測試:

在這裡插入圖片描述

6. 在 IDEA 中自定義配置檔案模板

mybatis-config.xml 和 SqlMapper.xml ,logback 檔案可以在IDEA中提前建立好模板,以後透過模板建立配置檔案。

在這裡插入圖片描述

在這裡插入圖片描述

mybatis 核心配置檔案的模板內容

<?xml version="1.0" encoding="UTF-8"?>

<configuration debug="false">
    <!-- 控制檯輸出 -->
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <!--格式化輸出:%d表示日期,%thread表示執行緒名,%-5level:級別從左顯示5個字元寬度%msg:日誌訊息,%n是換行符-->
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
        </encoder>
    </appender>

    <!--mybatis log configure-->
    <logger name="com.apache.ibatis" level="TRACE"/>
    <logger name="java.sql.Connection" level="DEBUG"/>
    <logger name="java.sql.Statement" level="DEBUG"/>
    <logger name="java.sql.PreparedStatement" level="DEBUG"/>

    <!-- 日誌輸出級別,logback日誌級別包括五個:TRACE < DEBUG < INFO < WARN < ERROR -->
    <root level="DEBUG">
        <appender-ref ref="STDOUT"/>
        <appender-ref ref="FILE"/>
    </root>

</configuration>

logback 的日誌配置模板內容

<?xml version="1.0" encoding="UTF-8"?>

<configuration debug="false">
    <!-- 控制檯輸出 -->
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <!--格式化輸出:%d表示日期,%thread表示執行緒名,%-5level:級別從左顯示5個字元寬度%msg:日誌訊息,%n是換行符-->
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
        </encoder>
    </appender>

    <!--mybatis log configure-->
    <logger name="com.apache.ibatis" level="TRACE"/>
    <logger name="java.sql.Connection" level="DEBUG"/>
    <logger name="java.sql.Statement" level="DEBUG"/>
    <logger name="java.sql.PreparedStatement" level="DEBUG"/>

    <!-- 日誌輸出級別,logback日誌級別包括五個:TRACE < DEBUG < INFO < WARN < ERROR -->
    <root level="DEBUG">
        <appender-ref ref="STDOUT"/>
        <appender-ref ref="FILE"/>
    </root>

</configuration>

SqLMapper的配置模板內容:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
		PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
		"http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<!--namespace 一定要是:對應的介面的全限定類名-->
<mapper namespace="">

<!--	id 要是 namespace 對應介面上的方法名: -->
	
</mapper>

7. 補充:插入資料時獲取自動生成的主鍵的值

**前提是:主鍵是自動生成的。 **

業務背景:一個使用者有多個角色。

在這裡插入圖片描述

插入一條新的記錄之後,自動生成了主鍵,而這個主鍵需要在其他表中使用時。

插入一個使用者資料的同時需要給該使用者分配角色:需要將生成的使用者的id插入到角色表的user_id欄位上。

第一種方式:可以先插入使用者資料,再寫一條查詢語句獲取id,然後再插入user_id欄位。【比較麻煩】

第二種方式:mybatis提供了一種方式更加便捷。

<!--
	userGeneratedKeys = "true" 使用自動生成的主鍵值
	keyProperty="id" 指定主鍵值賦值給物件的哪個屬性,這個就表示將主鍵值給Car物件的 id屬性。
	注意:這個 keyProperty 指定的值,一定要和對應上的 pojo 物件類上的屬性一致,不然,不行的
	-->
	<insert id="insertCarUserGeneratedKey" useGeneratedKeys="true" keyProperty="id" >
		insert into t_car values(null,#{carNum},#{brand},#{guidePrice},#{produceTime},#{carType})
	</insert>

userGeneratedKeys = "true" 使用自動生成的主鍵值,false 則是不使用自動生成的主鍵值了
keyProperty="id" 指定主鍵值賦值給POJO類(ORM對映)物件的哪個屬性,這個就表示將主鍵值給Car物件的 id屬性。
注意:這個 keyProperty 指定的值,一定要和對應上的 pojo 物件類上的屬性一致,不然,不行的。

在這裡插入圖片描述

在這裡插入圖片描述

package com.rainbowsea.mybatis.mapper;

import com.rainbowsea.mybatis.pojo.Car;

import java.util.List;

/**
 * 封裝汽車相關資訊的pojo類,普通的Java類
 */
public interface CarMapper {

    /**
     * 插入 Car 資訊,並且使用生成的主鍵值
     * @param car
     * @return
     */
    int insertCarUserGeneratedKey(Car car);

}

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<!--namespace 一定要是:對應的介面的全限定類名-->
<mapper namespace="com.rainbowsea.mybatis.mapper.CarMapper">
    <!--
	userGeneratedKeys = "true" 使用自動生成的主鍵值
	keyProperty="id" 指定主鍵值賦值給物件的哪個屬性,這個就表示將主鍵值給Car物件的 id屬性。
	注意:這個 keyProperty 指定的值,一定要和對應上的 pojo 物件類上的屬性一致,不然,不行的
	-->
    <insert id="insertCarUserGeneratedKey" useGeneratedKeys="true" keyProperty="id">
        insert into t_car
        values (null, #{carNum}, #{brand}, #{guidePrice}, #{produceTime}, #{carType})
    </insert>
</mapper>

執行測試:

在這裡插入圖片描述

在這裡插入圖片描述

8. 總結:

  1. { } 與 ${ } 的區別和使用

- #{ } :先編譯 SQL 語句,再對佔位符傳值,底層是 PrepareedStatement 實現,可以防止 SQL 注入,比較常用
- ${ }:先進行SQL語句的拼接,然後再對SQL語句進行編譯,底層是 Statement 實現,這種方式存在 SQL隱碼攻擊現象,SQL隱碼攻擊的風險,簡單的說就是,直接將傳入的值拼接為了SQL語句,然後再執行的)。只有在需要進行SQL語句關鍵字拼接的情況下才會用到。
- 簡單的說一個區別就是:#{} 傳的值是帶有 '' 單引號的,而 ${} 傳的值是(直接就是值,沒有單引號,或者是雙引號,兩個都沒有)

  1. 什麼情況下必須使用 $
拼接表名
批次刪除
模糊查詢
  1. typeAliases 別名定義的使用,注意:<namespace = “”>介面,一定要為全限定類名(帶有包名),不可以用別名機制 ,建議採用第二種 package 方式,,同時注意:typeAliases 的正確順序和位置,可以參考報錯資訊,進行修正。
<!--    別名 -->
    <typeAliases>
        <!--使用 <package>	還可以將這個包下的所有的類的全部自動起別名,別名就是簡名,不區分大小寫-->
        <package name="com.rainbowsea.mybatis.pojo"/>
    </typeAliases>
  1. mappers 路徑設定的使用 ,建議採用 package 標籤的方式,注意條件,兩個條件的一致性。
- SQL對映檔案和mapper介面放在同一個目錄下。
- SQL對映檔案的名字也必須和mapper介面名一致。
  1. 在 IDEA 中自定義配置檔案模板
  2. 插入資料時獲取自動生成的主鍵的值,**前提是:在對應資料表中的主鍵是自動生成的(自增的方式才行)。 **
<!--
	userGeneratedKeys = "true" 使用自動生成的主鍵值
	keyProperty="id" 指定主鍵值賦值給物件的哪個屬性,這個就表示將主鍵值給Car物件的 id屬性。
	注意:這個 keyProperty 指定的值,一定要和對應上的 pojo 物件類上的屬性一致,不然,不行的
	-->
	<insert id="insertCarUserGeneratedKey" useGeneratedKeys="true" keyProperty="id" >
		insert into t_car values(null,#{carNum},#{brand},#{guidePrice},#{produceTime},#{carType})
	</insert>

9. 最後:

“在這個最後的篇章中,我要表達我對每一位讀者的感激之情。你們的關注和回覆是我創作的動力源泉,我從你們身上吸取了無盡的靈感與勇氣。我會將你們的鼓勵留在心底,繼續在其他的領域奮鬥。感謝你們,我們總會在某個時刻再次相遇。”

在這裡插入圖片描述

相關文章