Java Mybatis狂神說的詳細筆記(完整版)


Mybatis官方文檔:

Mybatis官方文檔

官網文檔: https://mybatis.org/mybatis-3/zh/getting-started.html

MyBatis

1、簡介

1.1 什么是Mybatis

在這里插入圖片描述

  • MyBatis 是一款優秀的持久層框架;
  • 它支持自定義 SQL、存儲過程以及高級映射。MyBatis 免除了幾乎所有的 JDBC 代碼以及設置參數和獲取結果集的工作。MyBatis 可以通過簡單的 XML 或注解來配置和映射原始類型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 對象)為數據庫中的記錄。

1.2 持久化

數據持久化

  • 持久化就是將程序的數據在持久狀態和瞬時狀態轉化的過程
  • 內存:斷電即失
  • 數據庫(Jdbc),io文件持久化。

為什么要持久化?

有一些對象,不能讓他丟掉
內存太貴

1.3 持久層

Dao層、Service層、Controller層

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

1.4 為什么需要MyBatis

  • 幫助程序員將數據存入到數據庫中

  • 方便

  • 傳統的JDBC代碼太復雜了,簡化,框架,自動化

  • 不用MyBatis也可以,技術沒有高低之分

  • 優點:

    • 簡單易學
    • 靈活
    • sql和代碼的分離,提高了可維護性。
    • 提供映射標簽,支持對象與數據庫的orm字段關系映射
    • 提供對象關系映射標簽,支持對象關系組建維護
    • 提供xml標簽,支持編寫動態sql

2、第一個Mybatis程序

思路:搭建環境 --> 導入MyBatis --> 編寫代碼 --> 測試

2.1 搭建環境

創建數據庫

CREATE DATABASE `mybatis`;
USE `mybatis`;
CREATE TABLE `user`(
    `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主鍵ID',
    `name` varchar(15) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT '用戶名稱',
    `pwd` varchar(15) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT '用戶密碼',
    PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=16 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;

INSERT INTO `user`(`id`,`name`,`pwd`) VALUES
(1,"狂神","123456"),
(2,"通達","123456"),
(3,"藍凌","123456");

 

新建項目 

1.創建一個普通的maven項目

2.刪除src目錄 (就可以把此工程當做父工程了,然后創建子工程)

3.導入maven依賴

<!--導入依賴-->
<dependencies>
    <!--mysqlq驅動-->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>8.0.12</version>
    </dependency>
    <!--mybatis-->
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis</artifactId>
        <version>3.5.4</version>
    </dependency>
    <!--junit-->
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
        <scope>test</scope>
    </dependency>
</dependencies>

4.創建一個Module

2.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核心配置文件-->
    <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?userSSL=true&amp;useUnicode=true&amp;characterEncoding=UTF-8&amp;serverTimezone=UTC"/>
                    <property name="username" value="root"/>
                    <property name="password" value="root"/>
                </dataSource>
            </environment>
        </environments>
    </configuration>

    詳解:
    <?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核心配置文件-->
    <configuration>
    <!--environments多環境配置 default默認=id-->
    <environments default="development">
    <!--environment環境 id-->
    <environment id="development">
    <!--事務管理 JDBC-->
    <transactionManager type="JDBC"/>
    <dataSource type="POOLED">
    <!--<property name="driver" value="${driver}"/>-->
    <property name="driver" value="com.mysql.jdbc.Driver"/>
    <!--<property name="url" value="${url}"/>-->
    <property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=true&amp;useUnicode=true&amp;charsetEncoding=UTF-8"/>
    <!--<property name="username" value="${username}"/>-->
    <property name="username" value="root"/>
    <!--<property name="password" value="${password}"/>-->
    <property name="password" value="123456"/>
    </dataSource>
    </environment>
    </environments>
    <!--<mappers>-->
    <!-- <mapper resource="com/tongda/dao/UserMapper.xml"/>-->
    <!--</mappers>-->
    </configuration>

  此處設useSSL=false。

  • 編寫mybatis工具類  
//SqlSessionFactory -->SqlSession
public class MybatisUtils {

    private static SqlSessionFactory sqlSessionFactory;
    static {

        try {
            //使用Mybaties第一步:獲取sqlSessionFactory對象
            String resource = "mybatis-config.xml";
            InputStream inputStream = Resources.getResourceAsStream(resource);
            sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    //既然有了 SqlSessionFactory,顧名思義,我們可以從中獲得 SqlSession 的實例。
    // SqlSession 提供了在數據庫執行 SQL 命令所需的所有方法。你可以通過 SqlSession 實例來直接執行已映射的 SQL 語句。
    public static SqlSession getSqlSession(){
//        SqlSession sqlSession =  sqlSessionFactory.openSession();
//        return sqlSession;

        return sqlSessionFactory.openSession();
    }

}

2.3 編寫代碼

  • 實體類
    //實體類
    public class User {
        private int id;
        private String name;
        private String pwd;
    
        public User() {
        }
    
        public User(int id, String name, String pwd) {
            this.id = id;
            this.name = name;
            this.pwd = pwd;
        }
    
        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 getPwd() {
            return pwd;
        }
    
        public void setPwd(String pwd) {
            this.pwd = pwd;
        }
    
        @Override
        public String toString() {
            return "User{" +
                    "id=" + id +
                    ", name='" + name + '\'' +
                    ", pwd='" + pwd + '\'' +
                    '}';
        }
    }
  • Dao接口

  

public interface UserDao {
    List<User> getUserList();
}

 

  • 接口實現類由原來的UserDaoImpl轉變成一個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">
<!--namespace=綁定一個對應的Dao/Mapper接口-->
<!--命名空間-->
<!--重點namespace\id\resultType都要對應,不能亂寫-->
<mapper namespace="com.tongda.dao.UserDao">

<!--sql:select查詢語句-->
<!--返回結果集:resulttype一個,resultmap多個-->
<!--返回結果集User是UserDao泛型<User>-->
<select id="getUserList" resultType="com.kuang.pojo.User">
select * from mybatis.user
</select>
</mapper>

junit測試.

注意點:會報錯

org.apache.ibatis.binding.BindingException: Type interface com.kuang.dao.UserDao is not known to the MapperRegistry.

MapperRegistry是什么?

核心配置文件中注冊mappers

    @Test
    public void test(){

        //1.獲取SqlSession對象
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        //2.執行SQL
        // 方式一:getMapper
        UserDao userDao = sqlSession.getMapper(UserDao.class);
        List<User> userList = userDao.getUserList();
        for (User user : userList) {
            System.out.println(user);
        }

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

mybatis-config.xml中

<!--每一個Mapper.xml都需要在Mybatis核心配置文件中注冊-->
    <mappers>
        <mapper resource="org/mybatis/example/BlogMapper.xml"/>
    </mappers>

可能會遇到的問題:

  • 配置文件沒有注冊
  • 綁定接口錯誤
  • 方法名不對
  • 返回類型不對
  • Maven導出資源問題

 

3、CURD

 1. namespace
namespace中的包名要和Dao/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">
<!--namespace=綁定一個對應的Dao/Mapper接口-->
<!--命名空間-->
<!--重點namespace\id\resultType都要對應,不能亂寫-->
<mapper namespace="com.tongda.dao.UserMapper">

    <!--sql:select查詢語句-->
    <!--返回結果集:resulttype一個,resultmap多個-->
    <!--返回結果集User是UserDao泛型<User>-->
    <select id="getUserList" resultType="com.tongda.pojo.User">
        select * from mybatis.user
    </select>
</mapper>

 2. select

選擇,查詢語句;

id:就是對應的namespace中的方法名;

resultType : Sql語句執行的返回值;

parameterType : 參數類型;

 

  1.編寫接口:UserMapper.java

public interface UserMapper {
    //查詢所有用戶
    public List<User> getUserList();
    //插入用戶
    public void addUser(User user);
}

  2.編寫對應的mapper中的sql語句:UserMapper.xml

<insert id="addUser" parameterType="com.kuang.pojo.User">
    insert into user (id,name,password) values (#{id}, #{name}, #{password})
</insert>

  3.測試:UserMapperTest.java

@Test
public void test2() {
    SqlSession sqlSession = MybatisUtils.getSqlSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    User user  = new User(3,"黑子","666");
    mapper.addUser(user);
    //增刪改一定要提交事務
    sqlSession.commit();

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

注意:增刪改查一定要提交事務:

sqlSession.commit();

增刪改查全部流程實現

3. Insert

4. update

5. Delete

UserMapper.java

//Dao以后等價mapper
public interface UserMapper {
    //返回所有用戶
    List<User> getUserList();

    //根據Id查詢用戶,User為返回值用戶
    User getUserById(int id);

    //insert一個用戶,參數是對象User
    int addUser(User user);

    //修改用戶
    int updateUser(User user);

    //刪除一個用戶
    int deleteUser(int id);

}

UserMapper.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--namespace=綁定一個對應的Dao/Mapper接口-->
<!--命名空間-->
<!--重點namespace\id\resultType都要對應,不能亂寫-->
<mapper namespace="com.tongda.dao.UserMapper">

    <!--sql:select查詢語句-->
    <!--返回結果集:resulttype一個,resultmap多個-->
    <!--返回結果集User是UserDao泛型<User>-->
    <select id="getUserList" resultType="com.tongda.pojo.User">
        select * from mybatis.user
    </select>

    <!--對應id查詢用戶,返回一個resultType結果集類型,參數類型int-->
    <select id="getUserById" resultType="com.tongda.pojo.User" parameterType="int">
        select * from user where id = #{id};
    </select>

    <!--insert一個用戶-->
    <insert id="addUser" parameterType="com.tongda.pojo.User">
        insert into mybatis.user(id,name,pwd) values(#{id},#{name},#{pwd});
    </insert>

    <!--updata修改用戶,根據id-->
    <update id="updateUser" parameterType="com.tongda.pojo.User">
        update mybatis.user set name=#{name},pwd=#{pwd} where id =#{id};
    </update>

    <delete id="deleteUser" parameterType="int">
        delete from mybatis.user where id =#{id};
    </delete>
</mapper>

UserDaoTest.java

package com.tongda.dao;

import com.tongda.pojo.User;
import com.tongda.utils.MybatisUtils;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;

import java.util.List;

/**
 * @Author JunLong
 * @Date 2022/4/5 6:59
 * @Version 1.0
 */
/*注意點:

        org.apache.ibatis.binding.BindingException: Type interface com.kuang.dao.UserDao is not known to the MapperRegistry.

        MapperRegistry是什么?

        核心配置文件中注冊mappers
        */
public class UserDaoTest {
    @Test
    public void test(){
        //第一步:獲取SqlSession對象,工具類MybatisUtils中
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        //執行Sql:方式一getMapper
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        List<User> userList = mapper.getUserList();

        // 方法一:
        for (User user : userList) {
            System.out.println(user);
        }

        //不推薦:方法二
        // List<User> userList = sqlSession.selectList("com.tongda.UserDao.getUserList");
        // for (Object user : userList) {
        //     System.out.println(user);
        //
        // }

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

    @Test
    public  void getUserById(){
        //第一步固定步驟寫法
        SqlSession sqlSession = MybatisUtils.getSqlSession();

        //獲得UserMapper接口
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        //getUserbyid(用戶id號)
        User user1 = mapper.getUserById(1);
        User user2 = mapper.getUserById(2);
        User user3 = mapper.getUserById(2);
        System.out.println(user1);
        System.out.println(user2);
        System.out.println(user3);

        sqlSession.close();
    }

    //增刪改必須提交事務
    @Test
    public void addUser(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();

        UserMapper mapper = sqlSession.getMapper(UserMapper.class);

        int res = mapper.addUser(new User(4, "哼哈", "123456"));
        if (res>0){
            System.out.println("插入成功!");
        }

        // 提交事務,增刪改必須提交事務
        sqlSession.commit();
        //關閉
        sqlSession.close();;
    }

    @Test
    public void updataUser(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        mapper.updateUser(new User(4,"呵呵","123123"));

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

    @Test
    public void deleteUser(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        mapper.deleteUser(4);

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

 

6.分析錯誤

  1.標簽不要匹配錯 com.tongda.dao.UserMapper;

  2.resource綁定mapper,需要使用路徑! com/tongda/dao/UserMapper  注意分隔符;

  3.程序配置文件必須符合規范;

  4.NullPointerException,沒有注冊到資源Utils中sqlSessionFactory作用域問題;

  5.亂碼問題<property name="url" value="jdbc:mysql://localhost:3306/自己的庫?useSSL=false&amp;useUnicode=true&amp;characterEncoding=UTF-8"/>,或者idea-setting-file encoding-utf-8

  6.maven資源沒有導出問題,pom.xml中在build中配置resources,來防止我們資源導出失敗的問題

 

7.萬能的Map

假設,我們的實體類,或者數據庫中的表,字段或者參數過多,我們應該考慮使用Map!

UserMapper接口
//用萬能Map插入用戶

public void addUser2(Map<String,Object> map);

UserMapper.xml

<!--對象中的屬性可以直接取出來 傳遞map的key-->
<insert id="addUser2" parameterType="map">
insert into user (id,name,password) values (#{userid},#{username},#{userpassword})
</insert>

測試UserDaoTest

@Test
public void test3(){
    SqlSession sqlSession = MybatisUtils.getSqlSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    HashMap<String, Object> map = new HashMap<String, Object>();
    map.put("userid",4);
    map.put("username","王虎");
    map.put("userpassword",789);
    mapper.addUser2(map);
    //提交事務
    sqlSession.commit();
    //關閉資源
    sqlSession.close();
}

Map傳遞參數,直接在sql中取出key即可! 【parameter=“map”】

對象傳遞參數,直接在sql中取出對象的屬性即可! 【parameter=“Object”】

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

多個參數用Map , 或者注解!

 

7.模糊查詢

模糊查詢怎么寫?

Java代碼執行的時候,傳遞通配符% %

    <select id="getUserLike1" parameterType="String" resultType="com.bupt.pojo.User">
        select  * from user where username like #{name}
    </select>

 

經常碰到這樣的面試題目:#{}和${}的區別是什么?
 
網上的答案是:#{}是預編譯處理,$ {}是字符串替換。

mybatis在處理#{}時,會將sql中的#{}替換為?號,調用PreparedStatement的set方法來賦值;

mybatis在處理 <span id="MathJax-Span-2" class="mrow"><span id="MathJax-Span-3" class="texatom"><span id="MathJax-Span-4" class="mrow"><span id="MathJax-Span-5" class="texatom"><span id="MathJax-Span-6" class="mrow"><span id="MathJax-Span-7" class="mo">時<span id="MathJax-Span-8" class="texatom"><span id="MathJax-Span-9" class="mrow"><span id="MathJax-Span-10" class="mo">,<span id="MathJax-Span-11" class="texatom"><span id="MathJax-Span-12" class="mrow"><span id="MathJax-Span-13" class="mo">就<span id="MathJax-Span-14" class="texatom"><span id="MathJax-Span-15" class="mrow"><span id="MathJax-Span-16" class="mo">是<span id="MathJax-Span-17" class="texatom"><span id="MathJax-Span-18" class="mrow"><span id="MathJax-Span-19" class="mo">把時,就是把{ } 替換成變量的值。

使用 #{} 可以有效的防止SQL注入,提高系統安全性。

sql注入指的是用戶輸入 or 1=1等變量導致原sql語句邏輯發生了變化,使得可以直接進入數據庫,而使用預編譯的方法可以讓sql語句只把傳入的數據當做參數進行處理

而不會改變原有sql的邏輯,preparestatement和#{}都是把變量預編譯,防止sql注入。

      

 

4、配置解析

1. 核心配置文件

  • mybatis-config.xml

  • Mybatis的配置文件包含了會深深影響MyBatis行為的設置和屬性信息。 

configuration(配置)
    properties(屬性)        必須掌握
    settings(設置)          必須掌握
    typeAliases(類型別名)    必須掌握
    typeHandlers(類型處理器)
    objectFactory(對象工廠)
    plugins(插件)
    environments(環境配置)   必須掌握
        environment(環境變量)
            transactionManager(事務管理器)
            dataSource(數據源)
    databaseIdProvider(數據庫廠商標識)
    mappers(映射器)

2. 環境配置 environments
MyBatis 可以配置成適應多種環境

不過要記住:盡管可以配置多個環境,但每個 SqlSessionFactory 實例只能選擇一種環境

 

 

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

注意一些關鍵點:

  • 默認使用的環境 ID(比如:default="development")。
  • 每個 environment 元素定義的環境 ID(比如:id="development")。
  • 事務管理器的配置(比如:type="JDBC")。
  • 數據源的配置(比如:type="POOLED")。

默認環境和環境 ID 顧名思義。 環境可以隨意命名,但務必保證默認的環境 ID 要匹配其中一個環境 ID。

MyBatis默認的事務管理器就是JDBC ,連接池:POOLED

3. 屬性 properties
我們可以通過properties屬性來實現引用配置文件

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

  1.編寫一個配置文件

db.properties

driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/mybatis?userSSL=true&useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC
username=root
password=123456

2.在核心配置文件中引入

<!--引用外部配置文件-->
<properties resource="db.properties">
    <property name="username" value="root"/>
    <property name="password" value="123456"/>
</properties>
<?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核心配置文件-->
<configuration>
    <!--注意點:核心配置文件書寫標簽配置順序,properties放在最前面-->
    <!--引入外部配置文件,resource=“路徑”-->
    <properties resource="db.properties"/>

    <!--environments多環境配置 default默認=id-->
    <environments default="development">
        <!--environment環境 id-->
        <environment id="development">
            <!--事務管理 JDBC-->
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">

                <!--引入外部配置文件后,不用寫死value值-->
                <!--<property name="driver" value="com.mysql.cj.jdbc.Driver"/>-->
                <property name="driver" value="${driver}"/>
                <!--<property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=false&amp;useUnicode=true&amp;characterEncoding=UTF-8"/>-->
                <property name="url" value="${url}"/>
                <!--<property name="username" value="root"/>-->
                <property name="username" value="${username}"/>
                <!--<property name="password" value="123456"/>-->
                <property name="password" value="${password}"/>
            </dataSource>
        </environment>
    </environments>
    <!--每一個Mapper.xml都需要在Mybatis核心配置文件中注冊-->
    <mappers>
        <mapper resource="com/tongda/dao/UserMapper.xml" />
    </mappers>
</configuration>

1.可以直接引入外部文件

2.可以在其中增加一些屬性配置

3.如果兩個文件有同一個字段,優先使用外部配置文件的

注意點:核心配置文件書寫標簽配置順序

properties放在最前面

 

 

4. 類型別名 typeAliases

  • 類型別名可為 Java 類型設置一個縮寫名字。 它僅用於 XML 配置.

  • 意在降低冗余的全限定類名書寫。

  mybatis-config.xml中

<!--注意標簽書寫順序,properties,setting,typeAlias-->
    <!--可以給實體類別名-->
    <typeAliases>
      <!--指定一個類,起別名,可以任意別名--> <typeAlias type="com.tongda.pojo.User" alias="User"/> </typeAliases>

 

UserMapper.xml

<!--resultType=“com.tongda.pojo.User",前面mybatis-config.xml中已別名-->
    <select id="getUserList" resultType="User">
        select * from mybatis.user </select>

 也可以指定一個包,每一個在包 domain.blog 中的 Java Bean,在沒有注解的情況下,會使用 Bean 的首字母小寫的非限定類名來作為它的別名

比如 com.tongda.pojo.User 的別名為 user,;若有注解,則別名為其注解值。見下面的例子: 

    <typeAliases>        
        <!--指定一個包,起別名,指定別名為user-->
        <package name="com.tongda.pojo"/>
    </typeAliases>

 

 

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

如果實體類十分多,建議用第二種掃描包的方式。

第一種可以DIY別名,第二種不行,如果非要改,需要在實體上增加注解。

@Alias("user")
public class User {
    ...
} 

5. 設置 Settings

這是 MyBatis 中極為重要的調整設置,它們會改變 MyBatis 的運行時行為。

 

 

 

 

 

 

 

 

 

6. 其他配置

7. 映射器 mappers

MapperRegistry:注冊綁定我們的Mapper文件;

方式一:【推薦使用】

<!--每一個Mapper.xml都需要在MyBatis核心配置文件中注冊-->
<mappers>
    <mapper resource="com/kuang/dao/UserMapper.xml"/>
</mappers>

方式二:使用class文件綁定注冊

<!--每一個Mapper.xml都需要在MyBatis核心配置文件中注冊-->
<mappers>
    <mapper class="com.kuang.dao.UserMapper"/>
</mappers>

注意點:

  • 接口和他的Mapper配置文件必須同名,如UserMapper.java和UserMapper.xml
  • 接口和他的Mapper配置文件必須在同一個包下

方式三:使用包掃描進行注入

<mappers>
    <package name="com.kuang.dao"/>
</mappers>

 

8. 作用域和生命周期

 

 

 聲明周期和作用域是至關重要的,因為錯誤的使用會導致非常嚴重的並發問題

SqlSessionFactoryBuilder:

一旦創建了SqlSessionFactory,就不再需要它了
局部變量


SqlSessionFactory:  *****最核心*****

說白了就可以想象為:數據庫連接池
SqlSessionFactory一旦被創建就應該在應用的運行期間一直存在,沒有任何理由丟棄它或重新創建一個實例
因此SqlSessionFactory的最佳作用域是應用作用域(ApplocationContext)。
最簡單的就是使用單例模式或靜態單例模式。

SqlSession:

連接到連接池的一個請求
SqlSession 的實例不是線程安全的,因此是不能被共享的,所以它的最佳的作用域是請求或方法作用域。
用完之后需要趕緊關閉,否則資源被占用!

 

 

這里每一個mapper,都是代表一個業務模塊

5、解決屬性名和字段名不一致的問題

1. 問題

數據庫中的字段

在這里插入圖片描述

新建一個項目,拷貝之前的,測試實體類字段不一致的情況

在這里插入圖片描述

測試出現問題
在這里插入圖片描述

// select * from user where id = #{id}
// 類型處理器
// select id,name,pwd from user where id = #{id}

 解決方法:

  • 起別名
<select id="getUserById" resultType="com.kuang.pojo.User">
    select id,name,pwd as password from USER where id = #{id}
</select>

 

2. resultMap

結果集映射

id name pwd

id name password

<!--結果集映射,id="UserMap"對應resultMap="UserMap"-->
<resultMap id="UserMap" type="User">
    <!--column數據庫中的字段,property實體類中的屬性-->
    <result column="id" property="id"></result>
    <result column="name" property="name"></result>
    <result column="pwd" property="password"></result>
</resultMap>

<select id="getUserList" resultMap="UserMap">
    select * from USER
</select>

 

 resultMap 元素是 MyBatis 中最重要最強大的元素。

ResultMap 的設計思想是,對簡單的語句做到零配置,對於復雜一點的語句,只需要描述語句之間的關系就行了。

ResultMap 的優秀之處——你完全可以不用顯式地配置它們。

<!--結果集映射,id="UserMap"對應resultMap="UserMap"-->
<resultMap id="UserMap" type="User">
    <!--column數據庫中的字段,property實體類中的屬性-->
    <--相同字段和屬性不用寫-->
    <!--<result column="id" property="id"></result>-->
    <!--<result column="name" property="name"></result>-->
    <!--只寫字段、屬性不同的-->
    <result column="pwd" property="password"></result>
</resultMap>

<select id="getUserList" resultMap="UserMap">
    select * from USER
</select>

如果這個世界總是這么簡單就好了。

6、日志

6.1 日志工廠

如果一個數據庫操作,出現了異常,我們需要排錯,日志就是最好的助手!

曾經:sout、debug

現在:日志工廠

  • SLF4J
  • LOG4J 【掌握】
  • LOG4J2
  • JDK_LOGGING
  • COMMONS_LOGGING
  • STDOUT_LOGGING 【掌握】 標准日志輸出
  • NO_LOGGING

在MyBatis中具體使用哪一個日志實現,在設置中設定

STDOUT_LOGGING 標准日志輸出(無需配置)

mybatis-config.xml中位置在properties后、TypeAliases前

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

 

 6.2 Log4j

什么是Log4j?

Log4j是Apache的一個開源項目,通過使用Log4j,我們可以控制日志信息輸送的目的地是控制台、文件、GUI組件;

我們也可以控制每一條日志的輸出格式;

通過定義每一條日志信息的級別,我們能夠更加細致地控制日志的生成過程;

最令人感興趣的就是,這些可以通過一個配置文件來靈活地進行配置,而不需要修改應用的代碼。

先導入log4j的包

<dependency>
    <groupId>log4j</groupId>
    <artifactId>log4j</artifactId>
    <version>1.2.17</version>
</dependency>

log4j.properties純凈版配置

#將等級為DEBUG的日志信息輸出到console和file這兩個目的地,console和file的定義在下面的代碼
log4j.rootLogger=DEBUG,console,file

#控制台輸出的相關設置
log4j.appender.console
= org.apache.log4j.ConsoleAppender log4j.appender.console.Target = System.out log4j.appender.console.Threshold=DEBUG 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/hzh.log log4j.appender.file.MaxFileSize=10mb 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.sql.Statement=DEBUG log4j.logger.java.sql.ResultSet=DEBUG log4j.logger.java.sql.PreparedStatement=DEBUG

  配置settings為log4j實現

mybatis-config.xml中位置在properties后、TypeAliases前

    <!--日志配置-->
    <settings>
        <setting name="logImpl" value=""/>
    </settings>

 

  測試運行

Log4j簡單使用

  1. 在要使用Log4j的類中,導入包 import org.apache.log4j.Logger;

  2. 日志對象,參數為當前類的class對象 

Logger logger = Logger.getLogger(UserDaoTest.class);

  3.日志級別

logger.info("info: 測試log4j");
logger.debug("debug: 測試log4j");
logger.error("error:測試log4j");

 

7、分頁

思考:為什么分頁?

  • 減少數據的處理量

7.1 使用Limit分頁(通過sql層面實現)

SELECT * from user limit startIndex,pageSize 
select * from user limit 3; #[0,n]

使用MyBatis實現分頁,核心SQL

1.接口UserMapper.java

//分頁推薦方法
List<User> getUserByLimit(Map<String,Integer> map);

2.Mapper.xml

<!--分頁-->
<!--死記startIndex,pageSize參數下標-->
<select id="getUserByLimit" parameterType="map" resultMap="UserMap">
select * from mybatis.user limit #{startIndex},#{pageSize}
</select>

3.測試

//分頁
   @Test
   public void getUserByLimit(){
       //獲取Session
       SqlSession sqlSession = MybatisUtils.getSqlSession();
       //反射獲得Usermapper接口類
       UserMapper mapper = sqlSession.getMapper(UserMapper.class);

       //使用接口類中方法,參數是map,需要構造
       HashMap<String, Integer> map = new HashMap<>();
       //取值要對應Usermapper.xml中的參數
       //設置從0開始
       map.put("startIndex",1);
       //設置到2結束
       map.put("pageSize",2);
       //返回結果集
       List<User> userList = mapper.getUserByLimit(map);
       //循環結果
       for (User user : userList) {
           System.out.println(user);
       }
sqlSession.close(); }

7.2 RowBounds分頁

不再使用SQL實現分頁

 

  1.接口

 

//分頁2
List<User> getUserByRowBounds();

  2.mapper.xml

<!--分頁2,了解下-->
    <select id="getUserByRowBounds" resultMap="UserMap">
        select * from mybatis.user
    </select>

<!--或者,分頁查詢2-->

<select id="getUserByRowBounds">
select * from user limit #{startIndex},#{pageSize}
</select>

3.測試

    public void getUserByRowBounds(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        //RowBounds實現
        RowBounds rowBounds = new RowBounds(1, 2);
        //通過Java代碼層面實現分頁
        List<User> userList = sqlSession.selectList("com.kaung.dao.UserMapper.getUserByRowBounds", null, rowBounds);
        for (User user : userList) {
            System.out.println(user);
        }
        sqlSession.close();
    }

7.3 分頁插件(通過第三方插件實現)

mybatis分頁插件
在這里插入圖片描述
注:不管使用哪種分頁它的底層都是limi

8、使用注解開發

8.1 面向接口開發

根本原因主要為了:==解耦==

1.關於接口的理解。
接口從更深層次的理解,應是定義(規范,約束)與實現(名實分離的原則)的分離。
接口的本身反映了系統設計人員對系統的抽象理解。
接口應有兩類:第一類是對一個個體的抽象,它可對應為一個抽象體(abstract class);
第二類是對一個個體某一方面的抽象,即形成一個抽象面(interface);
一個體有可能有多個抽象面。
抽象體與抽象面是有區別的。

三個面向區別

  • 面向對象是指,我們考慮問題時,以對象為單位,考慮它的屬性和方法;
  • 面向過程是指,我們考慮問題時,以一個具體的流程(事務過程)為單位,考慮它的實現;
  • 接口設計與非接口設計是針對復用技術而言的,與面向對象(過程)不是一個問題,更多的體現就是對系統整體的架構;

8.2 使用注解開發

1.注解在接口上實現 

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

2.需要在核心配置文件中綁定接口

<mappers>
    <mapper class="com.kuang.dao.UserMapper"/>
</mappers>

3.測試

對於增刪改需要把opensession的參數設置為true,這樣就等價於commit操作了,否則無法commit,及時寫了commit,

mybatisUtils.java工具類中

//既然有了 SqlSessionFactory,顧名思義,我們可以從中獲得 SqlSession 的實例。
    // SqlSession 提供了在數據庫執行 SQL 命令所需的所有方法。你可以通過 SqlSession 實例來直接執行已映射的 SQL 語句。
    public static SqlSession getSqlSession() {
        //獲取要提升作用域
        //固定代碼
        // 從 SqlSessionFactory 中獲取 SqlSession
        // SqlSession sqlSession = sqlSessionFactory.openSession();
        // return sqlSession;
        //優化
        return sqlSessionFactory.openSession();
    }
// 默認參數不提交事務
    public static SqlSession getSqlSession() {
        return getSqlSession(false);
    }

//如果添加這個參數,就會自動提交事務
    public static SqlSession getSqlSession(){
        return sqlSessionFactory.openSession(true);
    }

 

UserMapper.java接口

public interface UserMapper {
    //不在使用mapper.xml,使用注解
    @Select("select * from user")
    List<User> getUsers();
    //去mybatis-config.xml中綁定接口


    //方法存在多個參數,所有的參數前面必須加上@param("id"),規范
    @Select("select * from user where id = ${id}")
    //注意取參對應@param("參數名qid“),id=#{參數名qid}
    // User getUserByID(@Param("qid") int id);
    User getUserByID(@Param("id") int id);


    @Insert("insert into user(id,name,pwd) values (#{id},#{name},#{password})")
    // insert into user (id, name, pwd) VALUES (4,"李四","123456");
    int addUser(User user);


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

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


}

UserDaoTest.java

對於增刪改需要把opensession的參數設置為true,這樣就等價於commit操作了,否則無法commit,及時寫了commit,

public class UserDaoTest {
    @Test
    public void test(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        //底層使用反射機制,獲取類中所有方法
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);

        List<User> users = mapper.getUsers();
        for (User user : users) {
            System.out.println(user);

        }

        sqlSession.close();

    }

    @Test
    public void getUserById(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        User userByID = mapper.getUserByID(1);
        System.out.println(userByID);

        sqlSession.close();
    }

    @Test
    public void addUser(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);

        mapper.addUser(new User(4,"李四","123456"));

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

    @Test
    public void UpdateUser(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        mapper.updateUser(new User(4,"馬六","123123"));

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

    @Test
    public void deleteUser(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        mapper.deleteUser(4);

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

 

本質:反射機制實現

底層:動態代理
在這里插入圖片描述
MyBatis詳細執行流程
在這里插入圖片描述

8.3 注解CURD

Utils工具類

//如果添加這個參數,就會自動提交事務
    public static SqlSession getSqlSession(){
        return sqlSessionFactory.openSession(true);
    }

UserMapper.java接口類

public interface UserMapper {
    //不在使用mapper.xml,使用注解
    @Select("select * from user")
    List<User> getUsers();
    //去mybatis-config.xml中綁定接口


    //方法存在多個參數,所有的參數前面必須加上@param("id"),規范
    @Select("select * from user where id = #{id}")
    //注意取參對應@param("參數名“),id=#{參數名}
    // User getUserByID(@Param("qid") int id);
    User getUserByID(@Param("id") int id);


    @Insert("insert into user(id,name,pwd) values (#{id},#{name},#{password})")
    // insert into user (id, name, pwd) VALUES (4,"李四","123456");
    int addUser(User user);


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

    //方法存在多個參數,所有的參數前面必須加上@Param("id")注解
    @Delete("delete from user where id=#{uid},name=#{uname}")
    int deleteUser(@Param("uid") int id,@Param("uname") String name);

}

UserDaoTest.java測試類

對於增刪改需要把opensession的參數設置為true,這樣就等價於commit操作了,否則無法commit,及時寫了commit,

public class UserDaoTest {
    @Test
    public void test(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        //底層使用反射機制,獲取類中所有方法
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);

        List<User> users = mapper.getUsers();
        for (User user : users) {
            System.out.println(user);
        }

        sqlSession.close();
    }

    @Test
    public void getUserById(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        User userByID = mapper.getUserByID(1);
        System.out.println(userByID);

        sqlSession.close();
    }

    @Test
    public void addUser(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);

        mapper.addUser(new User(4,"李四","123456"));

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

    @Test
    public void UpdateUser(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        mapper.updateUser(new User(4,"馬六","123123"));

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

    @Test
    public void deleteUser(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        mapper.deleteUser(4,"李四");

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

}

【注意:必須要將接口注冊綁定到我們的核心配置文件中!】

關於@Param( )注解

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

#{} 和 ${}區別  

(1)n#{}表示一個占位符號,通過#{}可以實現preparedStatement向占位符中設置值,自動進行java類型和jdbc類型轉換,#{}可以有效防止sql注入。#{}可以接收簡單類型值或pojo屬性值。如果parameterType傳輸單個簡單類型值,#{}括號中可以是value或其它名稱。

(2)n<span id="MathJax-Span-2" class="mrow"><span id="MathJax-Span-3" class="texatom"><span id="MathJax-Span-4" class="mrow"><span id="MathJax-Span-5" class="texatom"><span id="MathJax-Span-6" class="mrow"><span id="MathJax-Span-7" class="mo">表<span id="MathJax-Span-8" class="texatom"><span id="MathJax-Span-9" class="mrow"><span id="MathJax-Span-10" class="mo">示<span id="MathJax-Span-11" class="texatom"><span id="MathJax-Span-12" class="mrow"><span id="MathJax-Span-13" class="mo">拼<span id="MathJax-Span-14" class="texatom"><span id="MathJax-Span-15" class="mrow"><span id="MathJax-Span-16" class="mo">接<span id="MathJax-Span-17" class="mi">s<span id="MathJax-Span-18" class="mi">q<span id="MathJax-Span-19" class="mi">l<span id="MathJax-Span-20" class="texatom"><span id="MathJax-Span-21" class="mrow"><span id="MathJax-Span-22" class="mo">串<span id="MathJax-Span-23" class="texatom"><span id="MathJax-Span-24" class="mrow"><span id="MathJax-Span-25" class="mo">,<span id="MathJax-Span-26" class="texatom"><span id="MathJax-Span-27" class="mrow"><span id="MathJax-Span-28" class="mo">通<span id="MathJax-Span-29" class="texatom"><span id="MathJax-Span-30" class="mrow"><span id="MathJax-Span-31" class="mo">過表示拼接sql串,通過{}可以將parameterType傳入的內容拼接在sql中且不進行jdbc類型轉換,<span id="MathJax-Span-33" class="mrow"><span id="MathJax-Span-34" class="texatom"><span id="MathJax-Span-35" class="mrow"><span id="MathJax-Span-36" class="texatom"><span id="MathJax-Span-37" class="mrow"><span id="MathJax-Span-38" class="mo">可<span id="MathJax-Span-39" class="texatom"><span id="MathJax-Span-40" class="mrow"><span id="MathJax-Span-41" class="mo">以<span id="MathJax-Span-42" class="texatom"><span id="MathJax-Span-43" class="mrow"><span id="MathJax-Span-44" class="mo">接<span id="MathJax-Span-45" class="texatom"><span id="MathJax-Span-46" class="mrow"><span id="MathJax-Span-47" class="mo">收<span id="MathJax-Span-48" class="texatom"><span id="MathJax-Span-49" class="mrow"><span id="MathJax-Span-50" class="mo">簡<span id="MathJax-Span-51" class="texatom"><span id="MathJax-Span-52" class="mrow"><span id="MathJax-Span-53" class="mo">單<span id="MathJax-Span-54" class="texatom"><span id="MathJax-Span-55" class="mrow"><span id="MathJax-Span-56" class="mo">類<span id="MathJax-Span-57" class="texatom"><span id="MathJax-Span-58" class="mrow"><span id="MathJax-Span-59" class="mo">型<span id="MathJax-Span-60" class="texatom"><span id="MathJax-Span-61" class="mrow"><span id="MathJax-Span-62" class="mo">值<span id="MathJax-Span-63" class="texatom"><span id="MathJax-Span-64" class="mrow"><span id="MathJax-Span-65" class="mo">或<span id="MathJax-Span-66" class="mi">p<span id="MathJax-Span-67" class="mi">o<span id="MathJax-Span-68" class="mi">j<span id="MathJax-Span-69" class="mi">o<span id="MathJax-Span-70" class="texatom"><span id="MathJax-Span-71" class="mrow"><span id="MathJax-Span-72" class="mo">屬<span id="MathJax-Span-73" class="texatom"><span id="MathJax-Span-74" class="mrow"><span id="MathJax-Span-75" class="mo">性<span id="MathJax-Span-76" class="texatom"><span id="MathJax-Span-77" class="mrow"><span id="MathJax-Span-78" class="mo">值<span id="MathJax-Span-79" class="texatom"><span id="MathJax-Span-80" class="mrow"><span id="MathJax-Span-81" class="mo">,<span id="MathJax-Span-82" class="texatom"><span id="MathJax-Span-83" class="mrow"><span id="MathJax-Span-84" class="mo">如<span id="MathJax-Span-85" class="texatom"><span id="MathJax-Span-86" class="mrow"><span id="MathJax-Span-87" class="mo">果<span id="MathJax-Span-88" class="mi">p<span id="MathJax-Span-89" class="mi">a<span id="MathJax-Span-90" class="mi">r<span id="MathJax-Span-91" class="mi">a<span id="MathJax-Span-92" class="mi">m<span id="MathJax-Span-93" class="mi">e<span id="MathJax-Span-94" class="mi">t<span id="MathJax-Span-95" class="mi">e<span id="MathJax-Span-96" class="mi">r<span id="MathJax-Span-97" class="mi">T<span id="MathJax-Span-98" class="mi">y<span id="MathJax-Span-99" class="mi">p<span id="MathJax-Span-100" class="mi">e<span id="MathJax-Span-101" class="texatom"><span id="MathJax-Span-102" class="mrow"><span id="MathJax-Span-103" class="mo">傳<span id="MathJax-Span-104" class="texatom"><span id="MathJax-Span-105" class="mrow"><span id="MathJax-Span-106" class="mo">輸<span id="MathJax-Span-107" class="texatom"><span id="MathJax-Span-108" class="mrow"><span id="MathJax-Span-109" class="mo">單<span id="MathJax-Span-110" class="texatom"><span id="MathJax-Span-111" class="mrow"><span id="MathJax-Span-112" class="mo">個<span id="MathJax-Span-113" class="texatom"><span id="MathJax-Span-114" class="mrow"><span id="MathJax-Span-115" class="mo">簡<span id="MathJax-Span-116" class="texatom"><span id="MathJax-Span-117" class="mrow"><span id="MathJax-Span-118" class="mo">單<span id="MathJax-Span-119" class="texatom"><span id="MathJax-Span-120" class="mrow"><span id="MathJax-Span-121" class="mo">類<span id="MathJax-Span-122" class="texatom"><span id="MathJax-Span-123" class="mrow"><span id="MathJax-Span-124" class="mo">型<span id="MathJax-Span-125" class="texatom"><span id="MathJax-Span-126" class="mrow"><span id="MathJax-Span-127" class="mo">值<span id="MathJax-Span-128" class="texatom"><span id="MathJax-Span-129" class="mrow"><span id="MathJax-Span-130" class="mo">,可以接收簡單類型值或pojo屬性值,如果parameterType傳輸單個簡單類型值,{}括號中只能是value。

注:
(1)簡單類型就是不是自己定義的類型

(2)模糊查詢:'%${value}%' 不可漏掉單引號

他們之間的區別用最直接的話來說就是:#相當於對數據 加上 雙引號,$相當於直接顯示數據。

1、#對傳入的參數視為字符串,也就是它會預編譯,select * from user where name = #{name},比如我傳一個csdn,那么傳過來就是 select * from user where name = 'csdn';

2、<span id="MathJax-Span-132" class="mrow"><span id="MathJax-Span-133" class="texatom"><span id="MathJax-Span-134" class="mrow"><span id="MathJax-Span-135" class="mo">將<span id="MathJax-Span-136" class="texatom"><span id="MathJax-Span-137" class="mrow"><span id="MathJax-Span-138" class="mo">不<span id="MathJax-Span-139" class="texatom"><span id="MathJax-Span-140" class="mrow"><span id="MathJax-Span-141" class="mo">會<span id="MathJax-Span-142" class="texatom"><span id="MathJax-Span-143" class="mrow"><span id="MathJax-Span-144" class="mo">將<span id="MathJax-Span-145" class="texatom"><span id="MathJax-Span-146" class="mrow"><span id="MathJax-Span-147" class="mo">傳<span id="MathJax-Span-148" class="texatom"><span id="MathJax-Span-149" class="mrow"><span id="MathJax-Span-150" class="mo">入<span id="MathJax-Span-151" class="texatom"><span id="MathJax-Span-152" class="mrow"><span id="MathJax-Span-153" class="mo">的<span id="MathJax-Span-154" class="texatom"><span id="MathJax-Span-155" class="mrow"><span id="MathJax-Span-156" class="mo">值<span id="MathJax-Span-157" class="texatom"><span id="MathJax-Span-158" class="mrow"><span id="MathJax-Span-159" class="mo">進<span id="MathJax-Span-160" class="texatom"><span id="MathJax-Span-161" class="mrow"><span id="MathJax-Span-162" class="mo">行<span id="MathJax-Span-163" class="texatom"><span id="MathJax-Span-164" class="mrow"><span id="MathJax-Span-165" class="mo">預<span id="MathJax-Span-166" class="texatom"><span id="MathJax-Span-167" class="mrow"><span id="MathJax-Span-168" class="mo">編<span id="MathJax-Span-169" class="texatom"><span id="MathJax-Span-170" class="mrow"><span id="MathJax-Span-171" class="mo">譯<span id="MathJax-Span-172" class="texatom"><span id="MathJax-Span-173" class="mrow"><span id="MathJax-Span-174" class="mo">,<span id="MathJax-Span-175" class="mi">s<span id="MathJax-Span-176" class="mi">e<span id="MathJax-Span-177" class="mi">l<span id="MathJax-Span-178" class="mi">e<span id="MathJax-Span-179" class="mi">c<span id="MathJax-Span-180" class="mi">t<span id="MathJax-Span-181" class="mo">&lowast;<span id="MathJax-Span-182" class="mi">f<span id="MathJax-Span-183" class="mi">r<span id="MathJax-Span-184" class="mi">o<span id="MathJax-Span-185" class="mi">m<span id="MathJax-Span-186" class="mi">u<span id="MathJax-Span-187" class="mi">s<span id="MathJax-Span-188" class="mi">e<span id="MathJax-Span-189" class="mi">r<span id="MathJax-Span-190" class="mi">w<span id="MathJax-Span-191" class="mi">h<span id="MathJax-Span-192" class="mi">e<span id="MathJax-Span-193" class="mi">r<span id="MathJax-Span-194" class="mi">e<span id="MathJax-Span-195" class="mi">n<span id="MathJax-Span-196" class="mi">a<span id="MathJax-Span-197" class="mi">m<span id="MathJax-Span-198" class="mi">e<span id="MathJax-Span-199" class="mo">=將不會將傳入的值進行預編譯,select∗fromuserwherename={name},比如我穿一個csdn,那么傳過來就是 select * from user where name=csdn;

3、#的優勢就在於它能很大程度的防止sql注入,而$則不行。

比如:用戶進行一個登錄操作,后台sql驗證式樣的:select * from user where username=#{name} and password = #{pwd},

如果前台傳來的用戶名是“wang”,密碼是 “1 or 1=1”,用#的方式就不會出現sql注入,而如果換成$方式,

sql語句就變成了 select * from user where username=wang and password = 1 or 1=1。這樣的話就形成了sql注入。

4、MyBatis排序時使用order by 動態參數時需要注意,用$而不是# 

字符串替換
默認情況下,使用#{}格式的語法會導致MyBatis創建預處理語句屬性並以它為背景設置安全的值(比如?)。這樣做很安全,很迅速也是首選做法,有時你只是想直接在SQL語句中插入一個不改變的字符串。比如,像ORDER BY,你可以這樣來使用:
ORDER BY ${columnName}
這里MyBatis不會修改或轉義字符串。

重要:接受從用戶輸出的內容並提供給語句中不變的字符串,這樣做是不安全的。這會導致潛在的SQL注入攻擊,因此你不應該允許用戶輸入這些字段,或者通常自行轉義並檢查

  

9、Lombok

Lombok項目是一個Java庫,它會自動插入編輯器和構建工具中,Lombok提供了一組有用的注釋,用來消除Java類中的大量樣板代碼。僅五個字符(@Data)就可以替換數百行代碼從而產生干凈,簡潔且易於維護的Java類。

使用步驟:

1.在IDEA中安裝Lombok插件

2.在項目中導入lombok的jar包

<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.10</version>
    <scope>provided</scope>
</dependency>

 3.在程序上加注解

@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

說明:

@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
    private int id;
    private String name;
    private String password;
}

 在這里插入圖片描述

對於mybatis來說復雜的sql語句還是用配置文件來做,下面兩個點是多對一一對多的介紹。推薦使用聯合嵌套查詢

10、多對一處理:mybatis-06模塊

多個學生一個老師;

alter table student ADD CONSTRAINT fk_tid foreign key (tid) references teacher(id) 

數據庫表:

CREATE TABLE `teacher` (
  `id` INT(10) NOT NULL,
  `name` VARCHAR(30) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=INNODB DEFAULT CHARSET=utf8

INSERT INTO teacher(`id`, `name`) VALUES (1, '秦老師');

CREATE TABLE `student` (
  `id` INT(10) NOT NULL,
  `name` VARCHAR(30) DEFAULT NULL,
  `tid` INT(10) DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `fktid` (`tid`),
  CONSTRAINT `fktid` FOREIGN KEY (`tid`) REFERENCES `teacher` (`id`)
) ENGINE=INNODB DEFAULT CHARSET=utf8INSERT INTO `student` (`id`, `name`, `tid`) VALUES ('1', '小明', '1');
INSERT INTO `student` (`id`, `name`, `tid`) VALUES ('2', '小紅', '1');
INSERT INTO `student` (`id`, `name`, `tid`) VALUES ('3', '小張', '1');
INSERT INTO `student` (`id`, `name`, `tid`) VALUES ('4', '小李', '1');
INSERT INTO `student` (`id`, `name`, `tid`) VALUES ('5', '小王', '1');

1. 測試環境搭建

  1. 導入lombok
  2. 新建實體類Teacher,Student
  3. 建立Mapper接口
  4. 建立Mapper.xml文件
  5. 在核心配置文件中綁定注冊我們的Mapper接口或者文件 【方式很多,隨心選】
  6. 測試查詢是否能夠成功

2.創建pojio實體類和Dao層要一一對應

Student實體類

package com.kuangshen.pojo;

import lombok.Data;

@Data
public class Student {
    private int id;
    private String name;
    private Teacher teacher;
}

Teacher實體類

1 package com.kuangshen.pojo;
2 
3 import lombok.Data;
4 
5 @Data
6 public class Teacher {
7     private int id;
8     private String name;
9 }

3.在resources創建跟com.tongda.dao包路徑要一致,確保xml和mapper接口同包下

Mapper接口和對應的配置文件

StudentMapper

package com.kuangshen.dao;

import com.bupt.pojo.Student;

import java.util.List;

public interface StudentMapper {
    //查詢所有學生的信息,及其對應的老師的信息
    public List<Student> getStudent();
    public List<Student> getStudent2();
}

 

4.StudentMapper.xml和TeacherMapper.xml中

  

StudentMapper.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.tongda.dao.StudentMapper">

    <!--思路:
    1. 查詢所有的學生信息
    2. 根據查詢出來的學生的tid尋找特定的老師 (子查詢)-->

    <!--方式一:按照子查詢的方式-->
    <select id="getStudent" resultMap="StudentTeacher">

        select * from mybatis.student;
    </select>
    <!--關聯resultMap="StudentTeacher" type=實體類名-->
    <resultMap id="StudentTeacher" type="Student">
        <!--property指的是java中的字段屬性,column指的是查詢的數據庫中列表-->
        <result property="id" column="id"/>
        <result property="name" column="name"/>
        <!--復雜的屬性,我們需要單獨處理,對象:association 實體類中老師對象,集合:collection -->
        <!--關鍵點嵌套查詢:javaType是給property="teacher"賦值類型,在使用select=“getTeacher"嵌套查詢-->
        <association property="teacher" column="tid" javaType="Teacher" select="getTeacher"/>
    </resultMap>

    <select id="getTeacher" resultType="Teacher">
        select * from teacher where id = #{id}
    </select>

    <!--方法二:按照結果嵌套查詢處理-->
    <select id="getStudent2" resultMap="StudentTeacher">
        select s.id sid,s.name sname,t.name table_name
        from student s,teacher t
        where s.tid=t.id
    </select>
    <!--結果封裝,將查詢出來的列封裝到對象屬性中-->
    <resultMap id="StudentTeacher2" type="student">
        <result property="id" column="sid"/>
        <result property="name" column="sname"/>
        <!--復雜的屬性,我們需要單獨處理,對象:association 實體類中老師對象,集合:collection -->
        <association property="teacher" javaType="teacher">
            <result property="name" column="tnmae"/>
        </association>
    </resultMap>
</mapper>

TeacherMapper.xml

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

<!--configuration核心配置文件-->
<mapper namespace="com.tongda.dao.TeacherMapper">

</mapper>

5.mybatis-config.xml中

    <!--每一個Mapper.xml都需要在Mybatis核心配置文件中注冊-->
    <!--綁定接口-->
    <mappers>
        <!--<mapper resource="com/tongda/dao/*Mapper.xml"/>-->
        <mapper class="com.tongda.dao.TeacherMapper"/>
        <mapper class="com.tongda.dao.StudentMapper"/>
        <!--<package name="com.tongda.dao"/>-->
    </mappers>

 6.測試

public class MyTest {
    public static void main(String[] args) {
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        TeacherMapper mapper = sqlSession.getMapper(TeacherMapper.class);
        Teacher teacher = mapper.getTeacher(1);
        System.out.println(teacher);

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

2. 按照查詢嵌套處理 

其中最重要的就是兩種處理方式 

方式一:按照查詢嵌套處理 

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.tongda.dao.StudentMapper">

    <!--思路:
    1. 查詢所有的學生信息
    2. 根據查詢出來的學生的tid尋找特定的老師 (子查詢)-->

    <!--方式一:按照子查詢的方式-->
    <select id="getStudent" resultMap="StudentTeacher">
        select * from student;
    </select>
    <!--關聯resultMap="StudentTeacher" type=實體類名-->
    <resultMap id="StudentTeacher" type="Student">
        <!--property指的是java中的字段屬性,column指的是查詢的數據庫中列表-->
        <result property="id" column="id"/>
        <result property="name" column="name"/>
        <!--復雜的屬性,我們需要單獨處理,對象:association 實體類中老師對象,集合:collection -->
        <!--關鍵點嵌套查詢:javaType是給property="teacher"賦值類型,在使用select=“getTeacher"嵌套查詢-->
        <association property="teacher" column="tid" javaType="Teacher" select="getTeacher"/>
    </resultMap>

    <select id="getTeacher" resultType="Teacher">
        select * from teacher where id = #{id}
    </select>

</mapper>

 方式二:按照結果嵌套處理

<!--按照結果進行查詢-->
    <select id="getStudent2" resultMap="StudentTeacher2">
        select s.id sid , s.name sname, t.name tname
        from student s,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>
        </association>
    </resultMap>

回顧Mysql多對一查詢方式:

  • 子查詢 (按照查詢嵌套)
  • 聯表查詢 (按照結果嵌套)

11、一對多處理

一個老師多個學生;

對於老師而言,就是一對多的關系;

1. 環境搭建

實體類

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

    //外鍵關聯語句,關聯teacher表
    //KEY `fktid` (`tid`),
    // CONSTRAINT `fktid` FOREIGN KEY(`tid`) REFERENCES `teacher` (`id`)
    private int tid;

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

    //一個老師擁有多個學生
    private List<Student> students;
}

2. 按照結果嵌套嵌套處理

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

<!--configuration核心配置文件-->
<mapper namespace="com.tongda.dao.TeacherMapper">

    <!--<select id="getTeacher" resultType="Teacher">
        select * from teacher
    </select>-->

    <!--當結果集出現null,需要做映射-->
    <!--按結果嵌套查詢-->
    <select id="getTeacher" resultMap="TeacherStudent">
        select s.id sid,s.name sname,t.name tname,t.id tid
        from student s,teacher t
        where s.tid = t.id and t.id = #{tid}
    </select>
    <!--mapper成對,類型=teacher-->
    <resultMap id="TeacherStudent" type="teacher">
        <result property="id" column="tid"/>
        <result property="name" column="tname"/>
        <!--復雜的屬性,我們需要單獨處理 對象:association 集合:collection
        javaType=""指定屬性的類型!
        集合中的泛型信息,我們使用ofType獲取
         -->
        <!--property=實體類中屬性,ofType=實體類中的泛型-->
        <collection property="students" ofType="Student">
            <!--Student 實體類屬性映射-->
            <result property="id" column="sid"/>
            <result property="name" column="sname"/>
            <result property="tid" column="tid"/>
        </collection>
    </resultMap>

</mapper>

 方式二:子查詢方式實現一對多

    <!--先查老師-->
    <select id="getTeacher2" resultMap="TeacherStudent2">
        select * from mybatis.teacher where id=#{tid}
    </select>
    <resultMap id="TeacherStudent2" type="Teacher">
        <!--實體類中相同屬性與字段可省略,只寫不同-->
        <!--<result property="id" column="id"/>-->
        <!--<result property="name" column="name"/>-->

        <!--復雜的屬性,我們需要單獨處理 對象:association 集合:collection
        javaType=""指定屬性的類型!
        集合中的泛型信息,我們使用ofType獲取
         -->
        <!--property=實體類中屬性,ofType=實體類中的泛型-->
        <collection property="students" javaType="ArrayList" ofType="Student" select="getStudentByTeacherId" column="id"/>

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

小結

1.關聯 - association 【多對一】
2.集合 - collection 【一對多】
3.javaType & ofType

  1. JavaType用來指定實體類中的類型
  2. ofType用來指定映射到List或者集合中的pojo類型,泛型中的約束類型

​ 注意點:

  • 保證SQL的可讀性,盡量保證通俗易懂
  • 注意一對多和多對一,屬性名和字段的問題
  • 如果問題不好排查錯誤,可以使用日志,建議使用Log4j

面試高頻

  • Mysql引擎
  • InnoDB底層原理
  • 索引
  • 索引優化

12、動態SQL

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

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

動態 SQL 是 MyBatis 的強大特性之一。如果你使用過 JDBC 或其它類似的框架,你應該能理解
根據不同條件拼接 SQL 語句有多痛苦,例如拼接時要確保不能忘記添加必要的空格,還要注意去
掉列表最后一個列名的逗號。利用動態 SQL,可以徹底擺脫這種痛苦。

之前用過 JSTL 或任何基於類 XML 語言的文本處理器,你對動態 SQL 元素可能會感覺似曾相識。在 MyBatis 之前的版本中,需要花時間了解大量的元素。借助功能強大的基於 OGNL 的表達式,MyBatis 3 替換了之前的大部分元素,大大精簡了元素種類,現在要學習的元素種類比原來的一半還要少。

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

搭建環境

創建SQL表時,id因使用UUID,類型Varchar(50)

CREATE TABLE `blog`(
    `id` VARCHAR(50) NOT NULL  COMMENT "博客id",
    `title` VARCHAR(30) NOT NULL COMMENT "博客標題",
    `author` VARCHAR(30) NOT NULL COMMENT "博客作者",
    `create_time` datetime(0) NOT NULL COMMENT "創建時間",
    `views` INT(30) NOT NULL COMMENT "瀏覽量",
    PRIMARY KEY (`id`)
    
)ENGINE=INNODB DEFAULT CHARSET=utf8

創建一個基礎工程

1.導包

2.編寫配置文件

3.編寫實體類

@Data
public class Blog {

    private String id;
    private String title;
    private String author;
    //用java.utils包,字段名駝峰,屬性與字段不一致
    private Date createTime;
    private int views;

}

解決屬性名和字段不一致問題:

mybatis-config.xml中

mapUnderscoreToCamelCase 是否開啟自動駝峰命名規則(camel case)映射,即從經典數據庫列名 A_COLUMN 到經典 Java 屬性名 aColumn 的類似映射。 true, false False
<!--日志配置-->
    <settings>
        <!--標准的日志工廠實現-->
        <setting name="logImpl" value="STDOUT_LOGGING"/>
        <!--<setting name="logImpl" value="LOG4J"/>-->
        <!--是否開啟自動駝峰命名規則(camel case)映射,即從經典數據庫列名 A_COLUMN 到經典 Java 屬性名 aColumn 的類似映射-->
        <setting name="mapUnderscoreToCamelCase" value="true"/>
    </settings>

編寫工具類IDutils

@SuppressWarnings("all")//抑制警告
public class IDutils {

    public static String getId(){
        //自增ID是有序的,而UUID是隨機的
        // 數據量非常大需要分庫,或者需要更好的安全性,那么使用UUID
        return UUID.randomUUID().toString().replaceAll("-","");
    }

    @Test
    public void test(){
        System.out.println(IDutils.getId());
        System.out.println(IDutils.getId());
        System.out.println(IDutils.getId());
    }

}

4.編寫實體類對應Mapper接口和Mapper.xml文件

 BlogMapper

public interface BlogMapper {

    int addBook(Blog blog);

    List<Blog> queryBlogIf(Map map);

    List<Blog> queryChoose(Map map);

    //更新博客
    int updateBlog(Map map);
}

BlogMapper.xml

通過使用動態sql可以動態的編寫sql語句,並且動態sql可以自動去除多余的and和where和,等多余的內容。

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.bupt.dao.BlogMapper">

    <insert id="addBook" parameterType="blog">
        insert into blog(id,title,author,create_time,views) values (#{id},#{title},#{author},#{createTime},#{views});
    </insert>

    <select id="queryBlogIf" parameterType="map" resultType="blog">
        select * from blog
       <where>
           <if test="title != null">
               title = #{title}
           </if>
           <if test="author != null">
               and author = #{author}
           </if>
       </where>
    </select>

    <select id="queryChoose" parameterType="map" resultType="blog">
        select * from blog
        <where>
            <choose>
                <when test="title != null">
                    title = #{title}
                </when>
                <when test="author !=null">
                    and author = #{author}
                </when>
                <otherwise>
                    and view = #{views}
                </otherwise>
            </choose>
        </where>
    </select>

    <update id="updateBlog" parameterType="map">
        update blog
        <set>
            <if test="title != null">
                title = #{title},
            </if>
            <if test="author != null">
                author = #{author}
            </if>
        </set>
        where id = #{id}
    </update>


</mapper>

 測試

public class MyTest {

    @Test
    public void addInitBlog(){
        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(9999);
        
        mapper.addBlog(blog);
        
        blog.setId(IDutils.getId());
        blog.setTitle("java如此簡單");
        mapper.addBlog(blog);
        
        blog.setId(IDutils.getId());
        blog.setTitle("Spring如此簡單");
        mapper.addBlog(blog);
        
        blog.setId(IDutils.getId());
        blog.setTitle("微服務如此簡單");
        mapper.addBlog(blog);

        sqlSession.close();
    }
}

 IF語句

需求:根據作者名字和博客名字來查詢博客!如果作者名字為空,那么只根據博客名字查詢,反之,則根據作者名來查詢

1、編寫接口類Mapper接口

//需求1
List<Blog> queryBlogIf(Map map);

2、編寫SQL語句Mapper.xml文件

<!--需求1:
根據作者名字和博客名字來查詢博客!
如果作者名字為空,那么只根據博客名字查詢,反之,則根據作者名來查詢
select * from blog where title = #{title} and author = #{author}
-->
<select id="queryBlogIf" parameterType="map" resultType="blog">
  select * from blog where
   <if test="title != null">
      title = #{title}
   </if>
   <if test="author != null">
      and author = #{author}
   </if>
</select>

3、測試

@Test
public void testQueryBlogIf(){
   SqlSession session = MybatisUtils.getSession();
   BlogMapper mapper = session.getMapper(BlogMapper.class);

   HashMap<String, String> map = new HashMap<String, String>();
   map.put("title","Mybatis如此簡單");
   map.put("author","狂神說");
   List<Blog> blogs = mapper.queryBlogIf(map);

   System.out.println(blogs);

   session.close();
}

這樣寫我們可以看到,如果 author 等於 null,那么查詢語句為 select * from user where title=#{title},但是如果title為空呢?那么查詢語句為 select * from user where and author=#{author},這是錯誤的 SQL 語句如何解決呢?請看下面的 where 語句!

Where&trim語句

修改上面的SQL語句;mapper.xml 

<select id="queryBlogIf" parameterType="map" resultType="blog">
  select * from blog
   <where>
       <if test="title != null">
          title = #{title}
       </if>
       <if test="author != null">
          and author = #{author}
       </if>
   </where>
</select>

 

這個“where”標簽會知道如果它包含的標簽中有返回值的話,它就插入一個‘where’。此外,如果標簽返回的內容是以AND 或OR 開頭的,則它會剔除掉。

trim, where, set

where 元素只會在子元素返回任何內容的情況下才插入 “WHERE” 子句。而且,若子句的開頭為 “AND” 或 “OR”,where 元素也會將它們去除。

如果 where 元素與你期望的不太一樣,你也可以通過自定義 trim 元素來定制 where 元素的功能。比如,和 where 元素等價的自定義 trim 元素為:

<trim prefix="WHERE" prefixOverrides="AND |OR ">
  ...
</trim>

前綴prefixOverrides 屬性會忽略通過管道符分隔的文本序列(注意此例中的空格是必要的)。上述例子會移除所有 prefixOverrides 屬性中指定的內容,並且插入 prefix 屬性中指定的內容。

后綴suffixOerrides屬性

 

Set&trim語句

同理,上面的對於查詢 SQL 語句包含 where 關鍵字,如果在進行更新操作的時候,含有 set 關鍵詞,我們怎么處理呢? 

1、編寫接口方法

int updateBlog(Map map);

2、sql配置文件

<!--注意set是用的逗號隔開-->
<update id="updateBlog" parameterType="map">
  update blog
     <set>
         <if test="title != null">
            title = #{title},
         </if>
         <if test="author != null">
            author = #{author}
         </if>
     </set>
  where id = #{id};
</update>

3、測試

@Test
public void testUpdateBlog(){
   SqlSession session = MybatisUtils.getSession();
   BlogMapper mapper = session.getMapper(BlogMapper.class);

   HashMap<String, String> map = new HashMap<String, String>();
   map.put("title","動態SQL");
   map.put("author","秦疆");
   map.put("id","9d6a763f5e1347cebda43e2a32687a77");

   mapper.updateBlog(map);

   session.close();
}

trim

這個例子中,set 元素會動態地在行首插入 SET 關鍵字,並會刪掉額外的逗號(這些逗號是在使用條件語句給列賦值時引入的)。

來看看與 set 元素等價的自定義 trim 元素吧:

<trim prefix="SET" suffixOverrides=",">
  ...
</trim>

注意,我們覆蓋了后綴值設置,並且自定義了前綴值。

 

choose語句:

choose:選擇(when:什么時候、otherwise:其他)

有時候,我們不想用到所有的查詢條件,只想選擇其中的一個,查詢條件有一個滿足即可,使用 choose 標簽可以解決此類問題,類似於 Java 的 switch 語句

1、編寫接口方法..

List<Blog> queryBlogChoose(Map map);

2、sql配置文件

<!--where:條件  choose語句:選擇, when:什么時候-->
    <select id="queryBlogChoose" parameterType="map" resultType="blog">
        select * from 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>

3、測試類

@Test
public void testQueryBlogChoose(){
   SqlSession session = MybatisUtils.getSession();
   BlogMapper mapper = session.getMapper(BlogMapper.class);

   HashMap<String, Object> map = new HashMap<String, Object>();
   map.put("title","Java如此簡單");
   map.put("author","狂神說");
   map.put("views",9999);
   List<Blog> blogs = mapper.queryBlogChoose(map);

   System.out.println(blogs);

   session.close();
}

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

練習:29道練習題實戰

 

SQL片段

有時候可能某個 sql 語句我們用的特別多,為了增加代碼的重用性,簡化代碼,我們需要將這些代碼抽取出來,然后使用時直接調用。

提取SQL片段: 

<!-- sql id="名字隨意起" -->
<sql id="if-title-author">
   <if test="title != null">
      title = #{title}
   </if>
   <if test="author != null">
      and author = #{author}
   </if>
</sql>

 引用SQL片段: 

<select id="queryBlogIf" parameterType="map" resultType="blog">
  select * from blog
   <where>
       <!-- 引用 sql 片段,如果refid 指定的不在本文件中,那么需要在前面加上 namespace -->
       <include refid="if-title-author"></include>
       <!-- 在這里還可以引用其他的 sql 片段 -->
   </where>
</select>

注意:

①、最好基於 單表來定義 sql 片段,提高片段的可重用性

②、在 sql 片段中不要包括 where

 

Foreach

將數據庫中前三個數據的id修改為1,2,3;

需求:我們需要查詢 blog 表中 id 分別為1,2,3的博客信息

 

1、編寫接口

List<Blog> queryBlogForeach(Map map);

2、編寫SQL語句

<select id="queryBlogForeach" parameterType="map" resultType="blog">
  select * from blog
   <where>
       <!--
       collection:指定輸入對象中的集合屬性
       item:每次遍歷生成的對象
       open:開始遍歷時的拼接字符串
       close:結束時拼接的字符串
       separator:遍歷對象之間需要拼接的字符串
       select * from blog where 1=1 and (id=1 or id=2 or id=3)
     -->
       <foreach collection="ids"  item="id" open="and (" close=")" separator="or">
          id=#{id}
       </foreach>
   </where>
</select>

3、測試

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

        HashMap map = new HashMap();
        List<Integer> ids = new ArrayList<Integer>();
        ids.add(1);
        ids.add(2);
        ids.add(3);
        map.put("ids",ids);

        List<Blog> blogs = mapper.queryBlogForeach(map);

        System.out.println(blogs);

        sqlSession.close();
    }

小結:

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

建議:

  現在Mysql中寫出完整的SQL,再對應的去修改成為我們的動態SQL實現通用即可!

其實動態 sql 語句的編寫往往就是一個拼接的問題,為了保證拼接准確,我們最好首先要寫原生的 sql 語句出來,然后在通過 mybatis 動態sql 對照着改,防止出錯。多在實踐中使用才是熟練掌握它的技巧。

動態SQL在開發中大量的使用,一定要熟練掌握!

13、緩存

13.1簡介

1、什么是緩存 [ Cache ]?

存在內存中的臨時數據。

將用戶經常查詢的數據放在緩存(內存)中,用戶去查詢數據就不用從磁盤上(關系型數據庫數據文件)查詢,從緩存中查詢,從而提高查詢效率,解決了高並發系統的性能問題。

2、為什么使用緩存?

減少和數據庫的交互次數,減少系統開銷,提高系統效率。
3、什么樣的數據能使用緩存?

經常查詢並且不經常改變的數據。【可以使用緩存】

 

13.2 Mybatis緩存

Mybatis緩存

MyBatis包含一個非常強大的查詢緩存特性,它可以非常方便地定制和配置緩存。緩存可以極大的提升查詢效率。

MyBatis系統中默認定義了兩級緩存:一級緩存和二級緩存

默認情況下,只有一級緩存開啟。(SqlSession級別的緩存,也稱為本地緩存)

二級緩存需要手動開啟和配置,他是基於namespace級別的緩存。

為了提高擴展性,MyBatis定義了緩存接口Cache。我們可以通過實現Cache接口來自定義二級緩存

 

13.3 一級緩存

一級緩存

一級緩存也叫本地緩存:

與數據庫同一次會話期間查詢到的數據會放在本地緩存中。

以后如果需要獲取相同的數據,直接從緩存中拿,沒必須再去查詢數據庫;

 

13.4 測試

測試

1、在mybatis中加入日志,方便測試結果

<settings>
    <!--標准的日志2廠實現-->
    <setting nane="logImp1" value="STDOUT_LOGGING"/>
</settings>

2、編寫接口方法

//根據id查詢用戶
User queryUserById(@Param("id") int id);

3、接口對應的Mapper文件

<select id="queryUserById" resultType="user">
  select * from user where id = #{id}
</select>

映射mybatis-config.xml

    <!--每一個Mapper.xml都需要在Mybatis核心配置文件中注冊-->
    <!--綁定接口-->
    <mappers>
        <!--<mapper resource="com/tongda/dao/*Mapper.xml"/>-->
        <!--<mapper class="com.tongda.dao.BlogMapperMapper"/>-->
        <!--<mapper class="com.tongda.dao.StudentMapper"/>-->
        <!--<mapper class="com.tongda.dao.BlogMapper"/>-->
        <!--<mapper resource="com/tongda/dao/BlogMapper.xml"/>-->
        <!--<package name="com.tongda.dao"/>-->
        <mapper class="com.tongda.dao.UserMapper"/>
    </mappers>

4、測試

public class Mytest {

    @Test
    public void test1(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);

        User user = mapper.queryUserById(1);
        System.out.println(user);

        User user2 = mapper.queryUserById(1);
        System.out.println(user2);
        System.out.println(user==user2);


        sqlSession.close();
    }
}

 

5、結果分析

13.5 一級緩存失效的四種情況

一級緩存失效的四種情況

1.查詢不同的東西
2.增刪改操作,可能會改變原來的數據,所以必定會刷新緩存!
3.查詢不同的Mapper.xml
4.手動清理緩存!

一級緩存是SqlSession級別的緩存,是一直開啟的,我們關閉不了它;

**一級緩存失效情況:**沒有使用到當前的一級緩存,效果就是,還需要再向數據庫中發起一次查詢請求!

1、sqlSession不同 

@Test
public void testQueryUserById(){
   SqlSession session = MybatisUtils.getSession();
   SqlSession session2 = MybatisUtils.getSession();
   UserMapper mapper = session.getMapper(UserMapper.class);
   UserMapper mapper2 = session2.getMapper(UserMapper.class);

   User user = mapper.queryUserById(1);
   System.out.println(user);
   User user2 = mapper2.queryUserById(1);
   System.out.println(user2);
   System.out.println(user==user2);

   session.close();
   session2.close();
}

觀察結果:發現發送了兩條SQL語句!

結論:每個sqlSession中的緩存相互獨立

2、sqlSession相同,查詢條件不同

@Test
public void testQueryUserById(){
   SqlSession session = MybatisUtils.getSession();
   UserMapper mapper = session.getMapper(UserMapper.class);
   UserMapper mapper2 = session.getMapper(UserMapper.class);

   User user = mapper.queryUserById(1);
   System.out.println(user);
   User user2 = mapper2.queryUserById(2);
   System.out.println(user2);
   System.out.println(user==user2);

   session.close();
}

觀察結果:發現發送了兩條SQL語句!很正常的理解

結論:當前緩存中,不存在這個數據

3、sqlSession相同,兩次查詢之間執行了增刪改操作!

增加方法 

//修改用戶
int updateUser(Map map);

編寫SQL

<update id="updateUser" parameterType="map">
  update user set name = #{name} where id = #{id}
</update>

測試 

@Test
public void testQueryUserById(){
   SqlSession session = MybatisUtils.getSession();
   UserMapper mapper = session.getMapper(UserMapper.class);

   User user = mapper.queryUserById(1);
   System.out.println(user);

   HashMap map = new HashMap();
   map.put("name","kuangshen");
   map.put("id",4);
   mapper.updateUser(map);

   User user2 = mapper.queryUserById(1);
   System.out.println(user2);

   System.out.println(user==user2);

   session.close();
}

觀察結果:查詢在中間執行了增刪改操作后,重新執行了

結論:因為增刪改操作可能會對當前數據產生影響

4、sqlSession相同,手動清除一級緩存 

@Test
public void testQueryUserById(){
   SqlSession session = MybatisUtils.getSession();
   UserMapper mapper = session.getMapper(UserMapper.class);

   User user = mapper.queryUserById(1);
   System.out.println(user);

   session.clearCache();//手動清除緩存

   User user2 = mapper.queryUserById(1);
   System.out.println(user2);

   System.out.println(user==user2);

   session.close();
}

小結:一級緩存默認是開啟的,只在一次SqlSession中有效,也就是拿到連接到關閉連接這個區間段!

一級緩存就是一個map

13.6 二級緩存

二級緩存:在SQL文件中添加

<mapper namespace="com.tongda.dao.UserMapper">
    <!--開啟二級緩存-->
    <cache/>

二級緩存也叫全局緩存,一級緩存作用域太低了,所以誕生了二級緩存

基於namespace級別的緩存,一個名稱空間,對應一個二級緩存;

工作機制

一個會話查詢一條數據,這個數據就會被放在當前會話的一級緩存中;

如果當前會話關閉了,這個會話對應的一級緩存就沒了;但是我們想要的是,會話關閉了,一級緩存中的數據被保存到二級緩存中;

新的會話查詢信息,就可以從二級緩存中獲取內容;

不同的mapper查出的數據會放在自己對應的緩存(map)中;

使用步驟

1、開啟全局緩存 【mybatis-config.xml】

<!--cacheEnabled該配置影響的所有映射器中配置的緩存的全局開關。true,false    默認true-->
        <setting name="cacheEnabled" value="true"/>

2、去每個mapper.xml中配置使用二級緩存,這個配置非常簡單;【xxxMapper.xml

<!--在當前xml中使用二級緩存-->
    <cache/>

官方示例=====>查看官方文檔

<cache
 eviction="FIFO"
 flushInterval="60000"
 size="512"
 readOnly="true"/>

這個更高級的配置創建了一個 FIFO 緩存,每隔 60 秒刷新,最多可以存儲結果對象或列表的 512 個引用,而且返回的對象被認為是只讀的,因此對它們進行修改可能會在不同線程中的調用者產生沖突。

3、代碼測試

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

Caused by:java.io.NotSerializableException:com.kuang.pojo.User
  • 所有的實體類先實現序列化 implements Serializable 接口
@Data
public class User implements Serializable {
    private int id;
    private String name;
    private String pwd;
}
  • 測試代碼
@Test
public void testQueryUserById(){
   SqlSession session = MybatisUtils.getSession();
   SqlSession session2 = MybatisUtils.getSession();

   UserMapper mapper = session.getMapper(UserMapper.class);
   UserMapper mapper2 = session2.getMapper(UserMapper.class);

   User user = mapper.queryUserById(1);
   System.out.println(user);
   session.close();

   User user2 = mapper2.queryUserById(1);
   System.out.println(user2);
   System.out.println(user==user2);

   session2.close();
}

結論

  • 只要開啟了二級緩存,我們在同一個Mapper中的查詢,可以在二級緩存中拿到數據

  • 查出的數據都會被默認先放在一級緩存中

  • 只有會話提交或者關閉以后,一級緩存中的數據才會轉到二級緩存中

 

13.7 緩存原理圖

 

 

13.10 EhCache

EhCache

在這里插入圖片描述

第三方緩存實現–EhCache: 查看百度百科

Ehcache是一種廣泛使用的java分布式緩存,用於通用緩存;

要在應用程序中使用Ehcache,需要引入依賴的jar包 

<!-- https://mvnrepository.com/artifact/org.mybatis.caches/mybatis-ehcache -->
<dependency>
   <groupId>org.mybatis.caches</groupId>
   <artifactId>mybatis-ehcache</artifactId>
   <version>1.1.0</version>
</dependency>

 在UserMapper.xml中使用對應的緩存即可

<mapper namespace = “org.acme.FooMapper” > 
   <cache type = “org.mybatis.caches.ehcache.EhcacheCache” /> 
</mapper>

編寫ehcache.xml文件,如果在加載時未找到/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
           eternal="false"
           maxElementsInMemory="10000"
           overflowToDisk="false"
           diskPersistent="false"
           timeToIdleSeconds="1800"
           timeToLiveSeconds="259200"
           memoryStoreEvictionPolicy="LRU"/>

   <cache
           name="cloud_user"
           eternal="false"
           maxElementsInMemory="5000"
           overflowToDisk="false"
           diskPersistent="false"
           timeToIdleSeconds="1800"
           timeToLiveSeconds="1800"
           memoryStoreEvictionPolicy="LRU"/>
   <!--
      defaultCache:默認緩存策略,當ehcache找不到定義的緩存時,則使用這個緩存策略。只能定義一個。
    -->
   <!--
     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,最近最少使用的,緩存的元素有一個時間戳,當緩存容量滿了,而又需要騰出地方來緩存新的元素的時候,那么現有緩存元素中時間戳離當前時間最遠的元素將被清出緩存。
  -->

</ehcache>

合理的使用緩存,可以讓我們程序的性能大大提升!

14、Mybatis總結

 

 

 

 

 


免責聲明!

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



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