Mybatis筆記(超長,完結)


image-20200531213054908.png

學習資源來自於嗶哩嗶哩UP遇見狂神說,一個寶藏UP大家快去關注吧!記得三連加分享,不要做白嫖黨.

Mybatis-9.28

環境:

  • JDK1.8
  • Mysql 5.7
  • maven 3.6.1
  • IDEA

回顧:

  • JDBC
  • Mysql
  • Java基礎
  • Maven
  • Junit

SSM框架: 配置文件. 最好的方式: 看官網文檔.Mybatis中文文檔

1.簡介

1.1什么是Mybatis

  • MyBatis 是一款優秀的持久層(Dao層)框架

  • 它支持自定義 SQL、存儲過程以及高級映射。

  • MyBatis 免除了幾乎所有的 JDBC 代碼以及設置參數和獲取結果集的工作。

  • MyBatis 可以通過簡單的 XML 或注解來配置和映射原始類型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 對象)為數據庫中的記錄。

  • 原名iBatis,所以很多包的名字叫做iBatis

  • 2013年11月遷移到Github

如何獲得Mybatis?

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

1.2 持久化

數據持久化

  • 持久化就是將程序的數據在持久狀態和瞬時狀態轉化過程
  • 內存: 斷電即失
  • 數據庫(jdbc),io文件持久化
  • 生活: 冷藏,罐頭

為什么需要持久化?

  • 有一些對象不能丟掉.

  • 內存太貴了

1.3 持久化

Dao層,Service層,Controller層...

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

1.4 為什么需要Mybatis?

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

  • 方便

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

  • 不用Mybatis也可以,更容易上手.技術沒有高低之分

  • 優點:(百度百科)

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

最重要的一點: 使用的人多!

2. 第一個Mybatis程序

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

2.1 搭建環境

1.搭建數據庫

CREATE DATABASE `mybatis`;

USE `mybatis`;

CREATE TABLE `user`(
`id` INT(20) NOT NULL,
`name`VARCHAR(20) NOT NULL DEFAULT 0 COMMENT '名字',
`pwd`VARCHAR(20) NOT NULL DEFAULT 123456 COMMENT '密碼',
PRIMARY KEY (`id`)
)ENGINE = INNODB DEFAULT CHARSET = utf8;

INSERT INTO `user`(`id`,`name`,`pwd`) VALUES
(1,'張三','4354453'),
(2,'李四','434534453'),
(3,'王五','4545343')

2.新建項目

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

  2. 刪掉src目錄

  3. 導入maven依賴

    <!--導入依賴-->
    <dependencies>
        <!--mysql驅動-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.47</version>
        </dependency>
        <!--mybatis-->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.5.2</version>
        </dependency>
        <!--junnit-->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>
    </dependencies>
    

2.2創建一個模塊

2

  • 編寫mybatis的核心配置文件
    3

    <?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.jdbc.Driver"/>
                    <property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=true&amp;useUnicode=true&amp;characterEncoding=UTF-8"/>
                    <property name="username" value="root"/>
                    <property name="password" value="123456"/>
                </dataSource>
            </environment>
        </environments>
    
    </configuration>
    
  • 編寫mybatis工具類

    //sqlSessionFactory --> sqlSession
    public class MybatisUtils {
    
        private static SqlSessionFactory sqlSessionFactory;
    
        static {
            try {
                //使用Mybatis第一步,獲取sqlSessionFactory對象
                String resource = "mybatis-config.xml";
                InputStream inputStream = Resources.getResourceAsStream(resource);
                sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    
        //既然有了 SqlSessionFactory,顧名思義,我們可以從中獲得 SqlSession 的實例。
        // SqlSession 提供了在數據庫執行 SQL 命令所需的所有方法。
    
        public static SqlSession sqlSession() {
            return sqlSessionFactory.openSession();
        }
    }
    

2.3 編寫代碼

  • 實體類(pojo)

    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接口

    //以后取名會用Mapper,這里先用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接口-->
    <mapper namespace="com.tan.dao.UserDao">
    
        <select id="getUserList" resultType="com.tan.pojo.User">
            select * from mybatis.user
        </select>
    </mapper>
    

2.4 測試

2.4.1 junit測試,第一種方法getMapper

public class UserDaoTest {

    @Test
    public void test() {

        //第一步: 獲得SqlSession對象
        SqlSession sqlSession = MybatisUtils.sqlSession();

        //方式一: getMapper
        UserDao userDao = sqlSession.getMapper(UserDao.class);
        List<User> userList = userDao.getUserList();

        for (User user : userList) {
            System.out.println(user);
        }

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

結果: 報錯

運行錯誤,報錯糾錯

注意點:

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

MapperRegistry(映射注冊表)是什么?

4

每一個Mapper.xml都需要在Mybatis核心配置文件中注冊!(注意這個錯誤的路徑寫法,這樣寫有問題,被這個問題坑了好久)

  • maven過濾設置

配置了Mappers之后仍然出錯

5

從圖中可以看出maven的生成文件中,dao包下的UserMapper.xml並沒有生成,因為maven約定只會生成在resources下的.xml的配置文件,而UserMapper.xml在java代碼包下.所以需要配置一下maven的資源過濾

pom.xml文件中添加一下代碼:

<build>
    <resources>
        <resource>
            <directory>src/main/java</directory>
            <includes>
                <include>**/*.properties</include>
                <include>**/*.xml</include>
            </includes>
            <filtering>false</filtering>
        </resource>
        <resource>
            <directory>src/main/resources</directory>
            <includes>
                <include>**/*.properties</include>
                <include>**/*.xml</include>
            </includes>
            <filtering>false</filtering>
        </resource>
    </resources>
</build>

寫到這里我仍然出錯,而且仍然是初始化錯誤,和沒設置maven過濾的報錯一樣,找了半天排除了所有的常規錯誤.

6

問題解決: mybatis-config.xml的mappers配置中

之前是這樣寫的: 7

因為IDEA自動提示路徑,所以我一直沒有懷疑這里會有問題.

改成路徑的格式后就正常了

8

test運行結果:

9

2.4.2 junit測試,第二種方法,直接執行sqlSession的方法

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

2.4.3 兩種方法的區別

首先,不建議使用方法二

官網解釋:

10

3. 總結

主要的7個步驟:

11

3. CRUD

1. namespace

namespace中的包名要和mapper接口的包名一致!

2. select

選擇,查詢語句;

  • id: 對應namespace中的方法名;
  • resultType: sql語句執行的返回值.
  • parameterType: 參數類型.
  1. 編寫接口

    java//查詢全部用戶
    List<User> getUserList();
    
  2. 編寫對應的mapper中的

    <!--select查詢語句-->
        <select id="getUserList" resultType="com.tan.pojo.User">
        select *
        from mybatis.user
        </select>
    
  3. 測試

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

3. insert

<!--對象中的屬性可以直接取出來-->
<insert id="addUser" parameterType="com.tan.pojo.User">
    insert into mybatis.user(id, name, pwd)
    values (#{id}, #{name}, #{pwd});
</insert>

4. Update

<update id="updateUser" parameterType="com.tan.pojo.User">
    update mybatis.user
    set name=(#{name}),
        pwd=(#{pwd})
    where id = (#{id});
</update>

5. Delete

<delete id="deleteUser" parameterType="com.tan.pojo.User">
    delete
    from mybatis.user
    where id = (#{});
</delete>

Test

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

    int res = mapper.addUser(new User(7, "asdawd", "afefew"));
    if (res > 0) {
        System.out.println("插入成功");
    }

    //提交事務
    sqlSession.commit();
    sqlSession.close();
}

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

    mapper.updateUser(new User(5,"hello","135468"));

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

注意點:

  • 增刪改需要提交事務

運行錯誤,報錯糾錯

在寫的時候突然報了一個錯誤:

java.lang.ExceptionInInitializerError
	at com.tan.dao.UserMapperTest.deleteUser(UserMapperTest.java:77)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.base/java.lang.reflect.Method.invoke(Method.java:564)
	at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
	at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
	at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
	at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
	at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
	at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
	at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
	at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
	at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
	at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
	at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
	at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
	at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
	at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:33)
	at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:230)
	at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:58)
Caused by: org.apache.ibatis.exceptions.PersistenceException: 
### Error building SqlSession.
### The error may exist in com/tan/dao/UserMapper.xml
### Cause: org.apache.ibatis.builder.BuilderException: Error parsing SQL Mapper Configuration. Cause: org.apache.ibatis.builder.BuilderException: Error parsing Mapper XML. The XML location is 'com/tan/dao/UserMapper.xml'. Cause: org.apache.ibatis.builder.BuilderException: Parsing error was found in mapping #{}.  Check syntax #{property|(expression), var1=value1, var2=value2, ...} 
	at org.apache.ibatis.exceptions.ExceptionFactory.wrapException(ExceptionFactory.java:30)
	at org.apache.ibatis.session.SqlSessionFactoryBuilder.build(SqlSessionFactoryBuilder.java:80)
	at org.apache.ibatis.session.SqlSessionFactoryBuilder.build(SqlSessionFactoryBuilder.java:64)
	at com.tan.utils.MybatisUtils.<clinit>(MybatisUtils.java:26)
	... 23 more
Caused by: org.apache.ibatis.builder.BuilderException: Error parsing SQL Mapper Configuration. Cause: org.apache.ibatis.builder.BuilderException: Error parsing Mapper XML. The XML location is 'com/tan/dao/UserMapper.xml'. Cause: org.apache.ibatis.builder.BuilderException: Parsing error was found in mapping #{}.  Check syntax #{property|(expression), var1=value1, var2=value2, ...} 
	at org.apache.ibatis.builder.xml.XMLConfigBuilder.parseConfiguration(XMLConfigBuilder.java:121)
	at org.apache.ibatis.builder.xml.XMLConfigBuilder.parse(XMLConfigBuilder.java:98)
	at org.apache.ibatis.session.SqlSessionFactoryBuilder.build(SqlSessionFactoryBuilder.java:78)
	... 25 more
Caused by: org.apache.ibatis.builder.BuilderException: Error parsing Mapper XML. The XML location is 'com/tan/dao/UserMapper.xml'. Cause: org.apache.ibatis.builder.BuilderException: Parsing error was found in mapping #{}.  Check syntax #{property|(expression), var1=value1, var2=value2, ...} 
	at org.apache.ibatis.builder.xml.XMLMapperBuilder.configurationElement(XMLMapperBuilder.java:122)
	at org.apache.ibatis.builder.xml.XMLMapperBuilder.parse(XMLMapperBuilder.java:94)
	at org.apache.ibatis.builder.xml.XMLConfigBuilder.mapperElement(XMLConfigBuilder.java:374)
	at org.apache.ibatis.builder.xml.XMLConfigBuilder.parseConfiguration(XMLConfigBuilder.java:119)
	... 27 more
Caused by: org.apache.ibatis.builder.BuilderException: Parsing error was found in mapping #{}.  Check syntax #{property|(expression), var1=value1, var2=value2, ...} 
	at org.apache.ibatis.builder.SqlSourceBuilder$ParameterMappingTokenHandler.parseParameterMapping(SqlSourceBuilder.java:132)
	at org.apache.ibatis.builder.SqlSourceBuilder$ParameterMappingTokenHandler.buildParameterMapping(SqlSourceBuilder.java:72)
	at org.apache.ibatis.builder.SqlSourceBuilder$ParameterMappingTokenHandler.handleToken(SqlSourceBuilder.java:67)
	at org.apache.ibatis.parsing.GenericTokenParser.parse(GenericTokenParser.java:77)
	at org.apache.ibatis.builder.SqlSourceBuilder.parse(SqlSourceBuilder.java:45)
	at org.apache.ibatis.scripting.defaults.RawSqlSource.<init>(RawSqlSource.java:46)
	at org.apache.ibatis.scripting.defaults.RawSqlSource.<init>(RawSqlSource.java:40)
	at org.apache.ibatis.scripting.xmltags.XMLScriptBuilder.parseScriptNode(XMLScriptBuilder.java:72)
	at org.apache.ibatis.scripting.xmltags.XMLLanguageDriver.createSqlSource(XMLLanguageDriver.java:44)
	at org.apache.ibatis.builder.xml.XMLStatementBuilder.parseStatementNode(XMLStatementBuilder.java:96)
	at org.apache.ibatis.builder.xml.XMLMapperBuilder.buildStatementFromContext(XMLMapperBuilder.java:137)
	at org.apache.ibatis.builder.xml.XMLMapperBuilder.buildStatementFromContext(XMLMapperBuilder.java:130)
	at org.apache.ibatis.builder.xml.XMLMapperBuilder.configurationElement(XMLMapperBuilder.java:120)
	... 30 more
Caused by: java.lang.StringIndexOutOfBoundsException: String index out of range: 0
	at java.base/java.lang.StringLatin1.charAt(StringLatin1.java:48)
	at java.base/java.lang.String.charAt(String.java:711)
	at org.apache.ibatis.builder.ParameterExpression.parse(ParameterExpression.java:44)
	at org.apache.ibatis.builder.ParameterExpression.<init>(ParameterExpression.java:39)
	at org.apache.ibatis.builder.SqlSourceBuilder$ParameterMappingTokenHandler.parseParameterMapping(SqlSourceBuilder.java:128)
	... 42 more


Process finished with exit code -1

這問題很奇怪的地方在於之前之前一直沒有出錯,突然就運行失敗,而且其他運行成功的test也沒有辦法再運行了.

剛開始搜的錯誤是第一個java.lang.ExceptionInInitializerError,但是這個錯誤太大了,沒有搜到答案,於是又搜索了最后一個錯誤java.lang.StringIndexOutOfBoundsException: String index out of range: 0,下標從0開始就越界,說明可能是數據庫出了問題,但是檢查一番發現數據庫沒有錯誤,可以運行,於是看了中間的錯誤Parsing error was found in mapping #{}. ,可能是mapper.xml中的 #{}出了問題,...

里面少了id,補上where id = (#{id});,全部都運行正確.

這么一個小錯誤,居然可以讓整個程序都無法運行......

6. 用Map更新特定字段

使用update更新有一個問題,就是更新的是整個User對象,這樣就必須將這個對象所有的字段都寫出來,但如果我們只更新其中一兩個字段怎么辦?使用Map就不用與表對象字段一一對應.

使用Map來更新:

接口層:

//用Map插入對象
int Map_updateUser(Map<String, Object> map);

Mapper.xml:

<!--Map更新-->
    <update id="Map_updateUser" parameterType="com.tan.pojo.User">
    update mybatis.user
    set name=(#{username})
    where id = (#{userid});
</update>

這里只更新了name,沒有更新password,如果用User對象來更新就必須要寫出更新后password的值

Test:

//Map更新
@Test
public void Map_updateUser() {
    SqlSession sqlSession = MybatisUtils.getSqlSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);

    Map<String, Object> map = new HashMap<String, Object>();
    map.put("userid", 6);
    map.put("username", "map更新");

    mapper.Map_updateUser(map);

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

7. 模糊查詢

由於mybatis會在sql語句兩邊添加單引號 `` 所以要用 雙引號包裹"%", 這樣可以進行模糊查詢,但是idea會提示sql語句錯誤.

<select id="queryBookByName" resultType="Books">
    select *
    from ssmbuild.books
    where bookName like "%"#{bookName}"%"
</select>

所以mybatis提供了CONCAT關鍵字 : 這樣就不會提示錯誤

<select id="queryBookByName" resultType="Books">
    select *
    from ssmbuild.books
    where bookName like CONCAT(CONCAT('%',#{userName},'%'))
</select>

4. 配置解析

1. 核心配置文件

  • mybatis-config.xml

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

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

2. 環境配置(environments)

MyBatis可以配置多種環境

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

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

Mybatis默認的事務管理器JDBC , 連接池: POOLED

3. 屬性(properties)

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

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

1.編寫一個配置文件

db.properties

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

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

配置文件規定的順序:

1

<!--引入外部配置文件-->
<properties resource="db.properties"/>

還可以這樣寫:

<!--引入外部配置文件-->
<properties resource="db.properties">
    <property name="username" value="root"/>
    <property name="password" value="123456"/>
</properties>
  • 可以直接引入外部文件
  • 可以在其中增加一些屬性配置
  • 這樣寫如果內部和外部不一樣,會先讀取內部再讀取外部,然后外部會覆蓋內部的property

4.類型別名(typeAliases)

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

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

掃描實體類的包,它的默認別名就為這個類的 類名的小寫.

<typeAliases>
    <package name="com.tan.pojo"/>
</typeAliases>
<!--pojo下的實體類為User,則縮寫為user(大寫也可以)-->

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

在實體類十分多,建議使用第二種,若實體類上有注解則使用注解名.

@Alias("自定義別名")
public class User {
}

5. 設置(沒必要記)

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

cacheEnabled 全局性地開啟或關閉所有映射器配置文件中已配置的任何緩存。
lazyLoadingEnabled 延遲加載的全局開關。當開啟時,所有關聯對象都會延遲加載。 特定關聯關系中可通過設置 fetchType 屬性來覆蓋該項的開關狀態。
logImpl 指定 MyBatis 所用日志的具體實現,未指定時將自動查找。
mapUnderscoreToCamelCase 是否開啟駝峰命名自動映射,即從經典數據庫列名 A_COLUMN 映射到經典 Java 屬性名 aColumn。

所有配置

6. 其他配置

  • plugins插件
    • mybatis-generator-core
    • mybatis-plus
    • 通用mapper

7.映射器(mappers)

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

方式一 :

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

方式二:使用class文件綁定注冊(不建議使用很容易出錯)

方式三: 使用包掃描注入綁定(不建議使用)

方式二和三的注意點:

  • 接口和Mapper配置文件必須同名
  • 接口和Mapper配置文件必須在同一個包下

8. 生命周期和作用域

(不知道為什么官網這部分沒有了)

image-20200606233013664

生命周期,和作用域,是至關重要的,因為錯誤地使用回到是非常嚴重的並發問題.

SqlSessionFactoryBuilder :

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

SqlSessionFactory :

  • 說白了就是可以想象為 : 數據庫連接池
  • 一旦創建就應該在運行期間一直存在,沒有任何理由丟棄它或重新創建一個實例
  • 應用作用域
  • 最簡單的就是使用單例模式或者靜態單例模式 , 保證全局只有一份變量

**SqlSession : **

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

image-20200606233946045

這里面的每一個Mapper , 就代表一個具體的業務 ! (增刪改查等)

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

1. 問題

數據庫中的字段

image-20200606234220890

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

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

輸出: User{id=1, name='張三', password='null'}
//  select * from mybatis.user where id = #{id}
//   類型處理器
//  select id,name,pwd from mybatis.user where id = #{id}

解決方法:

  • sql語句起別名

select id,name,pwd as password from mybatis.user where id = #{id}

2. ResultMap

結果集映射

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

<select id="getUserById" resultMap="UserMap">
    select *
    from mybatis.user
    where id = #{id}
</select>
  • resultMap 元素是 MyBatis 中最重要最強大的元素
  • ResultMap 的設計思想是,對簡單的語句做到零配置,對於復雜一點的語句,只需要描述語句之間的關系就行了
  • ResultMap 的優秀之處——你完全可以不用顯式地配置它們
  • 如果這個世界總是這么簡單就好了

image-20200607002512887

6. 日志

6.1 日志工廠

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

曾經: sout , debug

現在: 日志工廠.

logImpl指定 MyBatis 所用日志的具體實現,未指定時將自動查找。

  • SLF4J
  • LOG4J [掌握]
  • LOG4J2
  • JDK_LOGGING
  • COMMONS_LOGGING
  • STDOUT_LOGGING [掌握 ]
  • NO_LOGGING

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

STDOUT_LOGGING : 標准日志輸出

  • 在mybatis核心配置文件中配置日志:
<settings>
    <setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
<!--大小寫一定要注意,不能有多余的空格-->

image-20200607083141359

6.2 Log4j

什么是Log4j ?

  • Log4j是Apache的一個開源項目,通過使用Log4j,我們可以控制日志信息輸送的目的地是控制台)、文件、組件
  • 可以控制每一條日志的輸出格式
  • 通過定義每一條日志信息的級別,我們能夠更加細致地控制日志的生成過程
  • 最令人感興趣的就是,這些可以通過一個配置文件來靈活地進行配置,而不需要修改應用的代碼
  1. 先導包

    <!-- https://mvnrepository.com/artifact/log4j/log4j -->
    <dependency>
        <groupId>log4j</groupId>
        <artifactId>log4j</artifactId>
        <version>1.2.17</version>
    </dependency>
    
  2. 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/mylog.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
    
  3. 配置log4j為日志的實現

    <settings>
        <setting name="logImpl" value="LOG4J"/>
    </settings>
    
  4. Log4j的使用
    image-20200607085358640

簡單使用

  1. 在要使用Log4j的類中,導入import org.apache.log4j.Logger;,注意jdk中有一個自帶的Logger包,不要導錯了.

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

    static Logger logger = Logger.getLogger(UserMapperTest.class);
    
  3. 日志級別

    @Test
    public void testLog4j() {
        logger.info("info: 進入了testLog4j");
        logger.debug("debug: 進入了testLog4j");
        logger.error("error: 進入了testLog4j");
    }
    

7. 分頁

**思考 **:為什么要分頁?

  • 減少數據處理量

7.1 使用Limit分頁

語法: select * from user limit startIndex,pageSize;
select *from user limit 3; #[0,n)

使用Mybatis實現分頁,核心SQL

  1. 接口

    //分頁
    List<User> getUserByLimit(Map<String, Integer> map);
    
  2. Mapper.xml

    <!--分頁-->
    <select id="getUserByLimit" parameterType="map" resultMap="UserMap">
        select * from mybatis.user limit #{startIndex},#{pageSize}
    </select>
    
  3. 測試

    @Test
    public void getUserByLimit() {
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    
        HashMap<String, Integer> map = new HashMap<String, Integer>();
        map.put("startIndex", 0);
        map.put("pageSize", 2);
    
        List<User> userList = mapper.getUserByLimit(map);
        for (User user : userList) {
            System.out.println(user);
        }
    
        sqlSession.close();
    }
    

用框架分頁真的簡單了好多😭,當初用servlet傳統分頁,傳五六個參數,sql語句和java語句混在一起,還要拼接sql語句,sql語句寫在String類型里面還沒有任何sql語句的提示,真的要吐了.

7.2 RowBounds分頁

通過java代碼層面實現分頁,強行面向對象,不建議使用,筆記就不做了(其實是我懶).

7.3 分頁插件

Mybatis PageHelper

看官方文檔就好了,本質都是limit分頁

了解即可,只需要知到是什么就好了

8. 使用注解開發

8.1 面向接口編程

為了解耦

關於接口的理解

  • 接口從更深層次的理解,應是定義(規范,約束)與實現(名實分離的原則)的分離。

  • 接口的本身反映了系統設計人員對系統的抽象理解。

  • 接口應有兩類:

    • 第一類是對一個個體的抽象,它可對應為一個抽象體(abstract class);
    • 第二類是對一個個體某一方面的抽象,即形成一個抽象面(interface);
  • 一個體有可能有多個抽象面。抽象體與抽象面是有區別的。

8.2 使用注解開發

  1. 接口

    @Select("select * from user")
    List<User> getUsers();
    
  2. mybatis-config.xml (接口第一次時的配置)

    <!--綁定接口-->
    <mappers>
        <mapper class="com.tan.dao.UserMapper"/>
    </mappers>
    
  3. @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();
    }
    

官方建議: 對於簡單的sql語句可以這么用就,但對於復雜的sql語句就顯得力不從心,所以復雜的sql語句還是建議使用xml配置來運行.

注解開發的實現方法: 通過反射獲取一個類中的所有信息,並幫我們自動化完成一些操作.

  • 本質: 反射機制實現
  • 底層: 動態代理 !
    image-20200607105844722

注解與反射的筆記

Mybatis詳細的執行流程

Mybatis詳細的執行流程

  1. SqlSessionFactoryBuilder通過構造器build調用build構造方法
    image-20200607165902625

  2. build構造方法調用XMLConfigBuilder這個類解析了(inputStream, environment, properties)
    image-20200607165934987

  3. 解析完后傳給Configuration這個對象(Configuration中包含了所有的配置內容)
    image-20200607170003230

  4. 之后SqlSessionFactory實例化,獲取sqlSession(事務在這一層去做,相當於原來的Connection)
    image-20200607170106493

  5. sqlSession里面有一個executor執行器,executor執行mapper,mapper通過反射加載出類的所有信息,包括了sqlSession,sqlSession中又有緩存executor執行器(executor把自己套進去了),套進去后開始執行sql語句,sql從配置文件中讀取.
    成功執行sql語句提交事務,失敗回滾.

    image-20200607171401782

對第5步做一些簡單的解釋: sqlSession中包括了運行一個sql語句所需的條件,包括事務,執行器,加載器等.但就是沒有sql語句,於是通過反射獲取到了接口以及接口下類的所有信息,就有了sql語句,mapper只是放這所有信息的變量 . 於是有條件又有語句就可以執行sql語句了.
image-20200607171822567

9. Lombok

Project Lombok is a java library that automatically plugs into your editor and build tools, spicing up your java.
Never write another getter or equals method again, with one annotation your class has a fully featured builder, Automate your logging variables, and much more.
  • java library
  • plugs
  • build tools
  • with one annotation your class

使用步驟:

  1. 在IDEA中安裝Lombok插件.

  2. 在項目中導包

    <!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>1.18.12</version>
    </dependency>
    
  3. 在實體類上加注解.
    用Lombok后的實體類:

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

建議: idea的自動生成已經很厲害了 , Lombok只是實現了一點點的簡化,卻要在編譯時通過操作抽象語法樹(AST)改變字節碼的生成 , 變相改變了java語法.這就很不爽了,如果不是公司要求用就盡量別用.

10. 多對一處理

  • 多個學生,對應一個老師
  • 對於學生這邊而言 , 關聯 ... 多個學生關聯一個老師[多對一]
  • 對於老師而言, 集合 ... 一個老師有很多學生 [一對多]

image-20200607184746531

SQL:

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. 導入Lombok

  2. 新建實體類Teacher , Student
    注意: 學生的實體類中有tid所以實體類實體類應這樣寫 :

    @Data
    public class Student {
        private int id;
        private String name;
    
        //學生需要關聯一個老師
        private Teacher teacher;
    }
    
  3. 建立Mapper接口

  4. 建立Mapper.xml

  5. 在核心文件中綁定注冊我們的Mapper接口或者文件

  6. 測試查詢是否能夠成功

按照查詢嵌套處理

<!--
思路:
    1. 查詢所有的學生信息
    2.根據查詢出來的學生的tid,尋找對應的老師
-->
<select id="getStudent" resultMap="StudentTeacher">
    select *
    from student;
</select>
<resultMap id="StudentTeacher" type="Student">
    <result property="id" column="id"/>
    <result property="name" column="name"/>
    <!--復雜的屬性,我們需要單獨處理,對象: association,集合: collection
    -->
    <association property="teacher" column="tid" javaType="Teacher" select="getTeacher"/>
</resultMap>
<select id="getTeacher" resultType="Teacher">
    select *
    from teacher
    where id = #{tid};
</select>

簡單解釋:

<association property="tetacher" column="tid" javaType="Teacher" select="getTeacher"/>

用Map將字段對應到屬性,resultMap中前面兩個idname可以直接對應,但是tid這個復雜的屬性沒辦法直接對應.因為tid指向的是teacher這個對象,於是用association將字段tid指向teacher對象.但是teacher是一個復雜類型,需要再給他一個類型Teacher.

這樣就將兩個表通過tid聯系了起來,再在student的association中再嵌套一個查詢select="getTeacher",這樣就完成了一對多(也就是 sql 聯表查詢).

  • 一對多的最終解決辦法在springboot中用 json 解決,這個能看懂即可.

按照結果嵌套處理

<!--按照結果嵌套處理-->
<select id="getStudent2" resultMap="StudentTeacher2">
    select s.id sid, s.name sname, t.name tname, t.id tid
    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 property="id" column="tid"/>
    </association>
</resultMap>

上面寫 完整sql 語句,下面用resultMap寫關聯,比第一種好理解一些.

11. 一對多處理

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

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

環境搭建

  1. 環境搭建,和剛才一樣
    實體類:

    @Data
    public class Teacher {
        private int id;
        private String name;
    
        //一個老師擁有多個學生
        private List<Student> students;
    }
    

按照結果嵌套處理:

<!--按結果查詢-->
<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>
<resultMap id="TeacherStudent" type="Teacher">
    <result property="id" column="tid"/>
    <result property="name" column="tname"/>
    <!--
    復雜的屬性,我們需要單獨處理,對象: association,集合: collection
    javaType="" 指定屬性的類型,
    但此處類型為 List<Student> ,使用ofType獲取
    -->
    <collection property="students" ofType="Student">
        <result property="id" column="sid"/>
        <result property="name" column="sname"/>
        <result property="tid" column="tid"/>
    </collection>
</resultMap>

小結

  1. 關聯 - association [多對一]
  2. 集合 - collection [一對多]
  3. javaType & ofType
    1. javaType 用來指定實體類中屬性的類型
    2. ofType 用來指定映射到List或者集合中的 pojo 類型 , 也就是泛型中的約束類型

注意點:

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

面試高頻 :

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

12. 動態SQL

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

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

使用動態 SQL 並非一件易事,但借助可用於任何 SQL 映射語句中的強大的動態 SQL 語言,MyBatis 顯著地提升了這一特性的易用性。

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

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

搭建環境

create table `blog`
(
    `id`          varchar(50)  not null comment '博客id',
    `title`       varchar(100) not null comment '博客標題',
    `author`      varchar(30)  not null comment '博客作者',
    `create_time` datetime     not null comment '創建時間',
    `views`       int(30)      not null comment '瀏覽量'
) engine = InnoDB
  default charset = utf8;

創建一個基礎工程

  1. 導包

  2. 編寫配置文件

  3. 編寫實體類

    @Data
    public class Blog {
        private String id;
        private String title;
        private String author;
        private Date createTime;
        private int views;
    }
    
  4. 編寫實體類對應的Mapper接口 和 Mapper.xml文件

if

接口Mapper

//IF 查詢博客
List<Blog> queryBlogIF(Map map);

Mapper.xml

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

select * from mybatis.blog where 1=1 and title = ? and author = ? ,

注意點:

  • 如果title和author只傳其中一個,則查詢其中一個
  • 如果兩個都傳則取交集
  • 如果都不傳則執行select * from mybatis.blog where 1=1 查詢所有
  • 如果不寫1=1則在title和author都不傳的時候where關鍵字和and關鍵字會連起來sql語法錯誤.

但是在sql語句中加上1=1很明顯不規范,於是mybatis中又添加了<where>標簽.來自動判斷和處理sql拼接時關鍵字會連起來的問題.

where與set

where標簽

接口Mapper

//Choose 查詢博客
List<Blog> queryBlogChoose(Map map);

Mapper.xml

<select id="queryBlogChoose" parameterType="map" resultType="blog">
    select * from mybatis.blog
    <where>
        <if test="title != null">
            and title = #{title} /*此處 and 可有可無*/
        </if>
        <if test="author != null">
            and author = #{author} /*此處 and 可有可無*/
        </if>
    </where>
</select>

where標簽官網解釋 :

where 元素只會在至少有一個子元素的條件返回SQL子句的情況下才會去插入"WHERE"子句,而且,若語句的開頭為"AND"或"OR",where元素也會將他們去除

也就是說拼接sql語句select * from mybatis.blog where 1=1 and title = ? and author = ? 時where標簽會自動判斷是保留還是刪除and .

set標簽

與where類似,更新表的時候set關鍵字需要考慮, 是否需要,set標簽提供了只能添加與刪除, 保證不會因為缺少或則多余的, 影響sql語句的執行

接口Mapper

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

Mapper.xml

<update id="updateBlog" parameterType="map">
    update mybatis.blog
    <set>
        <if test="title != null">
            title = #{title}, /*此處 , 可有可無*/
        </if>
        <if test="views != null">
            views = #{views}, /*此處 , 可有可無*/
        </if>
    </set>
    where id = #{id}
</update>

choose (when, otherwise)

接口Mapper

//Choose 查詢博客
List<Blog> queryBlogChoose(Map map);

Mapper.xml

<select id="queryBlogChoose" parameterType="map" resultType="blog">
    select * from mybatis.blog
    <where>
        <choose>
            <when test="title != null">
                 title = #{title}
            </when>
            <when test="author != null">
               and  author = #{author}
            </when>
            <otherwise>
                and 1=1
            </otherwise>
        </choose>
    </where>
</select>

choose (when, otherwise)類似於java中的switch ,case ,只會執行when 標簽中最先滿足條件的那個,如果都不滿足條件則執行otherwise 標簽中的內容.

trim(where,set)

/*等價於set標簽*/
<trim prefix="SET" suffixOverrides=",">
    ...
</trim>

/*等價於where標簽*/
<trim prefix="WHERE" prefixOverrides="AND | OR">
    ...
</trim>

trim標簽 就是自定義某關鍵詞的前綴后綴是否保留.

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

if

where , set , choose , when

SQL片段

抽取部分常用的sql語句,便於復用.

  1. 使用sql 標簽抽取公共部分

    <!--sql片段-->
    <sql id="choose-title-test">
        <choose>
            <when test="title != null">
                title = #{title}
            </when>
            <when test="author != null">
                and author = #{author}
            </when>
            <otherwise>
                and 1=1
            </otherwise>
        </choose>
    </sql>
    
  2. 在需要的地方使用include標簽引用即可

    <!--引用sql片段部分-->
    <select id="queryBlogChoose" parameterType="map" resultType="blog">
        select * from mybatis.blog
        <where>
            <include refid="choose-title-test">
    
            </include>
        </where>
    </select>
    

注意事項:

  • 最好基於單表來定義SQL片段.保證復用性
  • sql 片段中不要有where標簽,盡量只要有 if 判斷就好了

Foreach

sql 語句查詢多個 id :

select * from user where 1=1 and (id=1 or id=2 or id=3)

select * from user where 1=1 and id in (1,2,3)

select * from user where id < 4

官網圖片 :

image-20200610162339218

接口:

//查詢第1-2-3-4號記錄的博客
List<Blog> queryBlogForeach(Map map);

Mapper.xml : (這樣寫Mapper傳入的id在數據庫中找不到不要緊,但一定要傳不然會報錯 . 不傳id會報錯的問題,我試了好久都沒有解決...)

<!--select * from blog where id in (1, 2, 3, 4,5)-->
<select id="queryBlogForeach" parameterType="map" resultType="blog">
    select * from mybatis.blog
    <where>
        id in
        <foreach collection="ids" item="id" open="(" separator="," close=")">
            #{id}
        </foreach>
    </where>
</select>

測試類: (因為我自己寫寫錯了,沒寫出來,所以這里貼一下)

//foreach查詢
@Test
public void queryBlogForeach() {
    SqlSession sqlSession = MybatisUtils.getSqlSession();
    BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);

    HashMap map = new HashMap();
    ArrayList<Integer> list = new ArrayList<Integer>();
    list.add(1);
    list.add(2);
    list.add(3);
    list.add(4);

    map.put("ids", list);

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

    for (Blog blog : blogs) {
        System.out.println(blog);
    }

    sqlSession.close();
}

往Map中傳List集合.

小總結

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

建議:

  • 先在IDEA的mysql控制台中寫出完整的SQL,再對應地去修改成為我們的動態SQL實現通用即可 .

最后再附上官網對動態SQL的解釋:

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

13. 緩存(重要,但是掌握概念即可)

具體會學習redis緩存.

13.1 簡介

查詢  :  連接數據庫 , 耗資源
	一次查詢的結果, 給他一個可以直接取到的地方 --> 內存 : 緩存
我們再次查詢相同數據的時候,直接就走緩存 ,就不用走數據庫了
  1. 什么是緩存[Cache]?
    • 存在內存中的臨時數據。
    • 將用戶經常查詢的數據放在緩存(內存)中,用戶去查詢數據就不用從磁盤上(關系型數據庫數據文件)查詢,從緩存中查詢,從而提高查詢效率,解決了高並發系統的性能問題。
  2. 為什么使用緩存?
    • 減少和數據庫的交互次數,減少系統開銷,提高系統效率。
  3. 什么樣的數據能使用緩存?
    • 經常查詢並且不經常改變的數據。

13.2 Mybatis緩存

  • MyBatis包含一個非常強大的查詢緩存特性,它可以非常方便地定制和配置緩存。緩存可以極大的提升查詢效率。
  • MyBatis系統中默認定義了兩級緩存:一級緩存二級緩存
    • 默認情況下,只有一級緩存開啟。(SqlSession級別的緩存,也稱為本地緩存)
    • 二級緩存需要手動開啟和配置,他是基於namespace級別的緩存。(也就是一個接口或者一個Mapper)
    • 為了提高擴展性,MyBatis定義了緩存接口Cache。我們可以通過實現Cache接口來自定義二級緩存

13.3 一級緩存

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

測試步驟:

  1. 開啟日志

  2. 接口(略)

  3. Mapper (select *from user where id = #{id})

  4. Test類

    @Test
    public void queryUserById() {
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    
        User user = mapper.queryUserById(1);
        System.out.println(user);
    
        System.out.println("==============================");
    
        User user2 = mapper.queryUserById(1);
        System.out.println(user2);
    
        System.out.println(user == user2);
    
        sqlSession.close();
    }
    
    1. 查詢的時候當第二次查詢和第一次查詢相同時,直接從一級緩存從獲取 :
      image-20200610181746666

    2. 當兩次查詢不同id時 :

      image-20200610182126608

      很明顯第二次從數據庫中查詢,沒有緩存

緩存失效的情況 :

  1. 查詢不同的東西 .(上面那個情況)
  2. 增刪改操作,可能會改變原來的數據,所以必定會刷新緩存 .
  3. 查詢不同的Mapper.xml (二級緩存都不行)
  4. 手動清理緩存sqlSession.clearCache();

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

13.4 二級緩存

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

步驟:

  1. mybatis-config.xml中開啟全局緩存

    <settings>
        <!--顯式開始全局緩存-->
        <setting name="cacheEnabled" value="true"/>
    </settings>
    
  2. 在使用二級緩存的Mapper.xml中開啟

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

    也可以自定義參數 官網緩存參數介紹

    <!--在當前Mapper.xml中使用二級緩存-->
    <cache eviction="FIFO"
           flushInterval="60000"
           size="512"
           readOnly="true"/>
    
  3. 測試
    <( ̄ c ̄)y▂ξ
    沒什么好寫的,就是一級緩存(SqlSession)關閉的時候,會自動提升作用域,其他SqlSession會直接從Mapper中獲取已有的結果.

測試的時候報錯,提醒沒有序列化.

  • 開啟二級緩存 存入緩存需要序列化(深拷貝),保證查的是同一個實體類.
    pojo 實體類實現Serializable接口即可 :

    @Data
    public class User implements Serializable {
        private int id;
        private String name;
        private String pwd;
    }
    

小結 :

  • 只要開啟了二級緩存,在同一個Mapper下就有效 .
  • 所有的數據都會先放在一級緩存中;
  • 只有當會話提交,或者關閉的時候,才會提交到二級緩沖中 .

13.5 緩存原理

緩存圖

用戶 -> 二級緩存 -> 一級緩存 -> 數據庫

14 配張思維導圖

Cache_7679607583f7c38a.

完結撒花! 完結撒花! 完結撒花!

開始Spring之旅~ ~ ~


免責聲明!

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



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