亚洲激情专区-91九色丨porny丨老师-久久久久久久女国产乱让韩-国产精品午夜小视频观看

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Mybatis是什么及怎么使用

發布時間:2023-05-11 15:02:20 來源:億速云 閱讀:151 作者:iii 欄目:開發技術

這篇文章主要介紹“Mybatis是什么及怎么使用”,在日常操作中,相信很多人在Mybatis是什么及怎么使用問題上存在疑惑,小編查閱了各式資料,整理出簡單好用的操作方法,希望對大家解答”Mybatis是什么及怎么使用”的疑惑有所幫助!接下來,請跟著小編一起來學習吧!

    一 數據庫操作框架的歷程

    1.1 JDBC

    JDBC(Java Data Base Connection,java數據庫連接)是一種用于執行SQL語句的Java API,可以為多種關系數據庫提供統一訪問,它由一組用Java語言編寫的類和接口組成.JDBC提供了一種基準,據此可以構建更高級的工具和接口,使數據庫開發人員能夠編寫數據庫應用程序

    • 優點:運行期:快捷、高效

    • 缺點:編輯期:代碼量大、繁瑣異常處理、不支持數據庫跨平臺

    Mybatis是什么及怎么使用

    jdbc核心api

    1. DriverManager 連接數據庫

    2. Connection 連接數據庫的抽象

    3. Statment 執行SQL

    4. ResultSet 數據結果集

    1.2 DBUtils

    DBUtils是Java編程中的數據庫操作實用工具,小巧簡單實用。
    DBUtils封裝了對JDBC的操作,簡化了JDBC操作,可以少寫代碼。
    DBUtils三個核心功能介紹

    1. QueryRunner中提供對sql語句操作的API

    2. ResultSetHandler接口,用于定義select操作后,怎樣封裝結果集

    3. DBUtils類,它就是一個工具類,定義了關閉資源與事務處理的方法

    1.3 Hibernate

    ORM 對象關系映射

    1. object java對象

    2. relational 關系型數據

    3. mapping 映射

    • Hibernate 是由 Gavin King 于 2001 年創建的開放源代碼的對象關系框架。它強大且高效的構建具有關系對象持久性和查詢服務的 Java 應用程序。

    • Hibernate 將 Java 類映射到數據庫表中,從 Java 數據類型中映射到 SQL 數據類型中,并把開發人員從 95% 的公共數據持續性編程工作中解放出來。

    • Hibernate 是傳統 Java 對象和數據庫服務器之間的橋梁,用來處理基于 O/R 映射機制和模式的那些對象。

    Mybatis是什么及怎么使用

    Hibernate 優勢
    • Hibernate 使用 XML 文件來處理映射 Java 類別到數據庫表格中,并且不用編寫任何代碼。

    • 為在數據庫中直接儲存和檢索 Java 對象提供簡單的 APIs。

    • 如果在數據庫中或任何其它表格中出現變化,那么僅需要改變 XML 文件屬性。

    • 抽象不熟悉的 SQL 類型,并為我們提供工作中所熟悉的 Java 對象。

    • Hibernate 不需要應用程序服務器來操作。

    • 操控你數據庫中對象復雜的關聯。

    • 最小化與訪問數據庫的智能提取策略。

    • 提供簡單的數據詢問。

    Hibernate劣勢
    • hibernate的完全封裝導致無法使用數據的一些功能。

    • Hibernate的緩存問題。

    • Hibernate對于代碼的耦合度太高。

    • Hibernate尋找bug困難。

    • Hibernate批量數據操作需要大量的內存空間而且執行過程中需要的對象太多

    1.4 JDBCTemplate

    JdbcTemplate針對數據查詢提供了多個重載的模板方法,你可以根據需要選用不同的模板方法.如果你的查詢很簡單,僅僅是傳入相應SQL或者相關參數,然后取得一個單一的結果,那么你可以選擇如下一組便利的模板方法。

    • 優點:運行期:高效、內嵌Spring框架中、支持基于AOP的聲明式事務

    • 缺點:必須于Spring框架結合在一起使用、不支持數據庫跨平臺、默認沒有緩存

    1.5 Mybatis

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

    優點
    1、與JDBC相比,減少了50%的代碼量
    2、 最簡單的持久化框架,簡單易學
    3、SQL代碼從程序代碼中徹底分離出來,可以重用
    4、提供XML標簽,支持編寫動態SQL
    5、提供映射標簽,支持對象與數據庫的ORM字段關系映射
    6、支持緩存、連接池、數據庫移植…

    缺點
    1、SQL語句編寫工作量大,熟練度要高
    2、數據庫移植性比較差,如果需要切換數據庫的話,SQL語句會有很大的差異

    二 MyBatis的配置文件詳解

    2.1 MyBatis日志配置

    導入pom

    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-api</artifactId>
        <version>1.7.30</version>
    </dependency>
    
     <dependency>
         <groupId>ch.qos.logback</groupId>
         <artifactId>logback-classic</artifactId>
         <version>1.2.3</version>
     </dependency>

    添加logback配置文件

    <configuration>
        <!--appender 追加器   日志以哪種方式進行輸出
                name 取個名字
                class 不同實現類會輸出到不同地方
                    ch.qos.logback.core.ConsoleAppender 輸出到控制臺
        -->
        <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
            <encoder>
                <!-- 格式 -->
                <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{100} - %msg%n</pattern>
            </encoder>
        </appender>
    <!--cn.tulingxueyuan.mapper-->
    <!--控制跟細粒度的日志級別  根據包\根據類-->
        <logger name="cn.tulingxueyuan.mapper" level="debug"></logger>
        org.apache.ibatis.transaction
        <!--控制所有的日志級別-->
        <root level="error">
            <!-- 將當前日志級別輸出到哪個追加器上面 -->
            <appender-ref ref="STDOUT" />
        </root>
    </configuration>
    Logger LOGGER= LoggerFactory.getLogger(this.getClass());
    /**
     * 日志級別
     * TRACE < DEBUG < INFO < WARN < ERROR。
     * 1        2       3      4       5
     */
    @Test
    public  void test02(){
        LOGGER.trace("跟蹤級別");
        LOGGER.debug("調試級別");
        LOGGER.info("信息級別");
        LOGGER.warn("警告級別");
        LOGGER.error("異常級別");
    }

    2.2 mybatis-config.xml全局配置文件詳解

    在mybatis的項目中,我們發現了有一個mybatis-config.xml的配置文件,這個配置文件是mybatis的全局配置文件,用來進行相關的全局配置,在任何操作下都生效的配置。下面我們要針對其中的屬性做詳細的解釋,方便大家在后續使用的時候更加熟練。

    官方說明:
    MyBatis 的配置文件包含了會深深影響 MyBatis 行為的設置和屬性信息。 配置文檔的頂層結構如下:

    • configuration(配置)

      • environments(環境配置)

      • environment(環境變量)

      • transactionManager(事務管理器)

      • properties(屬性)

      • settings(設置)

      • typeAliases(類型別名)

      • typeHandlers(類型處理器)

      • objectFactory(對象工廠)

      • plugins(插件)

      • dataSource(數據源)

      • databaseIdProvider(數據庫廠商標識)

      • mappers(映射器)

    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>
        <!--引入外部配置文件,類似于Spring中的property-placeholder
        resource:從類路徑引入
        url:從磁盤路徑或者網絡路徑引入
        -->
        <properties resource="db.properties"></properties>
        <!--用來控制mybatis運行時的行為,是mybatis中的重要配置-->
        <settings>
            <!--設置列名映射的時候是否是駝峰標識-->
            <setting name="mapUnderscoreToCamelCase" value="true"/>
        </settings>
        <!--typeAliases表示為我們引用的實體類起別名,默認情況下我們需要寫類的完全限定名
        如果在此處做了配置,那么可以直接寫類的名稱,在type中配置上類的完全限定名,在使用的時候可以忽略大小寫
        還可以通過alias屬性來表示類的別名
        -->
        <typeAliases>
    <!--        <typeAlias type="cn.tulingxueyuan.bean.Emp" alias="Emp"></typeAlias>-->
            <!--如果需要引用多個類,那么給每一個類起別名肯定會很麻煩,因此可以指定對應的包名,那么默認用的是類名-->
            <package name="cn.tulingxueyuan.bean"/>
        </typeAliases>
        <!--
        在實際的開發過程中,我們可能分為開發環境,生產環境,測試環境等等,每個環境的配置可以是不一樣的
        environment就用來表示不同環境的細節配置,每一個環境中都需要一個事務管理器以及數據源的配置
        我們在后續的項目開發中幾乎都是使用spring中配置的數據源和事務管理器來配置,此處不需要研究
        -->
        <!--default:用來選擇需要的環境-->
        <environments default="development">
            <!--id:表示不同環境的名稱-->
            <environment id="development">
                <transactionManager type="JDBC"/>
                <!--配置數據庫連接-->
                <dataSource type="POOLED">
                    <!--使用${}來引入外部變量-->
                    <property name="driver" value="${driverClassname}"/>
                    <property name="url" value="${url}"/>
                    <property name="username" value="${username}"/>
                    <property name="password" value="${password}"/>
                </dataSource>
            </environment>
        </environments>
        <!--
        在不同的數據庫中,可能sql語句的寫法是不一樣的,為了增強移植性,可以提供不同數據庫的操作實現
        在編寫不同的sql語句的時候,可以指定databaseId屬性來標識當前sql語句可以運行在哪個數據庫中
        -->
        <databaseIdProvider type="DB_VENDOR">
            <property name="MySQL" value="mysql"/>
            <property name="SQL Server" value="sqlserver"/>
            <property name="Oracle" value="orcl"/>
        </databaseIdProvider>
        
        <!--將sql的映射文件適用mappers進行映射-->
        <mappers>
            <!--
            指定具體的不同的配置文件
            class:直接引入接口的全類名,可以將xml文件放在dao的同級目錄下,并且設置相同的文件名稱,同時可以使用注解的方式來進行相關的配置
            url:可以從磁盤或者網絡路徑查找sql映射文件
            resource:在類路徑下尋找sql映射文件
            -->
    <!--        <mapper resource="EmpDao.xml"/>
            <mapper resource="UserDao.xml"/>
            <mapper class="cn.tulingxueyuan.dao.EmpDaoAnnotation"></mapper>-->
            <!--
            當包含多個配置文件或者配置類的時候,可以使用批量注冊的功能,也就是引入對應的包,而不是具體的配置文件或者類
            但是需要注意的是,
            1、如果使用的配置文件的形式,必須要將配置文件跟dao類放在一起,這樣才能找到對應的配置文件.
                如果是maven的項目的話,還需要添加以下配置,原因是maven在編譯的文件的時候只會編譯java文件
                    <build>
                        <resources>
                            <resource>
                                <directory>src/main/java</directory>
                            <includes>
                                <include>**/*.xml</include>
                            </includes>
                        </resource>
                        </resources>
                    </build>
    
            2、將配置文件在resources資源路徑下創建跟dao相同的包名
            -->
            <package name="cn.tulingxueyuan.dao"/>
        </mappers>
    </configuration>

    2.3 Mybatis SQL映射文件詳解

    MyBatis 的真正強大在于它的語句映射,這是它的魔力所在。由于它的異常強大,映射器的 XML 文件就顯得相對簡單。如果拿它跟具有相同功能的 JDBC 代碼進行對比,你會立即發現省掉了將近 95% 的代碼。MyBatis 致力于減少使用成本,讓用戶能更專注于 SQL 代碼。
    SQL 映射文件只有很少的幾個頂級元素(按照應被定義的順序列出):

    • cache &ndash; 該命名空間的緩存配置。

    • cache-ref &ndash; 引用其它命名空間的緩存配置。

    • resultMap &ndash; 描述如何從數據庫結果集中加載對象,是最復雜也是最強大的元素。

    • parameterMap &ndash; 老式風格的參數映射。此元素已被廢棄,并可能在將來被移除!請使用行內參數映射。文檔中不會介紹此元素。

    • sql &ndash; 可被其它語句引用的可重用語句塊。

    • insert &ndash; 映射插入語句。

    • update &ndash; 映射更新語句。

    • delete &ndash; 映射刪除語句。

    • select &ndash; 映射查詢語句。

    在每個頂級元素標簽中可以添加很多個屬性,下面我們開始詳細了解下具體的配置。

    insert、update、delete元素

    屬性描述
    id在命名空間中唯一的標識符,可以被用來引用這條語句。
    parameterType將會傳入這條語句的參數的類全限定名或別名。這個屬性是可選的,因為 MyBatis 可以通過類型處理器(TypeHandler)推斷出具體傳入語句的參數,默認值為未設置(unset)。
    parameterMap用于引用外部 parameterMap 的屬性,目前已被廢棄。請使用行內參數映射和 parameterType 屬性。
    flushCache將其設置為 true 后,只要語句被調用,都會導致本地緩存和二級緩存被清空,默認值:(對 insert、update 和 delete 語句)true。
    timeout這個設置是在拋出異常之前,驅動程序等待數據庫返回請求結果的秒數。默認值為未設置(unset)(依賴數據庫驅動)。
    statementType可選 STATEMENT,PREPARED 或 CALLABLE。這會讓 MyBatis 分別使用 Statement,PreparedStatement 或 CallableStatement,默認值:PREPARED。
    useGeneratedKeys(僅適用于 insert 和 update)這會令 MyBatis 使用 JDBC 的 getGeneratedKeys 方法來取出由數據庫內部生成的主鍵(比如:像 MySQL 和 SQL Server 這樣的關系型數據庫管理系統的自動遞增字段),默認值:false。
    keyProperty(僅適用于 insert 和 update)指定能夠唯一識別對象的屬性,MyBatis 會使用 getGeneratedKeys 的返回值或 insert 語句的 selectKey 子元素設置它的值,默認值:未設置(unset)。如果生成列不止一個,可以用逗號分隔多個屬性名稱。
    keyColumn(僅適用于 insert 和 update)設置生成鍵值在表中的列名,在某些數據庫(像 PostgreSQL)中,當主鍵列不是表中的第一列的時候,是必須設置的。如果生成列不止一個,可以用逗號分隔多個屬性名稱。
    databaseId如果配置了數據庫廠商標識(databaseIdProvider),MyBatis 會加載所有不帶 databaseId 或匹配當前 databaseId 的語句;如果帶和不帶的語句都有,則不帶的會被忽略。
     <!--如果數據庫支持自增可以使用這樣的方式-->
     <insert id="insertUser" useGeneratedKeys="true" keyProperty="id">
        insert into user(user_name) values(#{userName})
     </insert>
     <!--如果數據庫不支持自增的話,那么可以使用如下的方式進行賦值查詢-->
     <insert id="insertUser2" >
         <selectKey order="BEFORE" keyProperty="id" resultType="integer">
            select max(id)+1 from user
         </selectKey>
        insert into user(id,user_name) values(#{id},#{userName})
     </insert>

    更多詳細內容見下邊的第三章節

    三 MyBatis基于XML的詳細使用-參數、返回結果處理

    3.1 參數的取值方式

    在xml文件中編寫sql語句的時候有兩種取值的方式,分別是#{}和${},下面來看一下他們之間的區別:

    <!--獲取參數的方式:
        1.#{} ==> jdbc String sql=" SELECT id,user_name FROM EMP WHERE id=?"
            1.會經過JDBC當中PreparedStatement的預編譯,會根據不同的數據類型來編譯成對應數據庫所對應的數據。
            2.能夠有效的防止SQL注入。 推薦使用!!
            特殊用法:
            自帶很多內置參數的屬性:通常不會使用。了解
            javaType、jdbcType、mode、numericScale、resultMap、typeHandler.
            比如 需要改變默認的NULL===>OTHER:#{id,javaType=NULL}
                 想保留小數點后兩位:#{id,numericScale=2}
    
        2.${} ==> jdbc String sql=" SELECT id,user_name FROM EMP WHERE id="+id
            1.不會進行預編譯,會直接將輸入進來的數據拼接在SQL中。
            2.存在SQL注入的風險。不推薦使用。
            特殊用法:
                1.調試情況下可以臨時使用。
                2.實現一些特殊功能:前提一定要保證數據的安全性。
                 比如:動態表、動態列. 動態SQL.
    -->
    <select id="SelectEmp"  resultType="Emp"  resultMap="emp_map"  >
        SELECT id,user_name,create_date FROM EMP where id=#{id}
    </select>

    3.2 select的參數傳遞

    <!--
       參數傳遞的處理:
       1.單個參數:SelectEmp(Integer id);
           mybatis 不會做任何特殊要求
           獲取方式:
               #:{輸入任何字符獲取參數}
    
       2.多個參數:Emp SelectEmp(Integer id,String username);
           mybatis 會進行封裝
           會將傳進來的參數封裝成map:
           1個值就會對應2個map項 :  id===>  {key:arg0 ,value:id的值},{key:param1 ,value:id的值}
                                     username===>  {key:arg1 ,value:id的值},{key:param2 ,value:id的值}
           獲取方式:
                沒使用了@Param:
                          id=====>  #{arg0} 或者 #{param1}
                    username=====>  #{arg1} 或者 #{param2}
                除了使用這種方式還有別的方式,因為這種方式參數名沒有意義:
                設置參數的別名:@Param(""):SelectEmp(@Param("id") Integer id,@Param("username") String username);
                當使用了@Param:
                          id=====>  #{id} 或者 #{param1}
                    username=====>  #{username} 或者 #{param2}
    
        3. javaBean的參數:
           單個參數:Emp SelectEmp(Emp emp);
           獲取方式:可以直接使用屬性名
               emp.id=====>#{id}
               emp.username=====>#{username}
           多個參數:Emp SelectEmp(Integer num,Emp emp);
               num===>    #{param1} 或者 @Param
               emp===> 必須加上對象別名: emp.id===> #{param2.id} 或者  @Param("emp")Emp emp    ====>#{emp.id}
                                       emp.username===> #{param2.username} 或者  @Param("emp")Emp emp    ====>#{emp.username}
        4.集合或者數組參數:
               Emp SelectEmp(List<String> usernames);
           如果是list,MyBatis會自動封裝為map:
               {key:"list":value:usernames}
                 沒用@Param("")要獲得:usernames.get(0)  =====>  #{list[0]}
                                     :usernames.get(0)  =====>  #{agr0[0]}
                 有@Param("usernames")要獲得:usernames.get(0)  =====>  #{usernames[0]}
                                            :usernames.get(0)  =====>  #{param1[0]}
           如果是數組,MyBatis會自動封裝為map:
               {key:"array":value:usernames}
                 沒用@Param("")要獲得:usernames.get(0)  =====>  #{array[0]}
                                   :usernames.get(0)  =====>  #{agr0[0]}
                 有@Param("usernames")要獲得:usernames.get(0)  =====>  #{usernames[0]}
                                            :usernames.get(0)  =====>  #{param1[0]}
         5.map參數
           和javaBean的參數傳遞是一樣。
           一般情況下:
               請求進來的參數 和pojo對應,就用pojo
               請求進來的參數 沒有和pojo對應,就用map
               請求進來的參數 沒有和pojo對應上,但是使用頻率很高,就用TO、DTO(就是單獨為這些參數創建一個對應的javaBean出來,使參數傳遞更規范、更重用)
    
       --> 
    
       <!--
       接口:SelectEmp(String username,@Param("id") Integer id);
              username====>  #{arg0}  #{param1}
              id====>  #{id}  #{param2}
       接口:SelectEmp(@Param("beginDate") String beginDate,
                        String endDate,
                       Emp emp);
              beginDate====>  #{beginDate}  #{param1}
              endDate====>  #{arg1}  #{param2}
              emp.id====>#{arg2.id}  #{param2.id}
       接口:SelectEmp(List<Integer> ids,
                      String[] usernames,
                      @Param("beginDate") String beginDate,
                        String endDate,);
                   ids.get(0)=====> #{list[0]}      #{param1[0]}
                   usernames[0]=====> #{array[0]}      #{param2[0]}
              beginDate====>  #{beginDate}  #{param3}
              end====>  #{arg3}  #{param4}
       -->

    3.3 處理集合返回結果

    EmpDao.xml

    <!--當返回值的結果是集合的時候,返回值的類型依然寫的是集合中具體的類型-->
        <select id="selectAllEmp" resultType="cn.tulingxueyuan.bean.Emp">
            select  * from emp
        </select>
    <!--在查詢的時候可以設置返回值的類型為map,當mybatis查詢完成之后會把列的名稱作為key
        列的值作為value,轉換到map中
        -->
        <select id="selectEmpByEmpReturnMap" resultType="map">
            select * from emp where empno = #{empno}
        </select>
    
        <!--注意,當返回的結果是一個集合對象的時候,返回值的類型一定要寫集合具體value的類型,
        同時在dao的方法上要添加@MapKey的注解,來設置key是什么結果
        @MapKey("empno")
        Map<Integer,Emp> getAllEmpReturnMap();-->
        <select id="getAllEmpReturnMap" resultType="cn.tulingxueyuan.bean.Emp">
            select * from emp
        </select>

    3.4 自定義結果集&mdash;resultMap

    <!--1.聲明resultMap自定義結果集   resultType 和 resultMap 只能使用一個。
        id 唯一標識, 需要和<select 上的resultMap 進行對應
        type 需要映射的pojo對象, 可以設置別名
        autoMapping 自動映射,(默認=true) 只要字段名和屬性名遵循映射規則就可以自動映射,但是不建議,哪怕屬性名和字段名一一對應上了也要顯示的配置映射
        extends  如果多個resultMap有重復映射,可以聲明父resultMap,將公共的映射提取出來, 可以減少子resultMap的映射冗余
    -->
    <resultMap id="emp_map" type="emp" autoMapping="false" extends="common_map">
        <result column="create_date" property="cjsj"></result>
    </resultMap>
    
    <resultMap id="common_map" type="emp" autoMapping="false" >
        <!-- <id> 主鍵必須使用  對底層存儲有性能作用
                     column  需要映射的數據庫字段名
                     property 需要映射的pojo屬性名
         -->
        <id column="id" property="id"></id>
        <result column="user_name" property="username"></result>
    </resultMap>
    
    <!--2.使用resultMap 關聯 自定義結果集的id-->
    <select id="SelectEmp"  resultType="Emp"  resultMap="emp_map"  >
        SELECT id,user_name,create_date FROM EMP where id=#{id}
    </select>

    四 MyBatis基于XML的詳細使用&mdash;&mdash;高級結果映射

    4.1 聯合查詢

    emp.java

    import java.time.LocalDate;
    
    public class Emp {
        private Integer id;
        private String username;
        private LocalDate createDate;
        private deptId deptId;
    
    
        public Integer getId() {
            return id;
        }
    
        public void setId(Integer id) {
            this.id = id;
        }
    
        public String getUsername() {
            return username;
        }
    
        public void setUsername(String username) {
            this.username = username;
        }
    
        public LocalDate getCreateDate() {
            return createDate;
        }
    
        public void setCreateDate(LocalDate createDate) {
            this.createDate = createDate;
        }
    
        public Integer getDeptId() {
            return dept;
        }
    
        public void setDeptId(Integer deptId) {
            this.dept = dept;
        }
    
        @Override
        public String toString() {
            return "Emp{" +
                    "id=" + id +
                    ", username='" + username + '\'' +
                    ", createDate=" + createDate +
                    ", deptId=" + deptId+
                    '}';
        }
    }

    EmpMapper.xml

    <!-- 實現表聯結查詢的方式:  可以映射: DTO -->
    <resultMap id="QueryEmp_Map" type="QueryEmpDTO">
        <id column="e_id" property="id"></id>
        <result column="user_name" property="username"></result>
        <result column="d_id" property="deptId"></result>
        <result column="dept_name" property="deptName"></result>
    </resultMap>
    
    <select id="QueryEmp"  resultMap="QueryEmp_Map">
        select t1.id as e_id,t1.user_name,t2.id as d_id,t2.dept_name from emp t1
        INNER JOIN dept t2 on t1.dept_id=t2.id
        where t1.id=#{id}
    </select>
    
    <!-- 實現表聯結查詢的方式:  可以映射map -->
    <resultMap id="QueryEmp_Map" type="map">
        <id column="e_id" property="id"></id>
        <result column="user_name" property="username"></result>
        <result column="d_id" property="deptId"></result>
        <result column="dept_name" property="deptName"></result>
    </resultMap>
    
    <select id="QueryEmp"  resultMap="QueryEmp_Map">
        select t1.id as e_id,t1.user_name,t2.id as d_id,t2.dept_name from emp t1
        INNER JOIN dept t2 on t1.dept_id=t2.id
        where t1.id=#{id}
    </select>

    QueryEmpDTO

    public class QueryEmpDTO {
    
        private String deptName;
        private Integer deptId;
        private Integer id;
        private String username;
    
        public String getDeptName() {
            return deptName;
        }
    
        public void setDeptName(String deptName) {
            this.deptName = deptName;
        }
    
        public Integer getDeptId() {
            return deptId;
        }
    
        public void setDeptId(Integer deptId) {
            this.deptId = deptId;
        }
    
        public Integer getId() {
            return id;
        }
    
        public void setId(Integer id) {
            this.id = id;
        }
    
        public String getUsername() {
            return username;
        }
    
        public void setUsername(String username) {
            this.username = username;
        }
    
        @Override
        public String toString() {
            return "QueryEmpDTO{" +
                    "deptName='" + deptName + '\'' +
                    ", deptId=" + deptId +
                    ", id=" + id +
                    ", username='" + username + '\'' +
                    '}';
        }
    }

    Test

    @Test
    public void test01() {
        try(SqlSession sqlSession = sqlSessionFactory.openSession()){
            // Mybatis在getMapper就會給我們創建jdk動態代理
            EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
            QueryEmpDTO dto = mapper.QueryEmp(4);
            System.out.println(dto);
        }
    }

    4.2 嵌套結果

    4.2.1 多對一

    EmpMapper.xml

    <!--嵌套結果   多 對 一  -->
    <resultMap id="QueryEmp_Map2" type="Emp">
        <id column="e_id" property="id"></id>
        <result column="user_name" property="username"></result>
        <!--
        association 實現多對一中的  “一”
            property 指定對象中的嵌套對象屬性
        -->
        <association property="dept">
            <id column="d_id" property="id"></id>
            <id column="dept_name" property="deptName"></id>
        </association>
    </resultMap>
    
    <select id="QueryEmp2"  resultMap="QueryEmp_Map2">
        select t1.id as e_id,t1.user_name,t2.id as d_id,t2.dept_name from emp t1
        INNER JOIN dept t2 on t1.dept_id=t2.id
        where t1.id=#{id}
    </select>
    4.2.2 一對多
    <!-- 嵌套結果: 一對多  查詢部門及所有員工 -->
    <resultMap id="SelectDeptAndEmpsMap" type="Dept">
        <id column="d_id"  property="id"></id>
        <id column="dept_name"  property="deptName"></id>
        <!--
        <collection  映射一對多中的 “多”
            property 指定需要映射的“多”的屬性,一般聲明為List
            ofType  需要指定list的類型
        -->
        <collection property="emps" ofType="Emp" >
            <id column="e_id" property="id"></id>
            <result column="user_name" property="username"></result>
            <result column="create_date" property="createDate"></result>
        </collection>
    </resultMap>
    
    <select id="SelectDeptAndEmps" resultMap="SelectDeptAndEmpsMap">
        select t1.id as d_id,t1.dept_name,t2.id e_id,t2.user_name,t2.create_date from dept t1
        LEFT JOIN emp t2 on t1.id=t2.dept_id
        where t1.id=#{id}
    </select>

    Emp.java

    import java.time.LocalDate;
    
    public class Emp {
        private Integer id;
        private String username;
        private LocalDate createDate;
        private Dept dept;
    
    
        public Integer getId() {
            return id;
        }
    
        public void setId(Integer id) {
            this.id = id;
        }
    
        public String getUsername() {
            return username;
        }
    
        public void setUsername(String username) {
            this.username = username;
        }
    
        public LocalDate getCreateDate() {
            return createDate;
        }
    
        public void setCreateDate(LocalDate createDate) {
            this.createDate = createDate;
        }
    
        public Dept getDept() {
            return dept;
        }
    
        public void setDept(Dept dept) {
            this.dept = dept;
        }
    
        @Override
        public String toString() {
            return "Emp{" +
                    "id=" + id +
                    ", username='" + username + '\'' +
                    ", createDate=" + createDate +
                    ", dept=" + dept +
                    '}';
        }
    }

    Dept.java:

    public class Dept {
        private Integer id;
        private String deptName;
        private List<Emp> emps;
    
        public Integer getId() {
            return id;
        }
    
        public void setId(Integer id) {
            this.id = id;
        }
    
        public String getDeptName() {
            return deptName;
        }
    
        public void setDeptName(String deptName) {
            this.deptName = deptName;
        }
    
        public List<Emp> getEmps() {
            return emps;
        }
    
        public void setEmps(List<Emp> emps) {
            this.emps = emps;
        }
    
    
        @Override
        public String toString() {
            return "Dept{" +
                    "id=" + id +
                    ", deptName='" + deptName + '\'' +
                    ", emps=" + emps +
                    '}';
        }
    }

    EmpMapper.java:

    public interface EmpMapper {
    
        /*徐庶老師實際開發中的實現方式*/
        QueryEmpDTO QueryEmp(Integer id);
    
        /*實用嵌套結果實現聯合查詢  多對一 */
        Emp QueryEmp2(Integer id);
    
    
        /*實用嵌套查詢實現聯合查詢  多對一 */
        Emp QueryEmp3(Integer id);
    }

    DeptMapper.java:

    public interface DeptMapper {
        //嵌套查詢: 一對多   使用部門id查詢員工
       Dept SelectDeptAndEmps(Integer id);
    
       // 嵌套查詢(異步查詢): 一對多  查詢部門及所有員工
        Dept SelectDeptAndEmps2(Integer id);
    }

    4.3 嵌套查詢

    在上述邏輯的查詢中,是由我們自己來完成sql語句的關聯查詢的,那么,我們能讓mybatis幫我們實現自動的關聯查詢嗎?

    4.3.1 多對一

    EmpMapper.xml:

    <!--嵌套查詢(分步查詢)   多 對 一
      聯合查詢和分步查詢區別:   性能區別不大
                                分部查詢支持 懶加載(延遲加載)
       需要設置懶加載,一定要使用嵌套查詢的。
       要啟動懶加載可以在全局配置文件中設置 lazyLoadingEnabled=true
       還可以單獨為某個分步查詢設置立即加載 <association fetchType="eager"
      -->
    <resultMap id="QueryEmp_Map3" type="Emp">
        <id column="id" property="id"></id>
        <result column="user_name" property="username"></result>
        <!-- association 實現多對一中的  “一”
            property 指定對象中的嵌套對象屬性
            column  指定將哪個字段傳到分步查詢中
            select 指定分步查詢的 命名空間+ID
            以上3個屬性是實現分步查詢必須的屬性
            fetchType 可選, eager|lazy   eager立即加載   lazy跟隨全局配置文件中的lazyLoadingEnabled
         -->
        <association property="dept"    column="dept_id"  select="cn.tulingxueyuan.mapper.DeptMapper.SelectDept">
        </association>
    </resultMap>
    
    <select id="QueryEmp3"  resultMap="QueryEmp_Map3">
       select  * from emp where id=#{id}
    </select>

    DeptMapper.xml

    <!-- 根據部門id查詢部門-->
    <select id="SelectDept" resultType="dept">
        SELECT * FROM dept where id=#{id}
    </select>
    4.3.2 一對多

    DeptMapper.xml

    <!-- 嵌套查詢(異步查詢): 一對多  查詢部門及所有員工 -->
    <resultMap id="SelectDeptAndEmpsMap2" type="Dept">
        <id column="d_id"  property="id"></id>
        <id column="dept_name"  property="deptName"></id>
        <!--
        <collection  映射一對多中的 “多”
            property 指定需要映射的“多”的屬性,一般聲明為List
            ofType  需要指定list的類型
            column 需要將當前查詢的字段傳遞到異步查詢的參數
            select 指定異步查詢
        -->
        <collection property="emps" ofType="Emp" column="id" select="cn.tulingxueyuan.mapper.EmpMapper.SelectEmpByDeptId" >
        </collection>
    </resultMap>
    
    <select id="SelectDeptAndEmps2" resultMap="SelectDeptAndEmpsMap2">
        SELECT * FROM dept where id=#{id}
    </select>

    EmpMapper.xml

    <!-- 根據部門id所有員工 -->
    <select id="SelectEmpByDeptId"  resultType="emp">
        select  * from emp where dept_id=#{id}
    </select>

    Emp.java

    public class Emp {
        private Integer id;
        private String username;
        private LocalDate createDate;
        private Dept dept;
    
    
        public Integer getId() {
            return id;
        }
    
        public void setId(Integer id) {
            this.id = id;
        }
    
        public String getUsername() {
            return username;
        }
    
        public void setUsername(String username) {
            this.username = username;
        }
    
        public LocalDate getCreateDate() {
            return createDate;
        }
    
        public void setCreateDate(LocalDate createDate) {
            this.createDate = createDate;
        }
    
        public Dept getDept() {
            return dept;
        }
    
        public void setDept(Dept dept) {
            this.dept = dept;
        }
    
        @Override
        public String toString() {
            return "Emp{" +
                    "id=" + id +
                    ", username='" + username + '\'' +
                    ", createDate=" + createDate +
                    ", dept=" + dept +
                    '}';
        }
    
    }

    Dept.java:

    public class Dept {
        private Integer id;
        private String deptName;
        private List<Emp> emps;
    
        public Integer getId() {
            return id;
        }
    
        public void setId(Integer id) {
            this.id = id;
        }
    
        public String getDeptName() {
            return deptName;
        }
    
        public void setDeptName(String deptName) {
            this.deptName = deptName;
        }
    
        public List<Emp> getEmps() {
            return emps;
        }
    
        public void setEmps(List<Emp> emps) {
            this.emps = emps;
        }
    
    
        @Override
        public String toString() {
            return "Dept{" +
                    "id=" + id +
                    ", deptName='" + deptName + '\'' +
                    ", emps=" + emps +
                    '}';
        }
    }

    EmpMapper.java:

    public interface EmpMapper {
    
        /*徐庶老師實際開發中的實現方式*/
        QueryEmpDTO QueryEmp(Integer id);
    
        /*實用嵌套結果實現聯合查詢  多對一 */
        Emp QueryEmp2(Integer id);
    
    
        /*實用嵌套查詢實現聯合查詢  多對一 */
        Emp QueryEmp3(Integer id);
    }

    DeptMapper.java:

    public interface DeptMapper {
        //嵌套查詢: 一對多   使用部門id查詢員工
       Dept SelectDeptAndEmps(Integer id);
    
       // 嵌套查詢(異步查詢): 一對多  查詢部門及所有員工
        Dept SelectDeptAndEmps2(Integer id);
    }

    4.4 延遲查詢

    當我們在進行表關聯的時候,有可能在查詢結果的時候不需要關聯對象的屬性值(select count(1)&hellip;),那么此時可以通過延遲加載來實現功能。在全局配置文件中添加如下屬性
    mybatis-config.xml

    <!-- 開啟延遲加載,所有分步查詢都是懶加載 (默認是立即加載)-->
    <setting name="lazyLoadingEnabled" value="true"/>
    <!--當開啟式, 使用pojo中任意屬性都會加載延遲查詢 ,默認是false
    <setting name="aggressiveLazyLoading" value="false"/>-->
    <!--設置對象的哪些方法調用會加載延遲查詢   默認:equals,clone,hashCode,toString-->
    <setting name="lazyLoadTriggerMethods" value=""/>

    如果設置了全局加載,但是希望在某一個sql語句查詢的時候不使用延時策略,可以添加fetchType下屬性:

    <association property="dept" fetchType="eager"  column="dept_id"  select="cn.tulingxueyuan.mapper.DeptMapper.SelectDept">
    </association>

    4.5 總結

    Mybatis是什么及怎么使用

    三種關聯關系都有兩種關聯查詢的方式: 嵌套查詢,嵌套結果
    Mybatis的延遲加載配置, 在全局配置文件中加入下面代碼

    <settings>
    	<setting name=”lazyLoadingEnabled” value=”true” />
    	<setting name=”aggressiveLazyLoading” value=”false”/>
    </settings>

    在映射文件中,元素和元素中都已默認配置了延遲加載屬性,即默認屬性fetchType=”lazy”(屬性fetchType=”eager”表示立即加載),所以在配置文件中開啟延遲加載后,無需在映射文件中再做配置

    一對一

    使用元素進行一對一關聯映射非常簡單,只需要參考如下兩種示例配置即可

    Mybatis是什么及怎么使用

    一對多

    <resultMap>元素中,包含了一個<collection>子元素,MyBatis就是通過該元素來處理一對多關聯關系的
    <collection>子元素的屬性大部分與<association>元素相同,但其還包含一個特殊屬性&ndash;ofType
    ofType屬性與javaType屬性對應,它用于指定實體對象中集合類屬性所包含的元素類型。
    <collection >元素的使用也非常簡單,同樣可以參考如下兩種示例進行配置,具體代碼如下:

    Mybatis是什么及怎么使用

    多對多

    多對多的關聯關系查詢,同樣可以使用前面介紹的元素進行處理(其用法和一對多關聯關系查詢語句用法基本相同)

    五 動態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

    • bind

    • sql片段

    5.1 if

    EmpDao.xml

    <select id="getEmpByCondition" resultType="cn.tulingxueyuan.bean.Emp">
        select * from emp where 
        <if test="empno!=null">
            empno = #{empno} and
        </if>
        <if test="ename!=null">
            ename like #{ename} and
        </if>
        <if test="sal!=null">
            sal > #{sal}
        </if>
    </select>

    上邊代碼看起來是比較正常的,但是大家需要注意的是如果我們傳入的參數值有缺失會怎么呢?這個時候拼接的sql語句就會變得有問題,例如不傳參數或者丟失最后一個參數,那么語句中就會多一個where或者and的關鍵字,因此在mybatis中也給出了具體的解決方案:

    where

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

    <select id="getEmpByCondition" resultType="cn.tulingxueyuan.bean.Emp">
        select * from emp
        <where>
            <if test="empno!=null">
                empno = #{empno}
            </if>
            <if test="ename!=null">
                and ename like #{ename}
            </if>
            <if test="sal!=null">
                and sal > #{sal}
            </if>
        </where>
    </select>

    現在看起來沒有什么問題了,但是我們的條件添加到了拼接sql語句的前后,那么我們該如何處理呢?

    trim

     <!--
     trim截取字符串:
     prefix:前綴,為sql整體添加一個前綴
     prefixOverrides:去除整體字符串前面多余的字符
     suffixOverrides:去除后面多余的字符串
     -->
     <select id="getEmpByCondition" resultType="cn.tulingxueyuan.bean.Emp">
         select * from emp
    
         <trim prefix="where" prefixOverrides="and" suffixOverrides="and">
             <if test="empno!=null">
                 empno = #{empno} and
             </if>
             <if test="ename!=null">
                 ename like #{ename} and
             </if>
             <if test="sal!=null">
                 sal > #{sal} and
             </if>
         </trim>
     </select>

    5.2 foreach

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

     <!--foreach是對集合進行遍歷
    	 collection="deptnos" 指定要遍歷的集合
    	 close="" 表示以什么結束
    	 index="" 給定一個索引值
    	 item="" 遍歷的每一個元素的值
    	 open="" 表示以什么開始
    	 separator="" 表示多個元素的分隔符
     -->
     <select id="getEmpByDeptnos" resultType="Emp">
        select * from emp where deptno in
         <foreach collection="deptnos" close=")" index="idx" item="deptno" open="(" separator=",">
            #{deptno}
         </foreach>
     </select>

    5.3 choose、when、otherwise

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

    <select id="getEmpByConditionChoose" resultType="cn.tulingxueyuan.bean.Emp">
            select * from emp
         <where>
             <choose>
                 <when test="empno!=null">
                     empno > #{empno}
                 </when>
                 <when test="ename!=null">
                     ename like #{ename}
                 </when>
                 <when test="sal!=null">
                     sal > #{sal}
                 </when>
                 <otherwise>
                     1=1
                 </otherwise>
             </choose>
         </where>
     </select>

    5.4 set

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

    <update id="updateEmpByEmpno">
      update emp
       <set>
           <if test="empno!=null">
              empno=#{empno},
           </if>
           <if test="ename!=null">
              ename = #{ename},
           </if>
           <if test="sal!=null">
              sal = #{sal}
           </if>
       </set>
       <where>
          empno = #{empno}
       </where>
    </update>

    5.5 bind

    bind 元素允許你在 OGNL 表達式以外創建一個變量,并將其綁定到當前的上下文。比如:

    <select id="selectBlogsLike" resultType="Blog">
      <bind name="pattern" value="'%' + _parameter.getTitle() + '%'" />
      SELECT * FROM BLOG
      WHERE title LIKE #{pattern}
    </select>

    5.6 sql

    這個元素可以用來定義可重用的 SQL 代碼片段,以便在其它語句中使用。 參數可以靜態地(在加載的時候)確定下來,并且可以在不同的 include 元素中定義不同的參數值。比如:

    <sql id="userColumns"> ${alias}.id,${alias}.username,${alias}.password </sql>

    這個 SQL 片段可以在其它語句中使用,例如:

    <select id="selectUsers" resultType="map">
      select
        <include refid="userColumns"><property name="alias" value="t1"/></include>,
        <include refid="userColumns"><property name="alias" value="t2"/></include>
      from some_table t1
        cross join some_table t2
    </select>

    六 MyBatis緩存

    6.1 緩存介紹

    MyBatis 內置了一個強大的事務性查詢緩存機制,它可以非常方便地配置和定制。 為了使它更加強大而且易于配置,我們對 MyBatis 3 中的緩存實現進行了許多改進。
    默認情況下,只啟用了本地的會話緩存,它僅僅對一個會話中的數據進行緩存。 要啟用全局的二級緩存,只需要在你的 SQL 映射文件中添加一行:

    <cache/>

    當添加上該標簽之后,會有如下效果:

    • 映射語句文件中的所有 select 語句的結果將會被緩存。

    • 映射語句文件中的所有 insert、update 和 delete 語句會刷新緩存。

    • 緩存會使用最近最少使用算法(LRU, Least Recently Used)算法來清除不需要的緩存。

    • 緩存不會定時進行刷新(也就是說,沒有刷新間隔)。

    • 緩存會保存列表或對象(無論查詢方法返回哪種)的 1024 個引用。

    • 緩存會被視為讀/寫緩存,這意味著獲取到的對象并不是共享的,可以安全地被調用者修改,而不干擾其他調用者或線程所做的潛在修改。

    在進行配置的時候還會分為一級緩存和二級緩存:

    • 一級緩存:線程級別的緩存,是本地緩存,sqlSession級別的緩存

    • 二級緩存:全局范圍的緩存,不止局限于當前會話

    6.2 一級緩存的使用

    一級緩存是sqlsession級別的緩存,默認是存在的。在下面的案例中,大家發現我發送了兩個相同的請求,但是sql語句僅僅執行了一次,那么就意味著第一次查詢的時候已經將結果進行了緩存。

    @Test
    public void test01() {
    
         SqlSession sqlSession = sqlSessionFactory.openSession();
         try {
             EmpDao mapper = sqlSession.getMapper(EmpDao.class);
             List<Emp> list = mapper.selectAllEmp();
             for (Emp emp : list) {
                 System.out.println(emp);
            }
             System.out.println("--------------------------------");
             List<Emp> list2 = mapper.selectAllEmp();
             for (Emp emp : list2) {
                 System.out.println(emp);
            }
        } catch (Exception e) {
             e.printStackTrace();
        } finally {
             sqlSession.close();
        }
    }

    在大部分的情況下一級緩存是可以的,但是有幾種特殊的情況會造成一級緩存失效:

    1、一級緩存是sqlSession級別的緩存,如果在應用程序中只有開啟了多個sqlsession,那么會造成緩存失效

     @Test
     public void test02(){
         SqlSession sqlSession = sqlSessionFactory.openSession();
         EmpDao mapper = sqlSession.getMapper(EmpDao.class);
         List<Emp> list = mapper.selectAllEmp();
         for (Emp emp : list) {
             System.out.println(emp);
        }
         System.out.println("================================");
         SqlSession sqlSession2 = sqlSessionFactory.openSession();
         EmpDao mapper2 = sqlSession2.getMapper(EmpDao.class);
         List<Emp> list2 = mapper2.selectAllEmp();
         for (Emp emp : list2) {
             System.out.println(emp);
        }
         sqlSession.close();
         sqlSession2.close();
    }

    2、在編寫查詢的sql語句的時候,一定要注意傳遞的參數,如果參數不一致,那么也不會緩存結果
    3、如果在發送過程中發生了數據的修改,那么結果就不會緩存

    @Test
    public void test03(){
        SqlSession sqlSession = sqlSessionFactory.openSession();
        EmpDao mapper = sqlSession.getMapper(EmpDao.class);
        Emp empByEmpno = mapper.findEmpByEmpno(1111);
        System.out.println(empByEmpno);
        System.out.println("================================");
        empByEmpno.setEname("zhangsan");
        int i = mapper.updateEmp(empByEmpno);
        System.out.println(i);
        System.out.println("================================");
        Emp empByEmpno1 = mapper.findEmpByEmpno(1111);
        System.out.println(empByEmpno1);
        sqlSession.close();
    }

    4、在兩次查詢期間,手動去清空緩存,也會讓緩存失效

    @Test
    public void test03(){
        SqlSession sqlSession = sqlSessionFactory.openSession();
        EmpDao mapper = sqlSession.getMapper(EmpDao.class);
        Emp empByEmpno = mapper.findEmpByEmpno(1111);
        System.out.println(empByEmpno);
        System.out.println("================================");
        System.out.println("手動清空緩存");
        sqlSession.clearCache();
        System.out.println("================================");
        Emp empByEmpno1 = mapper.findEmpByEmpno(1111);
        System.out.println(empByEmpno1);
        sqlSession.close();
    }

    特性

    一級緩存特性:

    1. 默認就開啟了,也可以關閉一級緩存 localCacheScope=STATEMENT

    2. 作用域:是基于sqlSession(默認),一次數據庫操作會話。

    3. 緩存默認實現類PerpetualCache ,使用map進行存儲的

    4. 查詢完就會進行存儲

    5. 先從二級緩存中獲取,再從一級緩存中獲取 key==> sqlid+sql

    一級緩存失效情況:

    1. 不同的sqlSession會使一級緩存失效

    2. 同一個SqlSession,但是查詢語句不一樣

    3. 同一個SqlSession,查詢語句一樣,期間執行增刪改操作

    4. 同一個SqlSession,查詢語句一樣,執行手動清除緩存

    6.3 二級緩存的使用

    二級緩存是全局作用域緩存,默認是不開啟的,需要手動進行配置。
    Mybatis提供二級緩存的接口以及實現,緩存實現的時候要求實體類實現Serializable接口,二級緩存在sqlSession關閉或提交之后才會生效。

    二級緩存的使用

    步驟:
    1、全局配置文件中添加如下配置:

    <setting name="cacheEnabled" value="true"/>

    2、需要在使用二級緩存的映射文件處使用標簽標注
    3、實體類必須要實現Serializable接口

    @Test
    public void test04(){
        SqlSession sqlSession = sqlSessionFactory.openSession();
        SqlSession sqlSession2 = sqlSessionFactory.openSession();
        EmpDao mapper = sqlSession.getMapper(EmpDao.class);
        EmpDao mapper2 = sqlSession2.getMapper(EmpDao.class);
        Emp empByEmpno = mapper.findEmpByEmpno(1111);
        System.out.println(empByEmpno);
        sqlSession.close();
    
        Emp empByEmpno1 = mapper2.findEmpByEmpno(1111);
        System.out.println(empByEmpno1);
        sqlSession2.close();
    }

    緩存的屬性

    • eviction:表示緩存回收策略,默認是

      • LRU LRU:最近最少使用的,移除最長時間不被使用的對象

      • FIFO:先進先出,按照對象進入緩存的順序來移除

      • SOFT:軟引用,移除基于垃圾回收器狀態和軟引用規則的對象

      • WEAK:弱引用,更積極地移除基于垃圾收集器狀態和弱引用規則的對象

    • flushInternal:刷新間隔,單位毫秒

      • 默認情況是不設置,也就是沒有刷新間隔,緩存僅僅調用語句時刷新

    • size:引用數目,正整數

      • 代表緩存最多可以存儲多少個對象,太大容易導致內存溢出

    • readonly:只讀,true/false

      • true:只讀緩存,會給所有調用這返回緩存對象的相同實例,因此這些對象不能被修改。

      • false:讀寫緩存,會返回緩存對象的拷貝(序列化實現),這種方式比較安全,默認值

    //可以看到會去二級緩存中查找數據,而且二級緩存跟一級緩存中不會同時存在數據,因為二級緩存中的數據是在sqlsession 關閉之后才生效的
    @Test
    public void test05(){
        SqlSession sqlSession = sqlSessionFactory.openSession();
        EmpDao mapper = sqlSession.getMapper(EmpDao.class);
        Emp empByEmpno = mapper.findEmpByEmpno(1111);
        System.out.println(empByEmpno);
        sqlSession.close();
    
        SqlSession sqlSession2 = sqlSessionFactory.openSession();
        EmpDao mapper2 = sqlSession2.getMapper(EmpDao.class);
        Emp empByEmpno2 = mapper2.findEmpByEmpno(1111);
        System.out.println(empByEmpno2);
        Emp empByEmpno3 = mapper2.findEmpByEmpno(1111);
        System.out.println(empByEmpno3);
        sqlSession2.close();
    }

    緩存查詢的順序是先查詢二級緩存再查詢一級緩存

    @Test
    public void test05(){
         SqlSession sqlSession = sqlSessionFactory.openSession();
         EmpDao mapper = sqlSession.getMapper(EmpDao.class);
         Emp empByEmpno = mapper.findEmpByEmpno(1111);
         System.out.println(empByEmpno);
         sqlSession.close();
    
         SqlSession sqlSession2 = sqlSessionFactory.openSession();
         EmpDao mapper2 = sqlSession2.getMapper(EmpDao.class);
         Emp empByEmpno2 = mapper2.findEmpByEmpno(1111);
         System.out.println(empByEmpno2);
         Emp empByEmpno3 = mapper2.findEmpByEmpno(1111);
         System.out.println(empByEmpno3);
    
         Emp empByEmpno4 = mapper2.findEmpByEmpno(7369);
         System.out.println(empByEmpno4);
         Emp empByEmpno5 = mapper2.findEmpByEmpno(7369);
         System.out.println(empByEmpno5);
         sqlSession2.close();
    }

    二級緩存的作用范圍

    如果設置了全局的二級緩存配置,那么在使用的時候需要注意,在每一個單獨的select語句中,可以設置將查詢緩存關閉,以完成特殊的設置

    1、在setting中設置,是配置二級緩存開啟,一級緩存默認一直開啟

    <setting name="cacheEnabled" value="true"/>

    2、select標簽的useCache屬性:

    在每一個select的查詢中可以設置當前查詢是否要使用二級緩存,只對二級緩存有效

    3、sql標簽的flushCache屬性

    增刪改操作默認值為true,sql執行之后會清空一級緩存和二級緩存,而查詢操作默認是false

    4、sqlSession.clearCache()

    只是用來清除一級緩存

    二級緩存特性

    • 默認開啟了,沒有實現

    • 作用域:基于全局范圍,應用級別。

    • 緩存默認實現類PerpetualCache ,使用map進行存儲的但是二級緩存根據不同的mapper命名空間多包了一層map

     : org.apache.ibatis.session.Configuration#caches    key:mapper命名空間   value:erpetualCache.map
     key==> sqlid+sql
    • 事務提交的時候(sqlSession關閉)

    • 先從二級緩存中獲取,再從一級緩存中獲取

    實現:

    1. 開啟二級緩存<setting name="cacheEnabled" value="true"/>

    2. 在需要使用到二級緩存的映射文件中加入,基于Mapper映射文件來實現緩存的,基于Mapper映射文件的命名空間來存儲的

    3. 在需要使用到二級緩存的javaBean中實現序列化接口implements Serializable 配置成功就會出現緩存命中率 同一個sqlId: 從緩存中拿出的次數/查詢總次數

    失效:

    1. 同一個命名空間進行了增刪改的操作,會導致二級緩存失效 但是如果不想失效:可以將SQL的flushCache 這是為false,但是要慎重設置,因為會造成數據臟讀問題,除非你能保證查詢的數據永遠不會執行增刪改

    2. 讓查詢不緩存數據到二級緩存中useCache=“false”

    3. 如果希望其他mapper映射文件的命名空間執行了增刪改清空另外的命名空間就可以設置:

    <cache-ref namespace="cn.tulingxueyuan.mapper.DeptMapper"/>

    6.4 整合第三方緩存

    整合redis

    添加redis-mybatis 緩存適配器 依賴

    <dependencies>
        <!--添加依賴-->
        <dependency>
            <groupId>org.mybatis.caches</groupId>
            <artifactId>mybatis-redis</artifactId>
            <version>1.0.0-beta2</version>
        </dependency>
    </dependencies>

    添加redis.properties在resources根目錄

    host=localhost
    port=6379
    connectionTimeout=5000
    soTimeout=5000
    password=無密碼可不填
    database=0
    clientName=

    設置mybatis二級緩存實現類

    <cache ... type="org.mybatis.caches.redis.RedisCache" ....../>

    整合ehcache

    導入對應的maven依賴

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

    導入ehcache配置文件

    <?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="D:\ehcache" />
    
    <defaultCache
      maxElementsInMemory="1"
      maxElementsOnDisk="10000000"
      eternal="false"
      overflowToDisk="true"
      timeToIdleSeconds="120"
      timeToLiveSeconds="120"
      diskExpiryThreadIntervalSeconds="120"
      memoryStoreEvictionPolicy="LRU">
    </defaultCache>
    </ehcache>
    
    <!--
    屬性說明:
    l diskStore:指定數據在磁盤中的存儲位置。
    l defaultCache:當借助CacheManager.add("demoCache")創建Cache時,EhCache便會采用<defalutCache/>指定的的管理策略
    
    以下屬性是必須的:
    l maxElementsInMemory - 在內存中緩存的element的最大數目
    l maxElementsOnDisk - 在磁盤上緩存的element的最大數目,若是0表示無窮大
    l eternal - 設定緩存的elements是否永遠不過期。如果為true,則緩存的數據始終有效,如果為false那么還要根據timeToIdleSeconds,timeToLiveSeconds判斷
    l overflowToDisk - 設定當內存緩存溢出的時候是否將過期的element緩存到磁盤上
    
    以下屬性是可選的:
    l timeToIdleSeconds - 當緩存在EhCache中的數據前后兩次訪問的時間超過timeToIdleSeconds的屬性取值時,這些數據便會刪除,默認值是0,也就是可閑置時間無窮大
    l timeToLiveSeconds - 緩存element的有效生命期,默認是0.,也就是element存活時間無窮大
    diskSpoolBufferSizeMB 這個參數設置DiskStore(磁盤緩存)的緩存區大小.默認是30MB.每個Cache都應該有自己的一個緩沖區.
    l diskPersistent - 在VM重啟的時候是否啟用磁盤保存EhCache中的數據,默認是false。
    l diskExpiryThreadIntervalSeconds - 磁盤緩存的清理線程運行間隔,默認是120秒。每個120s,相應的線程會進行一次EhCache中數據的清理工作
    l memoryStoreEvictionPolicy - 當內存緩存達到最大,有新的element加入的時候, 移除緩存中element的策略。默認是LRU(最近最少使用),可選的有LFU(最不常使用)和FIFO(先進先出)
    -->

    在mapper文件中添加自定義緩存

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

    七 MyBatis分頁插件&逆向工程

    7.1 分頁插件

    MyBatis 通過提供插件機制,讓我們可以根據自己的需要去增強MyBatis 的功能。需要注意的是,如果沒有完全理解MyBatis 的運行原理和插件的工作方式,最好不要使用插件,因為它會改變系底層的工作邏輯,給系統帶來很大的影響。
      MyBatis 的插件可以在不修改原來的代碼的情況下,通過攔截的方式,改變四大核心對象的行為,比如處理參數,處理SQL,處理結果。

    Mybatis插件典型適用場景

    分頁功能
    mybatis的分頁默認是基于內存分頁的(查出所有,再截取),數據量大的情況下效率較低,不過使用mybatis插件可以改變該行為,只需要攔截StatementHandler類的prepare方法,改變要執行的SQL語句為分頁語句即可;

    公共字段統一賦值
    一般業務系統都會有創建者,創建時間,修改者,修改時間四個字段,對于這四個字段的賦值,實際上可以在DAO層統一攔截處理,可以用mybatis插件攔截Executor類的update方法,對相關參數進行統一賦值即可;

    性能監控
    對于SQL語句執行的性能監控,可以通過攔截Executor類的update, query等方法,用日志記錄每個方法執行的時間;

    其它
    其實mybatis擴展性還是很強的,基于插件機制,基本上可以控制SQL執行的各個階段,如執行階段,參數處理階段,語法構建階段,結果集處理階段,具體可以根據項目業務來實現對應業務邏輯。

    實現思考:
    第一個問題:
      不修改對象的代碼,怎么對對象的行為進行修改,比如說在原來的方法前面做一點事情,在原來的方法后面做一點事情?
      答案:大家很容易能想到用代理模式,這個也確實是MyBatis 插件的原理。
      
    第二個問題:
      我們可以定義很多的插件,那么這種所有的插件會形成一個鏈路,比如我們提交一個休假申請,先是項目經理審批,然后是部門經理審批,再是HR 審批,再到總經理審批,怎么實現層層的攔截?
      答案:插件是層層攔截的,我們又需要用到另一種設計模式&mdash;&mdash;責任鏈模式。
      
      在之前的源碼中我們也發現了,mybatis內部對于插件的處理確實使用的代理模式,既然是代理模式,我們應該了解MyBatis 允許哪些對象的哪些方法允許被攔截,并不是每一個運行的節點都是可以被修改的。只有清楚了這些對象的方法的作用,當我們自己編寫插件的時候才知道從哪里去攔截。在MyBatis 官網有答案,我們來看一下:https://mybatis.org/mybatis-3/zh/configuration.html#plugins

    Mybatis是什么及怎么使用

    Executor 會攔截到CachingExcecutor 或者BaseExecutor。因為創建Executor 時是先創建CachingExcecutor,再包裝攔截。從代碼順序上能看到。我們可以通過mybatis的分頁插件來看看整個插件從包裝攔截器鏈到執行攔截器鏈的過程。
      在查看插件原理的前提上,我們需要來看看官網對于自定義插件是怎么來做的,官網上有介紹:通過 MyBatis 提供的強大機制,使用插件是非常簡單的,只需實現 Interceptor 接口,并指定想要攔截的方法簽名即可。這里本人踩了一個坑,在Springboot中集成,同時引入了pagehelper-spring-boot-starter 導致RowBounds參數的值被刷掉了,也就是走到了我的攔截其中沒有被設置值,這里需要注意,攔截器出了問題,可以Debug看一下Configuration配置類中攔截器鏈的包裝情況。

    自定義分頁插件

    @Intercepts({
            @Signature(type = Executor.class,method = "query" ,args ={MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class} ), // 需要代理的對象和方法
            @Signature(type = Executor.class,method = "query" ,args ={MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class, CacheKey.class, BoundSql.class} ) // 需要代理的對象和方法
    })
    public class MyPageInterceptor implements Interceptor {
    
        @Override
        public Object intercept(Invocation invocation) throws Throwable {
            System.out.println("簡易版的分頁插件:邏輯分頁改成物理分頁");
    
            // 修改sql 拼接Limit 0,10
            Object[] args = invocation.getArgs();
            // MappedStatement 對mapper映射文件里面元素的封裝
            MappedStatement ms= (MappedStatement) args[0];
            // BoundSql 對sql和參數的封裝
            Object parameterObject=args[1];
            BoundSql boundSql = ms.getBoundSql(parameterObject);
            // RowBounds 封裝了邏輯分頁的參數 :當前頁offset,一頁數limit
            RowBounds rowBounds= (RowBounds) args[2];
    
            // 拿到原來的sql語句
            String sql = boundSql.getSql();
            String limitSql=sql+ " limit "+rowBounds.getOffset()+","+ rowBounds.getLimit();
    
            //將分頁sql重新封裝一個BoundSql 進行后續執行
            BoundSql pageBoundSql = new BoundSql(ms.getConfiguration(), limitSql, boundSql.getParameterMappings(), parameterObject);
    
            // 被代理的對象
            Executor executor= (Executor) invocation.getTarget();
            CacheKey cacheKey = executor.createCacheKey(ms, parameterObject, rowBounds, pageBoundSql);
            // 調用修改過后的sql繼續執行查詢
            return  executor.query(ms,parameterObject,rowBounds, (ResultHandler) args[3],cacheKey,pageBoundSql);
        }
    }

    攔截簽名跟參數的順序有嚴格要求,如果按照順序找不到對應方法會拋出異常:

    org.apache.ibatis.exceptions.PersistenceException:
                ### Error opening session.  Cause: org.apache.ibatis.plugin.PluginException: 
                Could not find method on interface org.apache.ibatis.executor.Executor named queryv

    MyBatis 啟動時掃描 標簽, 注冊到Configuration 對象的 InterceptorChain 中。property 里面的參數,會調用setProperties()方法處理。

    分頁插件使用
    添加pom依賴:

    <dependency>
    	<groupId>com.github.pagehelper</groupId>
    	<artifactId>pagehelper</artifactId>
    	<version>1.2.15</version>
    </dependency>

    插件注冊,在mybatis-config.xml 中注冊插件:

    <configuration>
    
    	<plugins>
    		<!-- com.github.pagehelper為PageHelper類所在包名 -->
    		<plugin interceptor="com.github.pagehelper.PageHelper">
    			<property name="helperDialect" value="mysql" />
    			<!-- 該參數默認為false -->
    			<!-- 設置為true時,會將RowBounds第一個參數offset當成pageNum頁碼使用 -->
    			<!-- 和startPage中的pageNum效果一樣 -->
    			<property name="offsetAsPageNum" value="true" />
    			<!-- 該參數默認為false -->
    			<!-- 設置為true時,使用RowBounds分頁會進行count查詢 -->
    			<property name="rowBoundsWithCount" value="true" />
    			<!-- 設置為true時,如果pageSize=0或者RowBounds.limit = 0就會查詢出全部的結果 -->
    			<!-- (相當于沒有執行分頁查詢,但是返回結果仍然是Page類型) -->
    			<property name="pageSizeZero" value="true" />
    			<!-- 3.3.0版本可用 - 分頁參數合理化,默認false禁用 -->
    			<!-- 啟用合理化時,如果pageNum<1會查詢第一頁,如果pageNum>pages會查詢最后一頁 -->
    			<!-- 禁用合理化時,如果pageNum<1或pageNum>pages會返回空數據 -->
    			<property name="reasonable" value="true" />
    			<!-- 3.5.0版本可用 - 為了支持startPage(Object params)方法 -->
    			<!-- 增加了一個`params`參數來配置參數映射,用于從Map或ServletRequest中取值 -->
    			<!-- 可以配置pageNum,pageSize,count,pageSizeZero,reasonable,不配置映射的用默認值 -->
    			<!-- 不理解該含義的前提下,不要隨便復制該配置 -->
    			<property name="params" value="pageNum=start;pageSize=limit;" />
    		</plugin>
    	</plugins>
    </configuration>

    調用

    // 獲取配置文件
    InputStream inputStream = Resources.getResourceAsStream("mybatis/mybatis-config.xml");
    // 通過加載配置文件獲取SqlSessionFactory對象
    SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(inputStream);
    try (SqlSession sqlSession = sqlSessionFactory.openSession()) {
        // Mybatis在getMapper就會給我們創建jdk動態代理
        EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
        PageHelper.startPage(1, 5);
        List<Emp> list=mapper.selectAll(); 
        PageInfo<ServiceStation> info = new PageInfo<ServiceStation>(list, 3);                   
              System.out.println("當前頁碼:"+info.getPageNum());
              System.out.println("每頁的記錄數:"+info.getPageSize());
              System.out.println("總記錄數:"+info.getTotal());
              System.out.println("總頁碼:"+info.getPages());
              System.out.println("是否第一頁:"+info.isIsFirstPage());
              System.out.println("連續顯示的頁碼:");
              int[] nums = info.getNavigatepageNums();
              for (int i = 0; i < nums.length; i++) {
                   System.out.println(nums[i]);
              }     
    }

    代理和攔截是怎么實現的?
      上面提到的可以被代理的四大對象都是什么時候被代理的呢?Executor 是openSession() 的時候創建的; StatementHandler 是SimpleExecutor.doQuery()創建的;里面包含了處理參數的ParameterHandler 和處理結果集的ResultSetHandler 的創建,創建之后即調用InterceptorChain.pluginAll(),返回層層代理后的對象。代理是由Plugin 類創建。在我們重寫的 plugin() 方法里面可以直接調用returnPlugin.wrap(target, this);返回代理對象。
      單個插件的情況下,代理能不能被代理?代理順序和調用順序的關系? 可以被代理。

    Mybatis是什么及怎么使用

    因為代理類是Plugin,所以最后調用的是Plugin 的invoke()方法。它先調用了定義的攔截器的intercept()方法。可以通過invocation.proceed()調用到被代理對象被攔截的方法。

    Mybatis是什么及怎么使用

    調用流程時序圖:

    Mybatis是什么及怎么使用

    PageHelper 原理
      先來看一下分頁插件的簡單用法:

    PageHelper.startPage(1, 3);
    List<Blog> blogs = blogMapper.selectBlogById2(blog);
    PageInfo page = new PageInfo(blogs, 3);

    對于插件機制我們上面已經介紹過了,在這里我們自然的會想到其所涉及的核心類 :PageInterceptor。攔截的是Executor 的兩個query()方法,要實現分頁插件的功能,肯定是要對我們寫的sql進行改寫,那么一定是在 intercept 方法中進行操作的,我們會發現這么一行代碼:

     String pageSql = this.dialect.getPageSql(ms, boundSql, parameter, rowBounds, cacheKey);

    調用到 AbstractHelperDialect 中的 getPageSql 方法:

    public String getPageSql(MappedStatement ms, BoundSql boundSql, Object parameterObject, RowBounds rowBounds, CacheKey pageKey) {
            // 獲取sql
            String sql = boundSql.getSql();
            //獲取分頁參數對象
            Page page = this.getLocalPage();
            return this.getPageSql(sql, page, pageKey);
        }

    這里可以看到會去調用 this.getLocalPage(),我們來看看這個方法:

    public <T> Page<T> getLocalPage() {
      return PageHelper.getLocalPage();
    }
    //線程獨享
    protected static final ThreadLocal<Page> LOCAL_PAGE = new ThreadLocal();
    public static <T> Page<T> getLocalPage() {
      return (Page)LOCAL_PAGE.get();
    }

    可以發現這里是調用的是PageHelper的一個本地線程變量中的一個 Page對象,從其中獲取我們所設置的 PageSize 與 PageNum,那么他是怎么設置值的呢?請看:

    PageHelper.startPage(1, 3);
    
    public static <E> Page<E> startPage(int pageNum, int pageSize) {
            return startPage(pageNum, pageSize, true);
    }
    
    public static <E> Page<E> startPage(int pageNum, int pageSize, boolean count, Boolean reasonable, Boolean pageSizeZero) {
            Page<E> page = new Page(pageNum, pageSize, count);
            page.setReasonable(reasonable);
            page.setPageSizeZero(pageSizeZero);
            Page<E> oldPage = getLocalPage();
            if (oldPage != null && oldPage.isOrderByOnly()) {
                page.setOrderBy(oldPage.getOrderBy());
         }
            //設置頁數,行數信息
            setLocalPage(page);
            return page;
    }
    
    protected static void setLocalPage(Page page) {
            //設置值
            LOCAL_PAGE.set(page);
    }

    在我們調用 PageHelper.startPage(1, 3); 的時候,系統會調用 LOCAL_PAGE.set(page) 進行設置,從而在分頁插件中可以獲取到這個本地變量對象中的參數進行 SQL 的改寫,由于改寫有很多實現,我們這里用的Mysql的實現:

    Mybatis是什么及怎么使用

     在這里我們會發現分頁插件改寫SQL的核心代碼,這個代碼就很清晰了,不必過多贅述:

    public String getPageSql(String sql, Page page, CacheKey pageKey) {
            StringBuilder sqlBuilder = new StringBuilder(sql.length() + 14);
            sqlBuilder.append(sql);
            if (page.getStartRow() == 0) {
                sqlBuilder.append(" LIMIT ");
                sqlBuilder.append(page.getPageSize());
            } else {
                sqlBuilder.append(" LIMIT ");
                sqlBuilder.append(page.getStartRow());
                sqlBuilder.append(",");
                sqlBuilder.append(page.getPageSize());
                pageKey.update(page.getStartRow());
            }
    
            pageKey.update(page.getPageSize());
            return sqlBuilder.toString();
    }

    PageHelper 就是這么一步一步的改寫了我們的SQL 從而達到一個分頁的效果。
    關鍵類總結:
      

    Mybatis是什么及怎么使用

    7.2 MyBatis逆向工程

    引入pom依賴

    <dependency>
       <groupId>org.mybatis.generator</groupId>
        <artifactId>mybatis-generator-core</artifactId>
        <version>1.4.0</version>
    </dependency>

    編寫配置文件:

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE generatorConfiguration
      PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
      "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
    
    <generatorConfiguration>
    <!-- 指定數據庫驅動
    	用java代碼的方式生成可以不指定(只需要吧mybatis-generator和數據庫驅動依賴到項目)
    
      <classPathEntry location ="F:\java\jar\mysql-connector-java-5.1.22-bin.jar" /> -->
      
      <!-- targetRuntime
      	MyBatis3  可以生成通用查詢,可以指定動態where條件
        MyBatis3Simple 只生成CURD
       -->
      <context id="DB2Tables" targetRuntime="MyBatis3">
      
      <!-- 關于注釋的生成規則 -->
    	<commentGenerator>
    	<!-- 設置不生成注釋 -->
    		<property name="suppressAllComments" value="true"/>
    	</commentGenerator>
    	
      <!-- 數據庫的連接信息 -->
        <jdbcConnection driverClass="com.mysql.jdbc.Driver"
            connectionURL="jdbc:mysql://localhost:3306/bookstore"
            userId="root"
            password="root">
        </jdbcConnection>
    
    <!-- java類型生成方式 -->
        <javaTypeResolver >
        <!-- forceBigDecimals 
    	    	true 當數據庫類型為decimal 或number 生成對應的java的BigDecimal
    	    	false 會根據可數據的數值長度生成不同的對應java類型
    	    useJSR310Types
    	        false 所有數據庫的日期類型都會生成java的 Date類型	
    	        true  會將數據庫的日期類型生成對應的JSR310的日期類型
    	        		比如 mysql的數據庫類型是date===>LocalDate
    	        		必須jdk是1.8以上
        -->
          <property name="forceBigDecimals" value="false" />
        </javaTypeResolver>
    
    	<!-- pojo的生成規則 -->
        <javaModelGenerator  
        	targetPackage="cn.tuling.pojo" targetProject="./src/main/java">
          <property name="enableSubPackages" value="true" />
          <property name="trimStrings" value="true" />
        </javaModelGenerator>
    
    	<!-- sql映射文件的生成規則 -->
        <sqlMapGenerator targetPackage="cn.tuling.mapper"  targetProject="./src/main/resources">
          <property name="enableSubPackages" value="true" />
        </sqlMapGenerator>
    
    	<!-- dao的接口生成規則 UserMapper-->
        <javaClientGenerator type="XMLMAPPER" targetPackage="cn.tuling.mapper"  targetProject="./src/main/java">
          <property name="enableSubPackages" value="true" />
        </javaClientGenerator>
    
        <table tableName="emp" domainObjectName="Emp" mapperName="EmpMapper" ></table>
     	<table tableName="dept" domainObjectName="Dept" mapperName="DeptMapper" ></table>
    
    
      </context>
    </generatorConfiguration>

    編寫測試類

    public class MBGTest {
    
        @Test
        public void test01() throws Exception {
            List<String> warnings = new ArrayList<String>();
            boolean overwrite = true;
            File configFile = new File("generatorConfig.xml");
            ConfigurationParser cp = new ConfigurationParser(warnings);
            Configuration config = cp.parseConfiguration(configFile);
            DefaultShellCallback callback = new DefaultShellCallback(overwrite);
            MyBatisGenerator myBatisGenerator = new MyBatisGenerator(config, callback, warnings);
            myBatisGenerator.generate(null);
        }
    }

    調用

    @Test
    public void test01() {
        try (SqlSession sqlSession = sqlSessionFactory.openSession()) {
            // Mybatis在getMapper就會給我們創建jdk動態代理
            EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
    
            Emp emp = mapper.selectByPrimaryKey(4);
            System.out.println(emp);
        }
    }
    
    
    
    /**
     * Mybatis3生成調用方式
     */
    @Test
    public void test02() {
        try (SqlSession sqlSession = sqlSessionFactory.openSession()) {
            // Mybatis在getMapper就會給我們創建jdk動態代理
            EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
    
            // 使用Example實現動態條件語句
            EmpExample empExample=new EmpExample();
            EmpExample.Criteria criteria = empExample.createCriteria();
            criteria.andUserNameLike("%帥%")
                    .andIdEqualTo(4);
    
            List<Emp> emps = mapper.selectByExample(empExample);
            System.out.println(emps);
        }
    }

    到此,關于“Mybatis是什么及怎么使用”的學習就結束了,希望能夠解決大家的疑惑。理論與實踐的搭配能更好的幫助大家學習,快去試試吧!若想繼續學習更多相關知識,請繼續關注億速云網站,小編會繼續努力為大家帶來更多實用的文章!

    向AI問一下細節

    免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

    AI

    固安县| 凤阳县| 沙田区| 新疆| 茶陵县| 阳西县| 大厂| 卢氏县| 陆河县| 汕头市| 安吉县| 赣榆县| 绵竹市| 阿尔山市| 都兰县| 喀喇| 南澳县| 保山市| 屯留县| 云浮市| 壤塘县| 石泉县| 邯郸县| 旌德县| 平邑县| 阳谷县| 金门县| 凯里市| 剑阁县| 蒲城县| 日土县| 重庆市| 萨嘎县| 武陟县| 富裕县| 泾源县| 沈丘县| 平山县| 博罗县| 阿拉善盟| 揭西县|