Mybatis-基本學習(下)

薩拉蒂亞發表於2021-09-14

四,MAP的使用--超常用

思考:多表連線查詢怎麼做?---MAP的好處!---返回 List<Object>

使用場景
類似加了一層封裝

  • 實體類,或者資料庫中的表,欄位或者引數過多,就考慮使用Map
  • 非常靈活,不用死死的在方法中定一個值,然後最後處理一個值的鎖定。。。它可以隨意定義幾個值的鎖定(前提是sql語句有對應)

規範? -滿足pojo的型別,SQL值的需求- Map需要<XX,Object>,因為Object通配所有,畢竟有int float String……

使用的依據:

  • 本質上parameterType就是一個送引數的,不管你送啥,你要將下面的值和sql以後要使用的引數對應就好
  • 最後送參時還是要對應原本的型別的,只是使用容器作為一箇中介

考慮,能否使用ArrayList---答:能 比較麻煩,沒有map直接--未考慮好

注意:因為map只是送值的,所以只有put操作。。

  • value分不分型別? 插入時不分,鎖定值時區分 ---(最好對應-)map.put("id",8);

操作

  • 介面中
    // map的使用---萬能的Map
    int addUser1(Map<String,Object> map);
    
    User  getUserById1(Map<String,Object> map);  // map 用法
    
    
  • Mapper.xml中
    <!--map的使用-->
    <insert id="addUser1" parameterType="map">
        insert into  mybatis.user(id,name,password) values (#{userId},#{userName},#{userPassword});
    </insert>
    
        <select id="getUserById1" parameterType="map" resultType="com.zjz.pojo.User">
        select * from mybatis.user where id = ${id}
    </select>
  • Test
    @Test
    public void TestAddUser1(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);

         Map<String, Object> map = new HashMap<>();
        map.put("userId",5);       // 對應#{userId}--型別限制POJO
        map.put("userName","zjz5");   // 對應#{userName}--型別限制POJO
        map.put("userPassword","321616");  // 對應#{UserPassword}--型別限制POJO
        mapper.addUser1(map);

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

        sqlSession.close();

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

        Map<String, Object> map = new HashMap<>();
        map.put("id","8");
        User userById1 = mapper.getUserById1(map);
        System.out.println(userById1);

        sqlSession.close();

    }
    

使用詳情及規範

  • 型別限制(POJO),值對應(SQL)
  • 方法只使用 put 送值-,
  • value型別沒對應好,。value分不分型別? 插入時不分,鎖定值時區分 ---(最好對應-)map.put("id",8);
  • key沒對應好,如果送的值多了,報錯---少了,送null --(主鍵不送---肯定報錯啊!)
    • 注:查詢時必須都得對應上key

不懂的---兩個例子,你自己做一個,三列即可確定一件事了

問題以及雙鎖定條件

根據 密碼 和 名字 查詢使用者

  • 思路一:直接在方法中傳遞引數

      1. 在介面方法的引數前加 @Param屬性
    • ------為啥需要@Param,不加時,直接報錯,因為型別影響後面
      1. Sql語句編寫的時候,直接取@Param中設定的值即可,不需要單獨設定引數型別(parameterType)
        //通過密碼和名字查詢使用者 
        User selectUserByNP(@Param("username") String username,@Param("pwd") String pwd);
        
        
         /* <select id="selectUserByNP" resultType="com.kuang.pojo.User"> 
         select * from user where name = #{username} and pwd = #{pwd} 
         </select> */
    
    
  • 思路二:使用萬能的Map

      1. 在介面方法中,引數直接傳遞Map;
      1. 編寫sql語句的時候,需要傳遞引數型別,引數型別為map
      1. 在使用方法的時候,Map的 key 為 sql中取的值即可,沒有順序要求!
        User selectUserByNP2(Map<String,Object> map);
        
        
        <select id="selectUserByNP2" parameterType="map" resultType="com.kuang.pojo.User"> 
        select * from user where name = #{username} and pwd = #{pwd} 
        </select>
        
        Map<String, Object> map = new HashMap<String, Object>(); 
          map.put("username","小明"); 
          map.put("pwd","123456"); 
        User user = mapper.selectUserByNP2(map);
    
  • 總結:

    • 如果引數過多,我們可以考慮直接使用Map實現,如果引數比較少,直接傳遞引數即可

@Param註解

1.使用@Param

  • 例項一 @Param註解單一屬性

      // 依據id,name查詢
      User getUserByIdName(@Param("id") int id,@Param("name") String name);
    -------------------------------------
     <select id="getUserByIdName" resultType="com.zjz.pojo.User">
          select * from mybatis.user where id = #{id} AND name = #{name}
      </select>
    
  • 1.當你使用了使用@Param註解來宣告引數時,如果使用 #{} 或 ${} 的方式都可以。

  // 根據id查詢
  User getUserById(@Param("id")int id);
  
  <select id="getUserById" parameterType="int" resultType="com.zjz.pojo.User">
        select * from mybatis.user where id = ${id}
    </select>
  • 2.當你不使用@Param註解來宣告引數時,必須使用 #{}方式且只有一個引數。在我的版本(M361,S802) ${} 的方式,不會報錯。(可能其它版本報錯)
  • 所以目前還是單個引數可以不用,多個引數要用
  // 根據id查詢
  User getUserById(int id);
  
  <select id="getUserById" parameterType="int" resultType="com.zjz.pojo.User">
        select * from mybatis.user where id = ${id}
    </select>

2,不使用@Param註解

  • 不使用@Param註解時,引數只能有一個,可以是屬性,也可以是物件
    • 目前似乎不支援@param("User")
      • 目前也沒有此限制--並且是Javabean。在SQL語句裡可以引用JavaBean的屬性,而且只能引用JavaBean的屬性。
    
      // 插入insert
      int addUser(User user);
      
        <insert id="addUser" parameterType="com.zjz.pojo.User">
          insert into  mybatis.user(id,name,password) values (#{id},#{name},#{password});
      </insert>
    
     @Test
      public void TestAddUser(){
          SqlSession sqlSession = MybatisUtils.getSqlSession();
          UserMapper mapper = sqlSession.getMapper(UserMapper.class);
           mapper.addUser(new User(4, "zjz4", "123456"));
          // 提交事務
          sqlSession.commit();
          sqlSession.close();
    
      }
    
    

小結:

  • 所有的增刪改操作都需要提交事務!
  • 介面所有的普通引數,儘量都寫上@Param引數,尤其是多個引數時,必須寫上!(否則報錯)
  • 有時候根據業務的需求,可以考慮使用map傳遞引數!
  • 為了規範操作,在SQL的配置檔案中,我們儘量將Parameter引數和resultType都寫上!

五.配置解析

1.核心配置檔案

  • mybatis-config.xml
configuration(配置)
  properties(屬性)
  settings(設定)
  typeAliases(型別別名)
  typeHandlers(型別處理器)
  objectFactory(物件工廠)
  plugins(外掛)
  environments(環境配置)
    environment(環境變數)
      transactionManager(事務管理器)
      dataSource(資料來源)
  databaseIdProvider(資料庫廠商標識)
  mappers(對映器)
  <!-- 注意元素節點的順序!順序不對會報錯 -->

2.environments元素

配置MyBatis的多套執行環境,將SQL對映到多個不同的資料庫上,必須指定其中一個為預設執行環境(通過default指定)

預設 事務JDBC 資料來源 POOLED

  • 1.子元素節點:environment

    • 具體的一套環境,通過設定id進行區別,id保證唯一!
    • 子元素節點:transactionManager - [ 事務管理器 ]
    • ` ` 有**兩個事務管理器**,不是一個!!
  • 2.子元素節點:資料來源(dataSource)

    • dataSource 元素使用標準的 JDBC 資料來源介面來配置 JDBC 連線物件的資源。
    • 資料來源是必須配置的。
    • 有三種內建的資料來源型別
    • type="[UNPOOLED|POOLED|JNDI]") ---JNDI 正常連線
    • (池:用完可以回收,不關,等下一個來連)
  • 3.屬性(properties)

    • 使用它實現引用配置檔案

    • 優先走外部的,再走裡面的property

           <!--引入外部配置檔案-->
          <properties resource="db.properties">
              <property name="username" value="root"/>
              <property name="password" value="123456"/>
          </properties>
      
      
  • 4.別名(typeAliases)

  • 1.起別名typeAlias

      <!--起別名-->
        <typeAliases>
            <typeAlias type="com.zjz.pojo.User" alias="User"/>
        </typeAliases>
    
    
      // 這樣在XXXMapper.xml中的resultType就可以直接使用User,不用com.zjz.pojo.User
    
      <select id="getUserList" resultType="User" >
            select * from user
        </select>
    
    
    
  • 2.package(重要)

  • 掃描包下的實體類,將類名首字母小寫作為別名-----

  • 可以自定義DIY --- 註解@Alias

    <!--起別名-->
    <typeAliases>
        <package name="com.zjz.pojo"/>
    </typeAliases>
  
  
    非必須:javabean 註解 
    @Alias("xiaoming")
  
  • 兩個之間的區別
    • Aliases 可以之間手動DIY一個別名
    • package 需要找到對應的javaBean,註解 ,然後就DIY的,否則都是類名首字母小寫

設定

  • setting

  • 一個配置完整的 settings 元素的示例如下:

        <settings>
            <setting name="cacheEnabled" value="true"/>      <!--開關對映器配置檔案中已配置的任何快取-->
            <setting name="lazyLoadingEnabled" value="true"/>  <!--延遲載入的全域性開關。-->
            <setting name="multipleResultSetsEnabled" value="true"/> <!--是否允許單個語句返回多結果集(需要資料庫驅動支援)-->
            <setting name="useColumnLabel" value="true"/>              <!--使用列標籤代替列名-->
            <setting name="useGeneratedKeys" value="false"/>          <!--允許 JDBC 支援自動生成主鍵,需要資料庫驅動支援-->
            <setting name="autoMappingBehavior" value="PARTIAL"/>       <!--允許 JDBC 支援自動生成主鍵,需要資料庫驅動支援-->
              <!--NONE 表示關閉自動對映;PARTIAL 只會自動對映沒有定義巢狀結果對映的欄位。 FULL 會自動對映任何複雜的結果集(無論是否巢狀)-->
            <setting name="autoMappingUnknownColumnBehavior" value="WARNING"/> <!--指定發現自動對映目標未知列(或未知屬性型別)的行為。-->
              <!-- NONE: 不做任何反應
                WARNING:  WARNING 輸出警告日誌('org.apache.ibatis.session.AutoMappingUnknownColumnBehavior' 的日誌等級必須設定為 WARN)
               FAILING:  FAILING 對映失敗 (丟擲 SqlSessionException) -->
            <setting name="defaultExecutorType" value="SIMPLE"/>          <!--配置預設的執行器。SIMPLE 就是普通的執行器;-->
            <setting name="defaultStatementTimeout" value="25"/>         <!--設定超時時間-->
            <setting name="defaultFetchSize" value="100"/>           <!--為驅動的結果集獲取數量(fetchSize)設定一個建議值。此引數只可以在查詢設定中被覆蓋。-->
            <setting name="safeRowBoundsEnabled" value="false"/>      <!--禁止在巢狀語句中使用結果處理器(ResultHandler) true開啟禁止-->
            <setting name="mapUnderscoreToCamelCase" value="false"/>   <!--是否開啟駝峰命名自動對映,
                 即從經典資料庫列名 A_COLUMN 對映到經典 Java 屬性名 aColumn。注:ORACLE資料庫常見-->
            <setting name="localCacheScope" value="SESSION"/>      <!--MyBatis 利用本地快取機制(Local Cache)防止迴圈引用和加速重複的巢狀查詢。-->
            <setting name="jdbcTypeForNull" value="OTHER"/>          <!--當沒有為引數指定特定的 JDBC 型別時,空值的預設 JDBC 型別-->
            <setting name="lazyLoadTriggerMethods" value="equals,clone,hashCode,toString"/> <!--指定物件的哪些方法觸發一次延遲載入-->
            </settings>
    
    
  • 其它配置

  • 型別處理器 typeHandlers

  • 物件工廠 objectFactory

  • plugins 外掛

    • mybatis-generator-core
    • mybatis-plus-----以後學
    • 通用mapper

對映(mapper)

  • MapperRegistry:註冊繫結我們的配置檔案
  • 方式一 resource(推薦使用)
  <!--每一個mapper.xml都需要在mybatis核心配置檔案中註冊-->

  <mappers>
      <mapper resource="com/zjz/dao/UserMapper.xml"/>
  </mappers>
  • 方式二 使用class檔案繫結註冊
  <!--每一個mapper.xml都需要在mybatis核心配置檔案中註冊-->
  <mappers>
      <mapper class="com.zjz.dao.UserMapper"/>
  </mappers>
  • class使用注意:
    • 介面必須和他的Mapper配置檔案必須同名
    • 介面和他的配置檔案必須在同一個包下
  <environments default="test">    <!--使用test環境-->
      <environment id="development">
          <!-- 事務管理-->
          <transactionManager type="JDBC"/>
          <dataSource type="POOLED">
              <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
              <property name="url"
                        value="jdbc:mysql://localhost:3306/mybatis? useSSL=true&useUnicode=true&characterEncoding=utf8"/>
              <property name="username" value="root"/>
              <property name="password" value="123456"/>
          </dataSource>
      </environment>


      <environment id="test">
          <!-- 事務管理-->
          <transactionManager type="JDBC"/>
          <dataSource type="POOLED">
              <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
              <property name="url"
                        value="jdbc:mysql://localhost:3306/mybatis? useSSL=true&useUnicode=true&characterEncoding=utf8"/>
              <property name="username" value="root"/>
              <property name="password" value="123456"/>
          </dataSource>
      </environment>

  </environments>

生命週期和作用域

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

  • SqlSessionFactoryBuilder:

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

    • 生命週期就同於 MyBatis 的應用週期。
    • 可以想象為:資料庫連線池
    • 一旦建立了 SqlSessionFactory,就要長期儲存它,直至不再使用MyBatis 應用
    • 往往希望 SqlSessionFactory 作為一個單例,讓它在應用中被共享。所以說 SqlSessionFactory 的最佳作用域是應用作用域。
  • SqlSession

    • 連線到連線池的請求
    • SqlSession 就相當於一個資料庫連線(Connection 物件),你可以在一個事務裡面執行多條 SQL,
      然後通過它的 commit、rollback等方法,提交或者回滾事務。
    • 所以它應該存活在一個業務請求中,處理完整個請求後,應該關閉這條連線,讓它歸還給 SqlSessionFactory,
      否則資料庫資源就很快被耗費精光,系統就會癱瘓,所以用 try...catch...finally... 語句來保證其正確關閉

ResultMap

屬性名,欄位名不一致

  • 如果POJO同資料庫的欄位不一致

  • 正常執行會顯示不一致的欄位為null
    User{id=0, name='zjz0', pwd='null'}

  • 1.解決方法--別名

    <select id="selectUserById" resultType="User">
         select id , name , password as pwd  from user where id = #{id}
     </select>
    
  • 2.方案二:使用結果集對映->ResultMap 【推薦】

    • 注意:<resultMap id="UserMap" type="User"> 的id
      對應 <select id="selectUserById" resultMap="UserMap">的resultMap
      <resultMap id="UserMap" type="User">
         <!-- id為主鍵 --> 
            <id column="id" property="id"/> 
        <!-- column是資料庫表的列名 , property是對應實體類的屬性名 -->
          <result column="name" property="name"/> 
           <result column="pwd" property="password"/>
     </resultMap> 
    
    
    <select id="selectUserById" resultMap="UserMap"> 
       select id , name , pwd from user where id = #{id}
     </select>
    
    
    
  • 資料庫中,存在一對多,多對一的情況,我們之後會
    使用到一些高階的結果集對映,association,collection這些

相關文章