1. 程式人生 > 其它 >nginx_php-fpm調優優化

nginx_php-fpm調優優化

Mybatis

環境

  • JDBC
  • java基礎
  • JDK 1.8
  • Mysql5.7
  • maven 3.6.1
  • 開發工具 idea
  • Junit

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

1 簡介

1.1什麼是mybatis?

  • MyBatis是一款優秀的持久層框架
  • 支援定製SQL、儲存過程以及高階對映
  • Mybatis避免了幾乎所有的JDBC程式碼和手動設定參樹,以及獲取結果集
  • Mybatis可以使用簡單的XML註解來配置和對映原生型別、介面和Java的POJO(Plain Old Java Objects,普通老式Java物件)為資料庫中的記錄
  • MyBatis本是apache的一個開源專案iBatis
  • 2010年這個專案由apache software foundation遷移到了google code,並且改名為MyBatis
  • 2013年11月遷移到Github

如何獲得Mybatis?

  • Maven倉庫:

    下載地址

      <!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->  <dependency>      <groupId>org.mybatis</groupId>      <artifactId>mybatis</artifactId>      <version>3.5.2</version>  </dependency>
    
  • Github:Mybatis地址

  • 中文文件地址

1.2 持久化

資料持久化

  • 持久化就是將程式的資料儲存在硬碟中
  • 記憶體特性:斷電會失去資訊
  • 資料庫(jdbc),io檔案持久化
  • 生活中的持久化:冷藏,寫……

為什麼需要持久化?

  • 我們需要將一些資料儲存下來,方便以後使用
  • 記憶體價格過高(製造成本高)

1.3 持久層

複習學習過的層:Dao,Service,Controller

持久層的工作

  • 完成持久化工作的程式碼塊
  • 層之間的接線非常明顯

1.4 為什麼需要Mybatis?

  • 幫助程式設計師將資料存入到資料庫中
  • 傳統的JDBC程式碼太複雜了
  • 簡化、框架、自動化
  • 不用Mybatis也能實現,Mybatis可以使程式設計師在不知道底層原理的情況下,完成網站後臺搭建
  • 優點:
    • 簡單易學
    • 靈活
    • 解除sql與程式程式碼的耦合
    • 提供對映標籤,支援物件與資料庫的orm欄位關係對映
    • 提供物件關係對映標籤,支援物件關係組建維護
    • 提供xml標籤,支援編寫動態sql。

2 第一個MyBatis程式

思路:搭建環境—>匯入MyBatis—>編寫程式碼—>測試

2.1 搭建環境

  • 搭建資料庫

       -- 建立資料庫   create database `mybatis`;   use mybatis;   -- 建立表   create table `user`(      `id` int(20) not null,      `name` varchar(30) default null,      `pwd` varchar(30) default null,      primary key(`id`)    )engine=InnoDB default charset=utf8mb4;   -- 插入資料   insert into `user`(`id`,`name`,`pwd`) values  (1,'千樹','123'),  (2,'張三','123'),  (3,'李飛','123');
    
  • mybatis官方文件:文件地址

  • 新建普通maven專案作為父專案,匯入sql驅動,mybatis,junit元件

      <!--匯入依賴-->  <dependencies>      <!--mysql驅動-->      <dependency>          <groupId>mysql</groupId>          <artifactId>mysql-connector-java</artifactId>          <version>5.1.46</version>      </dependency>      <!--Mybatis-->      <dependency>          <groupId>org.mybatis</groupId>          <artifactId>mybatis</artifactId>          <version>3.5.2</version>      </dependency>      <!--junit-->      <dependency>          <groupId>junit</groupId>          <artifactId>junit</artifactId>          <version>4.12</version>      </dependency>  </dependencies>
    
  • 新建新元件作為子級專案,普通maven的module

  • 新增配置檔案:

    在src->main->resources目錄下新建mybatis-config.xml檔案,把官方的配置程式碼複製貼上(不能在配置檔案中寫中文註釋)

      <?xml version="1.0" encoding="UTF-8" ?>  <!DOCTYPE configuration    PUBLIC "-//mybatis.org//DTD Config 3.0//EN"    "http://mybatis.org/dtd/mybatis-3-config.dtd">  <configuration>   
          <environments default="development">   
              <environment id="development">   
                  <transactionManager type="JDBC"/> 
                  <dataSource type="POOLED">         
                     <property name="driver" value="${driver}"/>//資料庫驅動,不同驅動可連線不同資料庫伺服器        
                      <property name="url" value="${url}"/>//連線資料庫的目錄    
                      <property name="username" value="${username}"/>//資料庫名字,預設root                         <property name="password" value="${password}"/>//資料庫密碼,自己的資料庫密碼,一般為root        </dataSource>   
              </environment> 
          </environments> 
    </configuration>
    

    配置檔案的作用就是連線資料庫

2.2 建立模組

  • 編寫mybatis工具類

      //SqlSessionFactory --生產--> SqlSession  public class MybatisUtils {      private static SqlSessionFactory sqlSessionFactory; //提升作用域      //獲取工廠,固定程式碼      static {          try {              String resource="mybatis-config.xml";              InputStream inputStream = Resources.getResourceAsStream(resource);              sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);          } catch (IOException e) {              e.printStackTrace();          }      }      //獲取sqlSession      //SqlSession完全包含了面向物件資料庫執行SQL命令所需的方法      public static SqlSession getSqlSession(){ 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();  }
    
  • 介面實現類改為以xxxMapper.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:名稱空間,繫結mapper/Dao介面-->  <mapper namespace="com.qian.dao.UserDao">  <!--id:介面的方法,resultType:介面的返回值型別-->      <select id="getUserList" resultType="com.qian.pojo.User">          select * from mybatis.user where id = #{id}      </select>  </mapper>
    

2.4 測試

  • 測試類

      public class UserDaoTest {      @Test      public void test(){          //獲取SqlSession物件          SqlSession sqlSession = MybatisUtils.getSqlSession();          //獲取mapper          UserDao mapper = sqlSession.getMapper(UserDao.class);          List<User> list = mapper.getUserList();          for (User u:list){              System.out.println(u);          }          //不推薦使用  /*      這種方式能夠正常工作,對使用舊版本 MyBatis 的使用者來說也比較熟悉。但現在有了一種更簡潔的方式——使用和指定語句的引數和返回值相匹配的介面(比如 BlogMapper.class),現在你的程式碼不僅更清晰,更加型別安全,還不用擔心可能出錯的字串字面值以及強制型別轉換。  */  //        List<User> list = sqlSession.selectList("com.qian.dao.UserDao.getUserList");  //        for (User user : list) {  //            System.out.println(user);  //        }          //關閉SqlSession          sqlSession.close();      }  }
    

    異常1:org.apache.ibatis.binding.BindingException: Type interface com.qian.dao.UserDao is not known to the MapperRegistry.

    解決方法:每一個Mapper.xml檔案都需要在src->main->resources目錄下新建mybatis-config.xml的核心配置檔案中註冊

    <mappers>  <mapper resource="com/qian/dao/UserMapper.xml"></mappers>
    

    異常2:
    Error building SqlSession.
    The error may exist in com/qian/dao/UserMapper.xml
    Cause: org.apache.ibatis.builder.BuilderException: Error parsing SQL Mapper Configuration.
    Cause: java.io.IOException: Could not find resource com/qian/dao/UserMapper.xml

    解決方法:

    <!-- 在maven中,約定大於配置,在pom中新增此檔案可以解決 --><build>  <resources>      <resource>          <directory>src/main/resources</directory>          <includes>              <include>**/*.properties</include>              <include>**/*.xml</include>          </includes>          <filtering>true</filtering>      </resource>      <resource>          <directory>src/main/java</directory>          <includes>              <include>**/*.properties</include>              <include>**/*.xml</include>          </includes>          <filtering>true</filtering>      </resource>  </resources></build>
    

    異常3:
    Error building SqlSession.
    The error may exist in com/qian/dao/UserMapper.xml
    Cause: org.apache.ibatis.builder.BuilderException: Error parsing SQL Mapper Configuration.
    Cause: org.apache.ibatis.builder.BuilderException: Error creating document instance. Cause: org.xml.sax.SAXParseException; lineNumber: 6;

    解決方法:

    <?xml version="1.0" encoding="UTF-8" ?>    <!-- 把mybatis-config.xml與mybatis-config.xml檔案的encoding修改成下面的 --><?xml version="1.0" encoding="UTF8" ?>
    

    另一種解決方法:刪除掉xxxMapper.xml檔案中所有的中文註釋

    異常4:
    Cause: com.mysql.jdbc.exceptions.jdbc4.CommunicationsException: Communications link failure

    解決方法:
    useSSL=true改為false(true也可以,需要在mysql中啟用SSL)

    <property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=false&amp;useUnicode=true&amp;characterEncoding=UTF8&amp;serverTimezone=GMT%2B8"/>
    

2.5 總結

public class UserDaoTest {    @Test    public void test(){        //獲取SqlSession物件        SqlSession sqlSession = MybatisUtils.getSqlSession();        try{            //獲取mapper            UserDao mapper = sqlSession.getMapper(UserDao.class);            List<User> list = mapper.getUserList();            for (User u:list){                System.out.println(u);            }            //不推薦使用//        List<User> list = sqlSession.selectList("com.qian.dao.UserDao.getUserList");//        for (User user : list) {//            System.out.println(user);//        }        }finally {            //關閉SqlSession            sqlSession.close();        }    }}

3 增刪改查實現

3.1 Mapper介面

public interface UserMapper {    //查詢全部使用者    List<User> getUserList();    //根據id查詢使用者    User getUserById(int id);    //增加新的使用者    boolean insertNewUser(User u);    //刪除使用者    boolean deleteUserById(int id);    boolean deleteUserByName(String name);    //修改使用者    boolean updateUserById(User u);}

3.2 xxxMapper.xml檔案

<?xml version="1.0" encoding="utf8" ?><!DOCTYPE mapper        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"        "http://mybatis.org/dtd/mybatis-3-mapper.dtd"><!--namespace:名稱空間,繫結mapper/Dao介面--><!--id:介面的方法,resultType:介面的返回值型別--><mapper namespace="com.qian.dao.UserMapper">    <select id="getUserList" resultType="com.qian.pojo.User">        select * from mybatis.user    </select>    <select id="getUserById" parameterType="int" resultType="com.qian.pojo.User">        select * from mybatis.user where id=#{id}    </select>    <!-- 物件中的屬性,可以直接取出來用 -->    <insert id="insertNewUser" parameterType="com.qian.pojo.User">        insert into mybatis.user (id, name, pwd) VALUES (#{id},#{name},#{pwd})    </insert>    <delete id="deleteUserById" parameterType="int">        delete from mybatis.user where id=#{id}    </delete>    <delete id="deleteUserByName" parameterType="String">        delete from mybatis.user where name=#{name}    </delete>    <update id="updateUserById" parameterType="com.qian.pojo.User">        update mybatis.user set name=#{name},pwd=#{pwd} where id=#{id}    </update></mapper>

3.3 Test類

注意:增刪改要提交事務!!!

public class UserDaoTest {    @Test    public void test(){        //獲取SqlSession物件        SqlSession sqlSession = MybatisUtils.getSqlSession();        try{            //獲取mapper            UserMapper mapper = sqlSession.getMapper(UserMapper.class);//            查詢全表//            List<User> list = mapper.getUserList();//            for (User u:list){//                System.out.println(u);//            }            //根據id查詢//            User user = mapper.getUserById(1);//            System.out.println(user);            //插入新使用者,注意:更新,插入,刪除都需要提交事務//            User user1 = new User(4,"李四","25615");//            boolean isInserted = mapper.insertNewUser(user1);//            sqlSession.commit();            //程式碼優化//            if (mapper.insertNewUser(new User(4,"李四","25615"))) sqlSession.commit();            //刪除使用者//            if (mapper.deleteUserById(4))sqlSession.commit();             if (mapper.deleteUserByName("李四"))sqlSession.commit();             //修改使用者            if (mapper.updateUserById(new User(4,"王五","6849816")))sqlSession.commit();        }finally {            //關閉SqlSession            sqlSession.close();        }    }}

3.4 常見錯誤

  • 標籤要匹配
  • resource繫結Mapper,需要使用路徑
  • 配置檔案中不要寫註釋
  • useSSL=true有時候無法使用,改為false

3.5 萬能Map

  • UserMapper.java

      User getUser(Map<String,Object> map);  boolean addUser(Map<String,Object> map);
    
  • UserMapper.xml

      <select id="getUser" parameterType="map" resultType="com.qian.pojo.User">   
          select * from mybatis.user where id=#{userId} 
    </select> 
    <insert id="addUser" parameterType="map">  
        insert into mybatis.user (id, name, pwd) VALUES (#{userId},#{userName},#{password})  </insert>
    
  • Test.java

      @Test  public void test(){      //獲取SqlSession物件   
          SqlSession sqlSession = MybatisUtils.getSqlSession();   
          UserMapper mapper = sqlSession.getMapper(UserMapper.class);   
          Map<String, Object> map = new HashMap<String, Object>(); 
          map.put("userId",5);     
          User user = mapper.getUser(map);
          System.out.println(user);     
          map.put("userId",5);     
          map.put("userName","孫悟空"); 
          map.put("password","565464");   
          if (mapper.addUser(map)) sqlSession.commit();      sqlSession.close();  }
    

3.6 模糊查詢

  • java執行的時候,傳遞萬用字元% %

  • UserMapper.java

      //模糊查詢  List<User> getUsersLike(String value);
    
  • UserMapper.xml

      <select id="getUsersLike" resultType="com.qian.pojo.User">    
          select * from mybatis.user where name like #{value}; 
      </select>
    
  • Test.java

      @Test  public void getUsersLike(){      
          UserMapper mapper = getUserMapper();    
          List<User> userList = mapper.getUsersLike("%千%");  
          System.out.println(userList);  
      }  public UserMapper getUserMapper(){  
          return MybatisUtils.getSqlSession().getMapper(UserMapper.class); 
      }
    
  • 在sql中使用拼接符

      <select id="getUsersLike" resultType="com.qian.pojo.User">    
      select * from mybatis.user where name like "%"#{value}"%"  
      </select>
    

4 配置解析

4.1 核心配合檔案

  • mybatis-config.xml

  • MyBatis 的配置檔案包含了會深深影響 MyBatis 行為的設定和屬性資訊。 配置文件的頂層結構如下:

      configuration(配置)  
      properties(屬性)  
      settings(設定)  
      typeAliases(類型別名)  
      environments(環境配置)      
      environment(環境變數)          
      transactionManager(事務管理器)          
      dataSource(資料來源)
      mappers(對映器)
    

​ 在Mybatis的配置檔案包中是按照從上往下的級別進行排序

4.2 環境配置(environments)

MyBatis 可以配置成適應多種環境,儘管可以配置多個環境,但每個 SqlSessionFactory 例項只能選擇一種環境。

MyBatis預設事務聯結器就是JDBC,連線池POOLED

  • 資料來源(dataSource)
    作用:連線資料庫
    c3p0 druid dbcp
    大多數 MyBatis 應用程式會按示例中的例子來配置資料來源。雖然資料來源配置是可選的,但如果要啟用延遲載入特性,就必須配置資料來源。
    有三種內建的資料來源型別(也就是 type=”[UNPOOLED|POOLED|JNDI]”):

4.3 屬性(properties)

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

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

  • 配置db.properties

      driver=com.mysql.jdbc.Driver 
      url=jdbc:mysql://localhost:3306/mybatis?       useSSL=false&;useUnicode=true&;characterEncoding=UTF8&;serverTimezone=GMT%2B8&;autoConnect=true  
      username=root  
      password=root
    
  • 在核心配置檔案中引入

    注意:在xml中,所有的標籤都可以規定順序

     (properties?,settings?,typeAliases?,typeHandlers?,objectFactory?,objectWrapperFactory?,reflectorFactory?,plugins?,environments?,databaseIdProvider?,mappers?)".
    
  • 核心檔案配置

      <configuration>     
      <properties resource="db.properties"/>   
      <environments default="development">    
          <environment id="development">        
              <transactionManager type="JDBC"></transactionManager>       
              <dataSource type="POOLED">          
                  <property name="driver" value="${driver}"/>       
                  <property name="url" value="${url}"/>    
                  <property name="username" value="${username}"/>  
                  <property name="password" value="${password}"/>   
              </dataSource>     
          </environment> 
          </configuration>
    
  • 總結

    • 可以直接引入外部檔案
    • 也可以在裡面配置
    • 外部引入的檔案(db.properties)的優先順序要比在要高
  • 錯誤提示

4.4 類型別名(typeAliases)

類型別名可為 Java 型別設定一個縮寫名字。 它僅用於 XML 配置,意在降低冗餘的全限定類名書寫。

  • 在mybatis-config.xml -> 配置

       <!-- 第一種:直接給類取別名 用於實體類較少 -->  
    <typeAliases>  
        <typeAlias type="com.yu.pojo.User" alias="user"> 
        </typeAlias>   
        
      <!-- 第二種:直接掃描一個包目錄下 預設名為類的小寫 -->   <!-- 如需自定義名在實體類中 @Alias--> 
     <typeAliases>  
      <package name="com.lsa.pojo"/> 
     </typeAliases>
        
    
  • 如果使用第二種掃描包目錄下的方式那麼返回值後面設定的就是該類名字的小寫

  • 然後xxxMapper.xml的返回值(resultType)就可以替換為resultType user

  • 實體類較少的時候使用第一種,較多就直接掃描包目錄

  • 第一種可以自定義取別名

  • 第二種也可以用註解@Alias(“xxx”)給類起別名

4.5 設定(settings)

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

  • 需要掌握的設定

4.6 對映器(mappers)

MapperRegistry:註冊繫結我們的Mapper檔案

每一個mapper.xml都需要在Mybatis核心配置檔案中註冊

幾種繫結方式

  • class與掃描包(packaaage)繫結的注意點
    • 介面和Mapper配置檔案必須同名
    • 介面和他的Mapper配置檔案必須在同一包下

4.7 其他配置

  • typeHandlers(型別處理器)
  • objectFactory(物件工廠)
  • plugins(外掛)

4.8 生命週期和作用域(Scope)

理解我們之前討論過的不同作用域和生命週期類別是至關重要的,因為錯誤的使用會導致非常嚴重的併發問題。

SqlSessionFactoryBuilder

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

SqlSessionFactory

  • 全域性變數:一旦被建立就應該在應用的執行期間一直存在,可以想象為資料庫連線池,沒有任何理由丟棄它或重新建立另一個例項。
  • 因此 SqlSessionFactory 的最佳作用域是應用作用域
  • 最簡單的就是使用單例模式或者靜態單例模式。
  • 說白了就是可以想象為:資料庫連線池

SqlSession

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

每一個Mapper代表一個具體的業務

5 解決屬性名和欄位名不一致

5.1 新建專案mybatis-03

  • 複製原檔案到對應目錄下,修改屬性(ctrl+r)pwd->password

  • 使用測試類測試

  • UserMapper.xml

      <mapper namespace="com.yu.dao.UserMapper">  
          <select id="getUserById" parameterType="int" resultType="user">    
              select * from mybatis.user where id=#{id}      
              <!--   這句程式碼的本質:select id,name,pwd from ... 型別處理器找不到對應欄位的屬性,無法賦值  -->     
          </select>
    </mapper>
    

5.2 解決方法

  • 起別名

      select id,name,pwd as password from mybatis.user where id=#{id}
    
  • 使用resultMap
    resultMap:結果集對映

      <!--column資料庫的列名 property實體類名-->
        <resultMap id="UserMap" type="User">
            <result column="id" property="id"/>
            <result column="name" property="name"/>
            <result column="pwd" property="password"/>
        </resultMap>
        <!-- 通過id查詢-->
        <select id="getUserById" parameterType="int" resultMap="UserMap">
            select * from user where id = #{id}
        </select>
    

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

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

    雖然上面的例子不用顯式配置 ResultMap。 但為了講解,我們來看看如果在剛剛的示例中,顯式使用外部的 resultMap 會怎樣,這也是解決列名不匹配的另外一種方式。

6 日誌

6.1 日誌工廠

如果資料庫操作出現異常,需要通過日誌獲取sql語句,方便糾錯。

  • logImpl:指定 MyBatis 所用日誌的具體實現,未指定時將自動查詢。

    • SLF4J +
    • LOG4J 【掌握】
    • | LOG4J2
    • | JDK_LOGGING
    • | COMMONS_LOGGING
    • | STDOUT_LOGGING 【掌握】
    • | NO_LOGGING
  • 在mybatis-config.xml中配置設定

    STDOUT_LOGGING 標準日誌輸出

      <configuration>  
          <properties resource="db.properties"/>   
          <settings>    
              <setting name="logImpl" value="STDOUT_LOGGING"/>   
          </settings>   
          ... ... 
          <configuration>
    

6.2 測試輸出

  • STDOUT_LOGGING

      Logging initialized using 'class org.apache.ibatis.logging.stdout.StdOutImpl' adapter.  /*      ...  */  Created connection 471579726.  Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@1c1bbc4e]  ==>  Preparing: select * from mybatis.user where id=?   ==> Parameters: 1(Integer)  <==    Columns: id, name, pwd  <==        Row: 1, 千樹, 123  <==      Total: 1  User{id=1, name='千樹', password='123'}  Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@1c1bbc4e]  Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@1c1bbc4e]  Returned connection 471579726 to pool.
    

    從上面的輸出可以看出,mybatis本質上是封裝了JDBC

  • LOG4J

      ......  Caused by: org.apache.ibatis.exceptions.PersistenceException:   ### Error building SqlSession.  ### The error may exist in SQL Mapper Configuration  ### Cause: org.apache.ibatis.builder.BuilderException: Error parsing SQL Mapper Configuration. Cause: org.apache.ibatis.logging.LogException: Error setting Log implementation.   ......  Caused by: org.apache.ibatis.builder.BuilderException: Error parsing SQL Mapper Configuration. Cause: org.apache.ibatis.logging.LogException: Error setting Log implementation.  Cause: java.lang.NoClassDefFoundError: org/apache/log4j/Priority  ...
    

    使用另一個標準就會報錯,說明需要配置另外的東西

    解決方法:配置maven匯入log4j

    pom.xml

      <!-- https://mvnrepository.com/artifact/log4j/log4j --> 
    <dependency>    
        <groupId>log4j</groupId> 
        <artifactId>log4j</artifactId>  
        <version>1.2.17</version> 
    </dependency>
    

    然後輸出為正常輸出了:

      log4j:WARN No appenders could be found for logger (org.apache.ibatis.logging.LogFactory).  log4j:WARN Please initialize the log4j system properly.  log4j:WARN See http://logging.apache.org/log4j/1.2/faq.html#noconfig for more info.  User{id=1, name='千樹', password='123'}
    

6.3 LOG4J

什麼是Log4J

  • log4j簡介

  • Log4j是一個由Java編寫可靠、靈活的日誌框架,是Apache旗下的一個開源專案;現如今,Log4j已經被移植到了C、C++、Python等語言中,服務更多的Developer;

  • 使用Log4j,我們更加方便的記錄了日誌資訊,它不但能控制日誌輸出的目的地,也能控制日誌輸出的內容格式;

  • 通過定義不同的日誌級別,可以更加精確的控制日誌的生成過程,從而達到我們應用的需求;

  • 這一切,都得益於一個靈活的配置檔案,並不需要我們更改程式碼

  • 1.先匯入log4j的包

  •        <dependencies>
            <dependency>
                <groupId>log4j</groupId>
                <artifactId>log4j</artifactId>
                <version>1.2.17</version>
            </dependency>
               
    
  • 2.log4j.properties

    
    log4j.rootLogger=DEBUG,console,file 
    #控制檯輸出 console appender
    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  
    #檔案輸出 rolling file appender
    log4j.appender.file=org.apache.log4j.RollingFileAppender 
    log4j.appender.file.File=./log/lsa.log
    log4j.appender.file.MaxFileSize=10mB
    log4j.appender.file.threshold=DEBUG   
    log4j.appender.file.layout=org.apache.log4j.PatternLayout   
    log4j.appender.file.MaxBackupIndex=2  
    log4j.appender.file.layout.ConversionPattern=%d{mmm d,yyyy hhss a} : %p [%t] %m%n   
    #日誌輸出級別 logger
    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>
    

  • 最終測試輸出

  • 簡單的使用

      public class UserDaoTest {   
          static Logger logger = Logger.getLogger(UserDaoTest.class);   
          @Test   
          public void loggerTest(){     
              logger.info("info:進入了TestLog4j");   
              logger.debug("debug:除錯");     
              logger.error("error:錯誤");   
              logger.fatal("fatal:致命錯誤");   
          } 
      }
    
    • 導包:import org.apache.log4j.Logger;

    • 獲取日誌物件,引數為當前類的class

    • 設定日誌級別

      7 分頁

      為什麼要分頁?

​ 答:減少資料處理量

7.1 limit分頁

  • 語法

             select * from xxx limit startIndex,pageSize 
             select * from user limit 3;
    
  • mybatis的sql語句如果有多個引數,需要用map封裝

  • 介面 UserMapper.java

      List<User> selectLimit(Map<String,Integer> map);
    
  • Mapper.xml

      <select id="selectLimit" parameterType="map" resultMap="UserMap">    
      select * from mybatis.user limit #{startIndex},#{pageSize} 
      </select>
    
  • 測試

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

    7.2 RowBounds分頁

    這種方法不推薦使用,因為官方不推薦

  • 介面

      List<User> selectRowBounds();
    
  • 配置

      <select id="selectRowBounds" resultMap="UserMap">   
      select * from mybatis.user 
      </select>
    
  • 測試

      @Test
    public void selectRowBounds(){
          SqlSession sqlSession = MybatisUtils.getSqlSession(); 
          RowBounds rowBounds = new RowBounds(0,2); 
          List<User> list = sqlSession.selectList("com.yu.dao.UserMapper.selectRowBounds",null,rowBounds);   
          for (User user : list) { 
              System.out.println(user); 
          }    
          sqlSession.close(); 
      }
    

7.3 分頁外掛

MyBatis分頁外掛 PageHelper

  • 網站:點選訪問
  • 網站有詳細的文件,此處不在介紹,瞭解即可,需要用的時候再學

8 使用註解開發

註解的本質是使用反射,底層是代理模式(見設計模式)

使用註解來對映簡單語句會使程式碼顯得更加簡潔,但對於稍微複雜一點的語句,Java 註解不僅力不從心,還會讓你本就複雜的 SQL 語句更加混亂不堪。 因此,如果你需要做一些很複雜的操作,最好用 XML 來對映語句。

8.1 註解查詢

  • 介面

      @Select("select * from mybatis.user")  
       List<User> selectAll();
    
  • 註冊繫結

      <mappers>    
      <mapper class="com.yu.dao.UserMapper"/>
      </mappers>
    
  • 測試

      @Test  public void selectAll(){   
          SqlSession sqlSession = MybatisUtils.getSqlSession();      //底層主要應用反射    
          UserMapper mapper = sqlSession.getMapper(UserMapper.class); 
          List<User> list=mapper.selectAll();  
          for (User user : list) {      
              System.out.println(user);
          }     
          sqlSession.close();
      }
    

8.2 註解CRUD

  • 設定自動提交

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

      //多個引數情況下,有兩種解決方式,一個map封裝,另一種是註解Param
    @Select("select * from mybatis.user where id=#{id}") 
    User selectUserById(@Param("id") int id); 
    @Select("select * from mybatis.user")
    List<User> selectAll(); 
    @Insert("insert into mybatis.user() values(#{id},#{name},#{password}) ")  
    boolean insertUser(User u); 
    @Update("update user set name=#{name},pwd=#{password} where id = #{id}") 
    boolean updateUser(User u); 
    @Delete("delete from mybatis.user where id=#{id}") 
    boolean deleteUser(@Param("id") int id);
    
  • Test

        @Test
        public void getUserById() {
            SqlSession sqlSession = MybatisUtils.getSqlSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
            List<User> userById = mapper.getUserById();
            for (User user : userById) {
                System.out.println(user);
            }
        }
    User{id=1, name='張三', password='null'}
    User{id=2, name='李四', password='null'}
    User{id=3, name='王五', password='null'}
    User{id=4, name='六六六', password='null'}
    User{id=5, name='7777', password='null'}
    User{id=6, name='李六', password='null'}
    
    
    

8.3 @Param註解

  • 基本型別的引數和String型別,需要加上這個註解

  • 引用型別不需要加

  • 如果只有一個基本型別的引數,可以省略

  • 我們在sql中引用的就是@Param(“xxx”)中設定的屬性名

  • 如果@parm後面定義的什麼,匹配的時候就應該是什麼

  • @Select("select * from mybatis01.user where id = #{id2}")
    List<User> getUserByIdandname(@Param("id") int id2);
    

9 MyBatis執行流程

  1. Resource獲取載入全域性配置檔案

  2. 例項化SqlSessionFactoryBuilder構造器

  3. 解析配置檔案流XMLConfigBuilder

  4. Configuration所有的配置資訊

  5. SqlSessionFactory例項化

  6. transaction事務管理器

  7. 建立executor執行器

  8. 建立sqlSession

  9. 實現CRUD

  10. 檢視是否執行成功

  11. 提交事務

  12. 關閉sqlSession連線

10 Lombok

Lombok是一個可以通過簡單的註解形式來幫助我們簡化消除一些必須有但顯得很臃腫的Java程式碼的工具,通過使用對應的註解,可以在編譯原始碼的時候生成對應的方法。

10.1 安裝方法

  1. 左上角File->Settings->Plugins

  2. 搜尋Lombok,下載安裝

  3. 匯入maven

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

10.2 使用Lombok

  • Lombok的支援

      @Getter and @Setter  @FieldNameConstants  @ToString  @EqualsAndHashCode  @AllArgsConstructor, @RequiredArgsConstructor and @NoArgsConstructor  @Log, @Log4j, @Log4j2, @Slf4j, @XSlf4j, @CommonsLog, @JBossLog, @Flogger, @CustomLog  @Data  @Builder  @SuperBuilder  @Singular  @Delegate  @Value  @Accessors  @Wither  @With  @SneakyThrows  @val  @var  experimental @var  @UtilityClass  Lombok config system  Code inspections  Refactoring actions (lombok and delombok)
    
  • 常用支援

      @Data支援: 無參構造,getter&setter,toString,hashCode,equals  @AllArgsConstructor: 有參構造  @NoArgsConstructor: 無參構造
    
  • 使用方法,在實體類上加註解

11 多對一(一對多)的處理

  • 多個學生對應一個老師

  • 學生關聯老師,多對一

  • 老師管理集合,一對多

  • Sql建表

  •   create table `teacher`
      (  `id` int not null, 
       `name` varchar(30) default null, 
       primary key(`id`) 
      ) engine=InnoDB default charset=utf8; 
      insert into teacher values (1,'王老師'); 
      create table `student`( 
          `id` int not null, 
          `name` varchar(30) default null,
          `tid` int not null,  primary key(`id`),
          key `FK_tid` (`tid`),  
          constraint `FK_tid` foreign key(`tid`) references `teacher`(`id`)  ) engine=InnoDB default charset=utf8;
    

11.1 測試環境搭建

  1. 匯入Lombok
  2. 新建Teacher,Student實體類
  3. 新建Mapper介面
  4. 在resources新建com->xxx->dao資料夾
  5. 新建xxxMapper.xml檔案
  6. 在mybatis-config.xml中註冊繫結xxxMapper.xml
  7. 在TeacherMapper介面中建立selectAll()方法
  8. 在TeacherMapper.xml中寫對應的查詢
  9. 新建測試類,在測試類中測試使用

11.2 按照查詢巢狀處理

  • 程式碼演示

      @Data
    @AllArgsConstructor
    @NoArgsConstructor
    public class Student { 
        private int id;   
        private String name;   
        private Teacher teacher; 
    }
    
      @Data 
    @AllArgsConstructor
    @NoArgsConstructor
    public class Teacher {   
        private int id; 
        private String name; 
    }
    
      List<Student> selectAll();
    
        <!--  查詢思路:1.查詢所有學生  2.根據查詢出的學生的tid查詢老師,子查詢   --> 
    <resultMap id="student_teacher" type="Student"> 
        <!-- property是實體類的屬性 column是資料庫的欄位 -->  
        <result property="id" column="id"/> 
        <result property="name" column="name"/>    
        <!-- 複雜的屬性,需要單獨處理,物件:association 集合collection  -->     
        <association property="teacher" column="tid" javaType="Teacher" select="getTeacher"/>  
    </resultMap> 
    <select id="selectAll" resultMap="student_teacher"> 
        select * from mybatis.student 
    </select>
    <select id="getTeacher" resultType="Teacher">   
            select * from mybatis.teacher where id=#{tid}
    </select>
    
       @Test  public void selectAll(){   
           SqlSession sqlSession = MybatisUtils.getSqlSession();  
           StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);      List<Student> studentList = mapper.selectAll();
           for (Student s:           studentList) {  
               System.out.println(s);
           }      
           sqlSession.close(); 
       }
    

11.3按照結果巢狀處理

  • 程式碼演示

      List<Student> selectAll2();
    
      <select id="selectAll2" resultMap="S_T">   
          select s.id sid,s.name sname,t.name tname 
          from mybatis.student s,mybatis.teacher t      where s.tid=t.id
    </select> 
    <resultMap id="S_T" type="Student">   
              <result property="id" column="sid"/> 
              <result property="name" column="sname"/>  
              <association property="teacher" javaType="Teacher">    
             <result property="name" column="tname"/>    
              </association> 
    </resultMap>
    
      //測試程式碼
    

11.4 回顧Mysql多對一查詢方式

  • 子查詢
  • 聯表查詢

11.5 一對多的處理

  • 程式碼演示

      @Data
    @AllArgsConstructor 
    @NoArgsConstructor 
    public class Teacher {    
        private int id;   
        private String name;   
        //老師擁有多個學生  
        private List<Student> students; 
    }
    
      @Data
    @AllArgsConstructor
    @NoArgsConstructor
    public class Student {
        private int id;  
        private String name;
        private int tid; 
    }
    
      public interface TeacherMapper { 
          List<Teacher> selectAll();  
          //獲取指定老師下的所有學生   
          Teacher getTeacher(@Param("tid")int id); 
          Teacher getTeacher2(@Param("tid")int id); 
          List<Student> getStudents(@Param("tid")int id);  }
    

    按照結果進行查詢

       <select id="selectAll" resultType="Teacher">  
           select * from mybatis.teacher 
    </select> 
    <select id="getTeacher" resultMap="S_T"> 
        select t.id tid, t.name tname,s.name sname 
        from mybatis.teacher t,mybatis.student s    
        where s.tid=tid and tid=#{tid} 
    </select>
    <resultMap id="S_T" type="Teacher">  
        <result property="id" column="tid"/> 
        <result property="name" column="tname"/>  
        <!-- 集合中的泛型資訊,我們使用ofType -->   
        <collection property="students" ofType="Student">  
            <result property="name" column="sname"/>  
            <result property="tid" column="tid"/>    
        </collection> 
    </resultMap> 
    ===============================================================================
    
    

    按照查詢巢狀處理

       <select id="getTeacherById3" resultMap="Teacher_Student3">
            select * from mybatis01.teacher where id = #{tid}
        </select>
        <resultMap id="Teacher_Student3" type="Teacher">
            <result property="id" column="id"/>
            <result property="name" column="name"/>
            <collection property="studentList" javaType="ArrayList" ofType="Student" select="getStudent" column="id"/>
        </resultMap>
        <select id="getStudent" resultType="Student">
            select * from mybatis01.student where tid = #{id}
        </select>
    

11.6 小結

  • 關聯 association 多對一 javatype
  • 集合 collection 一對多 oftype
  • 複雜的屬性,需要單獨處理,物件:association 集合collection
  • 集合中的泛型資訊,我們使用ofType
  • javaType 指定實體類中屬性的型別
  • ofType 用來指定對映到List或者集合中的pojo型別,泛型中的約束型別

注意點:

  • 保證sql語句的可讀性
  • 注意一對多和多對一屬性和欄位的問題
  • 面試要點
    • Mysql引擎
    • InnoDB底層原理
    • 索引
    • 索引優化

12 動態SQL

什麼是動態SQL?

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

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

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

12.1 搭建環境

  • 搭建資料庫

      create table blog(  
          id varchar(50) not null comment '部落格id',
          title varchar(100) not null comment '部落格標題', 
          author varchar(30) not null comment '部落格作者', 
          ctreate_time datetime not null comment '建立時間',
          views int not null comment '瀏覽量' 
      )engine=InnoDB default charset=utf8;
    

建立基礎工程

  • 導包

  • 編寫配置檔案

    mybatis-config 與MybatisUtils

  • 編寫實體類日

      import java.util.Date; 
    @Data 
    @AllArgsConstructor
    @NoArgsConstructor 
    public class Blog {  
          private String id;  
          private String title;  
          private String author; 
          private Date createTime; 
          private int views; 
    }
    
  • 編寫實體類對應Mapper介面和Mapper.xml檔案

      public interface BlogMapper {}
    
      <?xml version="1.0" encoding="UTF-8" ?>  <!DOCTYPE mapper          PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> 
    <mapper namespace="com.yu.dao.BlogMapper">
    </mapper>
    
  • 解決資料庫欄位名和實體類屬性名不一致的問題

      <!--       有兩種解決方法:     
         1.如果不是按規範轉換,在xxxMapper.xml用ResultMap,上面已經介紹過      
         2.如果是規範命名,在mybatis-config.xml檔案中<settings>-><setting>->id="mapUnderscoreToCamelCase" value="true",它的作用是駝峰命名轉換 
         3.直接在查詢的列名中起別名-->  
    <settings>   
          <setting name="logImpl" value="STDOUT_LOGGING"/> 
          <setting name="mapUnderscoreToCamelCase" value="true"/>  
    </settings>
    

12.2 if

  • 使用動態 SQL 最常見情景是根據條件包含 where 子句的一部分。比如:

      <select id="findActiveBlogWithTitleLike"   
              resultType="Blog"> 
          SELECT * FROM BLOG    WHERE state = ‘ACTIVE’  
          <if test="title != null">     
              AND title like #{title}   
          </if> 
    </select>
    
  • 程式碼演示

      //查詢部落格
    List<Blog> queryBlogIf(Map<String,Object> map);
    
      <select id="queryBlogIf" parameterType="map" resultType="Blog">  
          select * from mybatis.blog where 1=1  
          <if test="title!=null">     
              and title = #{title}    
          </if>   
          <if test="author!=null"> 
             and author = #{author}   
          </if> 
    </select>
    
    方法二帶有where 可以幫你取消and 或不符合的sql
    <select id="queryBlogByIf" parameterType="map" resultType="Blog">
            <!--id,title,author,ctreate_time as createTime,views-->
            select * from mybatis01.blog
            <where>
                <if test="title !=null">
                    and title = #{title}
                </if>
                <if test="author !=null">
                    and author = #{author}
                </if>
            </where>
        </select>
    
    
    ```java
      @Test
        public void queryBlogByIf() {
            SqlSession sqlSession = MybatisUtils.getSqlSession();
            BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
            Map map = new HashMap();
            //map.put("title", "Mybatis那麼簡單");
            map.put("author", "盧哥說");
            List<Blog> blogList = mapper.queryBlogByIf(map);
            for (Blog blog : blogList) {
                System.out.println(blog);
            }
            sqlSession.close();
        }
    

12.3 choose、when、otherwise

  • 有時候,我們不想使用所有的條件,而只是想從多個條件中選擇一個使用。針對這種情況,MyBatis 提供了 choose 元素,它有點像 Java 中的 switch 語句。

  • 還是上面的例子,但是策略變為:傳入了 “title” 就按 “title” 查詢,傳入了 “author” 就按 “author” 查詢的情形。若兩者都沒有傳入,就返回標記為 featured 的 BLOG(這可能是管理員認為,與其返回大量的無意義隨機 Blog,還不如返回一些由管理員挑選的 Blog)。

  • 程式碼演示

        List<Blog> queryBlogByChoose(Map map);
    
     <select id="queryBlogByChoose" parameterType="map" resultType="Blog">
            <!--        id,title,author,ctreate_time as createTime,views-->
            select * from mybatis01.blog
            <where>
                <choose>
                    <when test="title !=null">
                         title = #{title}
                    </when>
                    <when test="author !=null">
                        and author = #{author}
                    </when>
                    <otherwise>
                        and views = #{views}
                    </otherwise>
                </choose>
            </where>
        </select>
    
        
        @Test
        public void queryBlogByChoose() {
            SqlSession sqlSession = MybatisUtils.getSqlSession();
            BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
            Map map = new HashMap();
            map.put("title", "Mybatis那麼簡單");
            map.put("author", "盧哥說");
            map.put("views", 999);
            List<Blog> blogList = mapper.queryBlogByChoose(map);
            for (Blog blog : blogList) {
                System.out.println(blog);
            }
    
            sqlSession.close();
        }
    他只符合你的第一條,如果第一條滿足不會往下進行否則反之
    

12.4 trim、where、set

  • 前面幾個例子已經合宜地解決了一個臭名昭著的動態 SQL 問題

  • where

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

  • 就是隻有下面if成立才啟動where 且可以幫你取出and 或 or

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

  •   <trim prefix="WHERE" prefixOverrides="AND |OR ">    ...  </trim>
    
    
    ```xml
        List<Blog> queryBlogByIf(Map map);
        
            
            <select id="queryBlogByIf" parameterType="map" resultType="Blog">
            <!--        id,title,author,ctreate_time as createTime,views-->
            select * from mybatis01.blog
            <where>
                <if test="title !=null">
                    and title = #{title}
                </if>
                <if test="author !=null">
                    and author = #{author}
                </if>
            </where>
        </select>
    
    
    

  • set

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

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

  • 用於動態更新語句的類似解決方案叫做 set。set 元素可以用於動態包含需要更新的列,忽略其它不更新的列。比如:

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

  • <trim prefix="SET" suffixOverrides=",">    ...  </trim>
    
  •   
     int updateBlog(Map map);
       <update id="updateBlog" parameterType="map">
            update mybatis01.blog
            <set>
                <if test="title !=null">
                    title = #{title},
                </if>
                <if test="author !=null">
                    author = #{author}
                </if>
            </set>
            where id = #{id}
        </update>
       </select>
    
  • 所謂動態sql,本質還是SQL語句,只是我們可以在SQL層面,執行邏輯程式碼

12.5 SQL片段

  • 有的時候,我們可以將一些功能的部分抽取出來(類似函式的封裝),方便複用

    1.使用SQL標籤抽取公共部分

  •   <sql id="if-title-author">   
          <if test="title!=null">   
              title = #{title}   
          </if>    
          <if test="author!=null">  
              and author = #{author}   
          </if>
    </sql>
    

    2.在需要使用的地方使用Include標籤引用即可

  • 使用sql標籤封裝,在需要的地方使用include標籤引入

    <select id="queryBlogIf" parameterType="map" resultType="Blog">  
        select * from mybatis.blog   
        <where>  
            <include refid="if-title-author">
            </include> 
            <!--      <if test="title!=null">          title = #{title}      </if>      <if test="author!=null">          and author = #{author}      </if>      -->      </where> 
    </select>
    
  • 注意事項:

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

12.6 foreach

  • 動態 SQL 的另一個常見使用場景是對集合進行遍歷(尤其是在構建 IN 條件語句的時候)。比如:

      <select id="selectPostIn" resultType="domain.blog.Post">  
          SELECT *    FROM POST P    WHERE ID in   
          <foreach item="item" index="index" collection="list"  
                  open="(" separator="," close=")">  
              #{item}  
          </foreach>  </select>
    
  • foreach 元素的功能非常強大,它允許你指定一個集合,宣告可以在元素體內使用的集合項(item)和索引(index)變數。它也允許你指定開頭與結尾的字串以及集合項迭代之間的分隔符。這個元素也不會錯誤地新增多餘的分隔符,看它多智慧!

  • 提示 你可以將任何可迭代物件(如 List、Set 等)、Map 物件或者陣列物件作為集合引數傳遞給 foreach。當使用可迭代物件或者陣列時,index 是當前迭代的序號,item 的值是本次迭代獲取到的元素。當使用 Map 物件(或者 Map.Entry 物件的集合)時,index 是鍵,item 是值。

  • 至此,我們已經完成了與 XML 配置及對映檔案相關的討論。下一章將詳細探討 Java API,以便你能充分利用已經建立的對映配置。

  • foreach的作用,就是為了替代下面這種複雜的語句

    <select id="queryBlogByForeach" parameterType="map" resultType="Blog">
            select * from mybatis01.blog
            <where>
                <!--elect * from mybatis01.blog where id  = IN (1 OR 2 OR 3)-->
                <foreach collection="ids" item="id" open=" in (" close=")" separator="or">
                    id = #{id}
                </foreach>
            </where>
        </select>
    其中item 後的引數就相當於 sql where後面得資料線
    
  • 程式碼演示(改一下資料庫id)

      List<Blog> queryBlogForeach(Map<String,Object> map);
    
      <select id="queryBlogForeach" parameterType="map" resultType="Blog">      select * from mybatis.blog      <where>          <foreach collection="ids" item="id" open="(" close=")" separator="or">              id=#{id}          </foreach>      </where>  </select>
    
       @Test  public void queryBlogForeach(){      SqlSession sqlSession = MybatisUtils.getSqlSession();      BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);      Map<String,Object> map=new HashMap<String,Object>();      List<Integer> ids = new ArrayList<Integer>();//        ids.add(1);      ids.add(2);      ids.add(3);      map.put("ids",(List)ids);      List<Blog> blogs = mapper.queryBlogForeach(map);      sqlSession.close();  }
    

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

    建議:

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

13 快取

13.1 簡介

  • 為什麼要使用快取

    每次查詢都要連線資料庫,比較耗資源,我們把查詢到的資料暫存到記憶體裡面,下次查詢的時候,從記憶體讀取, 這個地方就叫快取。

  • 什麼樣的資料適用於快取?

    經常查詢且不經常改變的資料

13.2 Mybatis快取

  • Mybatis系統預設定義了兩級快取
    • 預設情況下,只有一級快取開啟(SqlSession快取,也稱為本地快取)
    • 二級快取需要手動配置,它是基於namespace級別的快取
    • Mybatis定義了快取介面Cache,可以通過實現Cache介面來自定義二級快取

13.3 一級快取

  • 測試步驟

    1. 開啟日誌

    2. 測試在一個Session中查詢兩次的計量局

    3. 檢視日誌輸出

      sql查詢出來一次

  • 程式碼演示

        User getUserById(@Param("id") int id);
    
     <select id="getUserById" parameterType="int" resultType="User">
            select * from mybatis01.user where id = #{id}
        </select>
    
        @Test
        public void userTest() {
            SqlSession sqlSession = MybatisUtils.getSqlSession();
            UserMapper mapper = sqlSession.getMapper(UserMapper.class);
            User userById = mapper.getUserById(1);
            System.out.println(userById);
            System.out.println("===========================");
    
            User userById2 = mapper.getUserById(1);
            System.out.println(userById);
    
            System.out.println(userById == userById2);
            sqlSession.close();
        }
    

  • 快取失效的情況

    1. 查詢不同的xxxMapper.xml

    2. 增刪改

    3. 查詢不同的東西

    4. 手動清理快取(sqlSession.clearCache())

      @Test
      public void updateUser() {
          SqlSession sqlSession = MybatisUtils.getSqlSession();
          UserMapper mapper = sqlSession.getMapper(UserMapper.class);
          User userById = mapper.getUserById(1);
          System.out.println(userById);
      
          System.out.println("===========================");
          mapper.updateUser(new User(2, "張四111", "123456789"));
              sqlSession.clearCache();
          System.out.println("===========================");
          User userById2 = mapper.getUserById(1);
          System.out.println(userById2);
      
          System.out.println(userById == userById2);
          sqlSession.close();
      }
      

​ 小結:一級快取預設是開啟的,只在一次sqlsession中有效 ,也就是拿到連結關閉連結

一級快取就是map

13.4 二級快取

  • MyBatis 內建了一個強大的事務性查詢快取機制,它可以非常方便地配置和定製。 為了使它更加強大而且易於配置,我們對 MyBatis 3 中的快取實現進行了許多改進。

  • 預設情況下,只啟用了本地的會話快取,它僅僅對一個會話中的資料進行快取。 要啟用全域性的二級快取,只需要在你的 SQL 對映檔案中新增一行:

      <cache/>
    
  • 基本上就是這樣。這個簡單語句的效果如下:

  • 這些屬性可以通過 cache 元素的屬性來修改。比如:

      <cache  
             eviction="FIFO"   
             flushInterval="60000"   
             size="512" 
             readOnly="true"/>
    
  • 這個更高階的配置建立了一個 FIFO 快取,每隔 60 秒重新整理,最多可以儲存結果物件或列表的 512 個引用,而且返回的物件被認為是隻讀的,因此對它們進行修改可能會在不同執行緒中的呼叫者產生衝突。

  • 使用二級快取步驟:

    1. 開啟全域性快取

      <!-- 雖然預設開啟,但是寫上可以讓看程式碼的人明白 -->
      <setting name="cacheEnabled" value="true"/>
      
    2. 在要使用二級快取的Mapper.xml中,寫標籤

      <cache 
             eviction="FIFO" 
             flushInterval="60000"
             size="512"
             readOnly="true"/>
      
    3. 測試使用

        @Test
          public void updateUser2() {
              SqlSession sqlSession = MybatisUtils.getSqlSession();
              SqlSession sqlSession2 = MybatisUtils.getSqlSession();
      
              UserMapper mapper = sqlSession.getMapper(UserMapper.class);
              User userById = mapper.getUserById(1);
              System.out.println(userById);
              sqlSession.close();
      
              System.out.println("===========================");
              UserMapper mapper2 = sqlSession2.getMapper(UserMapper.class);
              User userById2 = mapper2.getUserById(1);
              System.out.println(userById2);
      
      
              System.out.println(userById == userById2);
              sqlSession2.close();
          }
      
  • 問題

    • 我們需要實體類序列化,否則會丟擲異常
  • 小結

    • 二級快取在同一個Mapper下有效
    • 所有的資料都會先放在一級快取中
    • 當會話提交或者關閉,資料會被轉存到二級快取中

13.5 快取原理

  • 圖片演示

快取順序

1.先看二級快取中有沒有

2.再看一級快取中有沒有

3.查詢資料庫

13.6 自定義快取ehcache

  • 簡介

    EhCache 是一個純Java的程序內快取框架,具有快速、精幹等特點,是Hibernate中預設CacheProvider。Ehcache是一種廣泛使用的開源Java分散式快取。主要面向通用快取,Java EE和輕量級容器。

  • 使用

    1. 導包

      <!-- https://mvnrepository.com/artifact/org.mybatis.caches/mybatis-ehcache --><dependency>
          <groupId>org.mybatis.caches</groupId> 
          <artifactId>mybatis-ehcache</artifactId>
          <version>1.2.2</version>
      </dependency>
      
    2. 寫入配置檔案(resources->ehcache.xml)

      <?xml version="1.0" encoding="UTF-8"?>
      <ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
               xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd"><!-- 磁碟快取位置 -->
          <diskStore path="java.io.tmpdir/ehcache"/><!-- 預設快取 -->
          <defaultCache
                  maxEntriesLocalHeap="10000"
                  eternal="false"
                  timeToIdleSeconds="120"
                  timeToLiveSeconds="120"
                  maxEntriesLocalDisk="10000000"
                  diskExpiryThreadIntervalSeconds="120"
                  memoryStoreEvictionPolicy="LRU">
              <persistence strategy="localTempSwap"/>
          </defaultCache><!-- helloworld快取 -->
          <cache name="HelloWorldCache"
                 maxElementsInMemory="1000"
                 eternal="false"
                 timeToIdleSeconds="5"
                 timeToLiveSeconds="5"
                 overflowToDisk="false"
                 memoryStoreEvictionPolicy="LRU"/>
      </ehcache>
      
    3. 在Mapper中指定

      <cache type="org.mybatis.caches.ehcache.EhcacheCache"/>
      
    4. 測試使用(用之前的程式碼即可)

  • 自定義快取

    只要實現了org.apache.ibatis.cache.Cache介面,就能定義自己的快取,但是實現比較複雜,只需要會使用就行,ehcache是繼承了AbstractEhcacheCache,該類已經實現了Cache介面。

      public class MyCache implements Cache {      @Override      public String getId() {          return null;      }      @Override      public void putObject(Object key, Object value) {      }      @Override      public Object getObject(Object key) {          return null;      }      @Override      public Object removeObject(Object key) {          return null;      }      @Override      public void clear() {      }      @Override      public int getSize() {          return 0;      }  }
    
  • 實際開發中使用的快取

    • 在實際開發中,我們更多的使用Redis來做快取