MyBatis 基礎(chǔ)學(xué)習(xí)

MyBatis_01

1. MyBatis 認(rèn)識

  • MyBatis 是 ORM 的一種實現(xiàn); 使用MyBatis可以簡化JDBC的操作, 實現(xiàn)數(shù)據(jù)的持久化, 代替的就是三層模型種的Dao層

  • ORM : Object Relational Mapping : 對象關(guān)系映射; MyBatis的實現(xiàn)就是將數(shù)據(jù)庫種的一張表映射成一個JavaBeen對象; 通過 ORM 就可以像操作對象一樣操作數(shù)據(jù)庫種對應(yīng)的表


2. 配置 MyBatis 及測試

  • 下載 MyBatis3.4.6

  • 下載后需要將 mybatis3.4.6.jar 引入需要使用的項目中

  • MyBatis 是實現(xiàn)數(shù)據(jù)庫表和JavaBeen的映射關(guān)系, 這種映射關(guān)系的實現(xiàn)就是每個JavaBeenMapper.xml; 在映射文件中通過標(biāo)簽定義的增刪改查就會實際作用到數(shù)據(jù)庫表中

  • 如: personMapper.xml示例及參數(shù)說明

    • namespace: 該javaBeenMapper.xml 映射文件的全限定路徑; (在面向接口開發(fā)中: namespace的值就是對應(yīng)接口的全限定類名)

    • id : SQL語句的標(biāo)簽

    • parameterType : 接收參數(shù)的類型

      • 輸入?yún)?shù)parameterType和輸出參數(shù)resultType, 在形式上只能有一個(即如果需要傳遞多個參數(shù), 就需要將多個參數(shù)封裝起來)

      • 如果輸入/輸出參數(shù)是簡單類型(8個基本類型+String): 則在#{}中可以使用任何占位符; 如果是對象類型, 則#{}中必須是對象的屬性

    • resultType : 結(jié)果的類型, 即查完返回值類型;

      • 如果返回值類型是一個對象(如: Student), 則無論返回一個, 還是返回多個, 在resultType中都寫成 "xyz.xmcs.Entity.Student" 的形式
  <!-- studentMapper.xml: 用于映射 Student.java 和 MySQL的student表 -->
  <?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="xyz.xmcs.Entity.studentMapper">

      <!-- id: 該標(biāo)簽的唯一標(biāo)識; resultType:結(jié)果的類型, 即查完返回值類型; parameterType:接收參數(shù)的類型 -->
      <!-- #{} 就相當(dāng)于 pstmt 中SQL語句里面的 ? -->
      <select id="queryStudentByStuNo" resultType="xyz.xmcs.Entity.Student" parameterType="int">
          select * from student where stuNo = #{stuNo}
      </select>

      <!-- 增加 -->
      <insert id="addStudent" parameterType="xyz.xmcs.Entity.Student" >
          insert into student(stuNo, stuName, stuAge, graName) values(#{stuNo}, #{stuName}, #{stuAge}, #{graName})
      </insert>

      <!-- 刪除 -->
      <delete id="deleteStudentByStuNo" parameterType="int">
          delete from student where stuNo = #{stuNo}
      </delete>

      <!-- 修改 -->
      <update id="updateStudentByStuNo" parameterType="xyz.xmcs.Entity.Student">
          update student set stuName=#{stuName}, stuAge=#{stuAge}, graName=#{graName} where stuNo=#{stuNo}
      </update>

      <!-- 查詢?nèi)?-->
      <select id="queryAllStudent" resultType="xyz.xmcs.Entity.Student">
          select * from student
      </select>
  </mapper>
  • 除上述實現(xiàn)映射的配置外, 還需要配置數(shù)據(jù)庫信息以及需要加載的映射文件; 即MyBatis的總配置文件; 在src同級目錄下配置 conf.xml; conf.xml 重要標(biāo)簽說明

conf.xml 重要標(biāo)簽說明

  • configuration : 配置信息區(qū)域, 所有的配置信息卸載 configuration 標(biāo)簽體中

  • properties : 用于引入動態(tài)文件, 通過 resource 指定文件名來引入

  • settings : 用于設(shè)置全局參數(shù); 可設(shè)置 二級緩存, 開啟日志, 延遲加載, 以及其他配置

  • typeAliases : 用于設(shè)置單個/多個別名

  • typeHandlers : 用于配置類型轉(zhuǎn)換器, (JDBC類型和JAVA類型之間的轉(zhuǎn)換)

  • plugins : 通過<\plugin>標(biāo)簽配置自定義的插件

  • environments : 用于環(huán)境配置, environments里面可以配置多個environment, 并且在environment中的id指明數(shù)據(jù)庫環(huán)境, 在environments里的default中選擇對應(yīng)的id來指定MyBatis運行時的數(shù)據(jù)庫環(huán)境; (也可以在SSqlSessionFactoryBuilder().build(reader, "environment的id")來強行指定運行時的環(huán)境)

    • environment: 具體的環(huán)境配置, 可配置多個

      • dataSource: (在environment中配置)指明environment使用的數(shù)據(jù)源類型, 有如下三種類型, 一般選擇 POOLED

        • POOLED: 使用數(shù)據(jù)庫連接池

        • UNPOOLED: 使用傳統(tǒng)的JDBC模式(每次打開關(guān)閉, 消耗性能)

        • JNDI: 從tomcat里面獲取一個內(nèi)置的數(shù)據(jù)庫連接池

        • dataSource內(nèi)的property數(shù)據(jù)庫連接用的信息, 可以直接寫在conf.xml 中, 也可以將信息用KV對的形式寫在properties配置文件中, 在configuration標(biāo)簽里面增加 properties標(biāo)簽并指定其resource屬性來引入配置文件, 在 property 的值中就可以通過類似于EL表達式的方式來引入

    • transactionManager: (在environment中配置)指明事務(wù)的提交方式, 有如下兩種, 一般選擇 JDBC

      • JDBC : 利用JDBC方式處理事務(wù)(需要手工的 commit, rollback, close)

        • 手工提交就是在代碼中通過SqlSession的connit()方法手工提交
      • MANAGED : 將事務(wù)交由其他組件去托管(如: spring, jobss), 默認(rèn)會關(guān)閉連接, 不關(guān)閉的話需要在transactionManager下面配置: (<property name="closeConnection" value="false" />)

  • databaseIdProvider : 配置數(shù)據(jù)庫支持類: 大小寫不能亂寫; 通過 type 來指定, 一般是 DB_VENDOR; 標(biāo)簽體內(nèi)部通過property指定數(shù)據(jù)庫

  • mappers : 用于指定映射文件, 可以單獨映射, 也可以通過package批量映射

  • 注意的一個細節(jié)

    • 使用mysql8后, URL信息在xml和properties文件中需要注意(&)的細微區(qū)別; xml中(&)應(yīng)該寫成& 而在properties中可以直接寫 &

    • URL在properties中, 后面要增加一句: &allowPublicKeyRetrieval=true 否則MyBatis會報錯

  • 如 conf.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>

      <!-- 引人動態(tài)文件 -->
      <properties resource="db.properties" />

      <!-- 設(shè)置全局參數(shù)-->
      <settings>
          <!-- true: 開啟二級緩存 false: 關(guān)閉二級緩存; 再具體的Mapper.xml 中需要通過<cache/>標(biāo)簽聲明-->
          <setting name="cacheEnabled" value="true" />
          <!--<setting name="lazyLoadingEnabled" value="false" />-->
          <!-- 開啟日志, 并指定使用的具體日志 -->
          <setting name="logImpl" value="LOG4J"/>

          <!-- 開啟延遲加載 -->
          <setting name="lazyLoadingEnabled" value="true"/>
          <!-- 關(guān)閉立即加載 -->
          <setting name="aggressiveLazyLoading" value="false"/>
          <!-- 上面的配置沒錯, 但是運行就報 Error creating lazy proxy.  Cause: java.lang.NullPointerException
               的異常, 看了網(wǎng)上的解決方案是將 MyBatis的jar包更新到3.5.1
          -->
          <!-- 配置全局的NULL, 在遇到數(shù)據(jù)庫(oracle)不能解析時, 就使用NULL -->
          <setting name="jdbcTypeForNull" value="NULL"/>

      </settings>
      

      <!-- 設(shè)置單個/多個別名 -->
      <typeAliases>
          <!-- 單個別名
          <typeAlias type="xyz.xmcs.Entity.Student" alias="Student" /> -->
          <!-- 批量設(shè)置別名 -->
          <package name="xyz.xmcs.Entity" />
      </typeAliases>


      <!-- 配置類型轉(zhuǎn)換器: JDBC類型和JAVA類型之間的轉(zhuǎn)換 -->
      <typeHandlers>
          <!-- 指定轉(zhuǎn)換器的全類名, 并且說明java 和 jdbc 轉(zhuǎn)換的類型 -->
          <typeHandler handler="xyz.xmcs.converter.BooleanAndIntConverter" javaType="Boolean" jdbcType="INTEGER"/>
      </typeHandlers>

      <!-- 配置插件: 在這里配置自定義的插件 -->
      <!--<plugins>-->
          <!--
          <plugin interceptor="xyz.xmcs.my.interceptors.MyInterceptor">
              <property name="name" value="zs" />
              <property name="age" value="23"/>
          </plugin>
          -->
          <!--
          <plugin interceptor="xyz.xmcs.my.interceptors.MyInterceptor2">
              <property name="name" value="zs22222" />
              <property name="age" value="33"/>
          </plugin>
          -->
      <!--</plugins>-->

      <!-- 該標(biāo)簽中可配置數(shù)據(jù)庫信息, 如連接oracle, mysql 等 -->
      <environments default="devMySQL">
          <!-- MySQL 數(shù)據(jù)庫 -->
          <environment id="devMySQL">
              <transactionManager type="JDBC"/>
              <dataSource type="POOLED">
                  <!-- 配置數(shù)據(jù)庫信息; 可以直接在value中寫死, 也可以將值以kv對的形式寫在properties文件中,
                  以類似于EL表法是的方式動態(tài)引入(需要先加入 properties標(biāo)簽, 引入properties文件)-->
                  <property name="driver" value="${mysql.driver}"/>
                  <property name="url" value="${mysql.url}"/>
                  <property name="username" value="${mysql.username}"/>
                  <property name="password" value="${mysql.password}"/>
              </dataSource>
          </environment>

          <!-- Oracle 數(shù)據(jù)庫 -->
          <environment id="devOracle">
              <transactionManager type="JDBC"/>
              <dataSource type="POOLED">
                  <!-- 配置數(shù)據(jù)庫信息; 可以直接在value中寫死, 也可以將值以kv對的形式寫在properties文件中,
                  以類似于EL表法是的方式動態(tài)引入(需要先加入 properties標(biāo)簽, 引入properties文件)-->
                  <property name="driver" value="${oracle.driver}"/>
                  <property name="url" value="${oracle.url}"/>
                  <property name="username" value="${oracle.username}"/>
                  <property name="password" value="${oracle.password}"/>
              </dataSource>
          </environment>
      </environments>
      
      <!-- 配置數(shù)據(jù)庫支持類: 大小寫必能亂寫 -->
      <databaseIdProvider type="DB_VENDOR">
          <property name="MySQL" value="mysql"/>
          <property name="Oracle" value="oracle"/>
          <!--<property name="SQL Server" value=""/>-->
      </databaseIdProvider>
      
      <mappers>
          <!-- 加載映射文件 -->
          <mapper resource="xyz/xmcs/Entity/personMapper.xml"/>

          <!-- 這種是批量引入mapper; 可以將 xyz.xmcs.mapper包中的注解接口(即接口中的方法上帶注解)和xml全部一次性引入-->
          <package name="xyz.xmcs.Mapper"/>

          <!-- 這是使用注解的方式, 并單獨引入; 不推薦使用
          <mapper class="xyz.xmcs.Mapper.StudentMapper"/>-->

          <!--
          <mapper resource="xyz/xmcs/Mapper/studentMapper.xml"/>
          <mapper resource="xyz/xmcs/Mapper/studentCardMapper.xml"/>
          <mapper resource="xyz/xmcs/Mapper/studentClassMapper.xml"/> -->

      </mappers>
  </configuration>

實現(xiàn)MyBatis基礎(chǔ)方式的增刪改查

  • 測試類和傳統(tǒng)的JDBC一樣, 也有固定的步驟:

    1. 加載MyBatis配置文件(就是為了訪問數(shù)據(jù)庫): 通過Resources.getResourceAsReader("conf.xml")獲取配置文件的輸入流對象

    2. 通過new SqlSessionFactoryBuilder().build("輸入流對象") 獲得 SqlSession 對象; SqlSession對象就相當(dāng)于Connection

    3. 通過獲得的SqlSession對象調(diào)用其方法實現(xiàn)查詢并返回結(jié)果: sqlSession.selectOne("需要查詢的SQL的namespace.id", "SQL的參數(shù)值");

    4. 手動提交事務(wù): sqlSession.commit();

    5. 關(guān)閉 SqlSession 對象: sqlSession.close();

  • 示例:

  import org.apache.ibatis.io.Resources;
  import org.apache.ibatis.session.SqlSession;
  import org.apache.ibatis.session.SqlSessionFactory;
  import org.apache.ibatis.session.SqlSessionFactoryBuilder;
  import java.io.IOException;
  import java.io.Reader;
  import java.util.List;

  public class TestStudent {
    // 提取獲得SqlSession的工具類
      private static SqlSession getSqlSession() throws IOException {
          // 1. 加載MyBatis配置文件為Reader對象
          Reader reader = Resources.getResourceAsReader("conf.xml");

          // 2. 通過 Reader 對象獲取SqlSessionFactory 對象
          SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);

          // 3. 通過 SqlSessionFactory的對象獲取SqlSession, 并通過調(diào)用SqlSession對象的方法實現(xiàn)數(shù)據(jù)庫操作并拿到操作結(jié)果
          SqlSession sqlSession = sqlSessionFactory.openSession();
          return sqlSession;
      }
      // 按stuNo查詢一個學(xué)生
      public static void queryStudentByStuNo() throws IOException {
          SqlSession sqlSession = getSqlSession();
          String stmt = "xyz.xmcs.Entity.studentMapper.queryStudentByStuNo";
          Student student = sqlSession.selectOne(stmt,1);
          System.out.println(student);
          // 4. 手動提交事務(wù)
          sqlSession.commit();
          // 5. 關(guān)閉SqlSession對象
          sqlSession.close();
      }
      // 增加學(xué)生
      public static void addStudent() throws IOException {
          SqlSession sqlSession = getSqlSession();
          String stmt = "xyz.xmcs.Entity.studentMapper.addStudent";
          Student student = new Student(2, "ls", 20, "class1");
          int insert = sqlSession.insert(stmt, student);
          sqlSession.commit();
          System.out.println("Insert Result:" + insert);
          sqlSession.close();
      }
      // 刪除學(xué)生
      public static void deleteStudent() throws IOException {
          SqlSession sqlSession = getSqlSession();
          String stmt = "xyz.xmcs.Entity.studentMapper.deleteStudentByStuNo";
          int delete = sqlSession.delete(stmt, 3);
          System.out.println("Delete Result:" + delete);
          sqlSession.commit();
          sqlSession.close();
      }
      // 查詢?nèi)繉W(xué)生
      public static void queryAllStudent() throws IOException {
          SqlSession sqlSession = getSqlSession();
          String stmt = "xyz.xmcs.Entity.studentMapper.queryAllStudent";
          List<Student> students = sqlSession.selectList(stmt);
          for (Student student : students) {
              System.out.println(student);
          }
          sqlSession.commit();
          sqlSession.close();
      }
      // 根據(jù) stuNo 修改學(xué)生
      public static void updateStudentByStuNo() throws IOException {
          SqlSession sqlSession = getSqlSession();
          String stmt = "xyz.xmcs.Entity.studentMapper.updateStudentByStuNo";
          Student student = new Student(2, "LS", 14, "class2");
          int update = sqlSession.update(stmt, student);
          System.out.println("Update Result:" + update);
          sqlSession.commit();
          sqlSession.close();
      }

      public static void main(String[] args) throws IOException {
          queryStudentByStuNo();
          addStudent();
          queryAllStudent();
          updateStudentByStuNo();
          deleteStudent();
      }
  }

3. 實現(xiàn)mapper動態(tài)代理方式的CRUD (MyBatis接口開發(fā))

  • 原則: 約定優(yōu)于配置

  • 配置方式: 就是在一個 xxxMapper.xml 文件中指定配置參數(shù)

    • 如: 在 xxxMapper.xml 中指定: <name>MyBatisTest</name>
  • 硬編碼方式: 就是在xxx.java中: 通過 new Configuration().setName("MyBatisTest")


具體實現(xiàn)步驟

  • 基礎(chǔ)環(huán)境: mybatis.jar, mysql-connection.jar, conf.xml, mapper.xml 都和上述基礎(chǔ)查詢時配置的一樣

  • 與基礎(chǔ)查詢的不同之處:

    • 約定的目標(biāo): 省略掉stmt, 即根據(jù)約定直接可以定位處SQL語句

    • 習(xí)慣將mapper.xml 和 接口放在同一個包中

    • 約定:(即面向接口開發(fā), 約定就是對接口以及接口中方法的一些約定; 說到底就是讓接口和對應(yīng)的mapper.xml有關(guān)聯(lián))

      • mapper.xml 中namespace的值就是接口的全類名(即: 實現(xiàn)接口和mapper.xml的一一對應(yīng))

      • 接口中的方法名和mapper.xml中SQL標(biāo)簽的id一致

      • 方法的輸入?yún)?shù)和mapper.xml中parameterType的類型一致;(如果mapper.xml中沒有parameterType, 則說明沒有輸入?yún)?shù), 或者輸入多個參數(shù))

      • 返回值和mapper.xml中的resultType類型一致; (如果mapper.xml中沒有resultType, 則說明沒有輸出參數(shù)(也可以在接口抽象方法中直接定義返回值類型))

  • 匹配的過程: (約定的過程)

    • 根據(jù) 接口名 找到 mapper.xml 文件; (根據(jù)的是namespace=接口的全類名)

    • 根據(jù)接口的 方法名 找到mapper.xml文件中的SQL標(biāo)簽; (根據(jù)的是: 接口方法名=SQL標(biāo)簽的Id值)

    • 基于以上兩種約定: 當(dāng)我們調(diào)用接口中的方法時, 程序能自動定位到某一個mapper.xml文件中的SQL標(biāo)簽

  • 面向接口的執(zhí)行步驟:

    1. 獲取 SqlSession 對象

    2. 調(diào)用 SqlSession 對象的 getMappr(接口.class) 方法獲取接口, 返回值類型就是接口類型

    3. 再通過調(diào)用該接口的對應(yīng)的方法來執(zhí)行即可完成SQLCRUD操作

    4. 手動commit, 并關(guān)閉SqlSession對象


4. MyBatis 優(yōu)化

可以將配置信息單獨放入 properties 文件中, 然后動態(tài)引入

  • 在 properties 中以 K=V 的形式保存配置信息

  • 在 <\configuration>標(biāo)簽中通過 <\properties resource="db.properties" />來引入配置文件

  • 在需要引用的地方通過: ${k} 即可引入需要的V值


設(shè)置MyBatis全局參數(shù)

  • 在conf.xml 中的<\configuration>標(biāo)簽中通過<\settings>標(biāo)簽來設(shè)定

  • 如:

  <settings>
    <setting name="cacheEnabled" value="false" />
    <setting name="lazyLoadingEnabled" value="false" />
  </settings>

設(shè)置別名

  • 設(shè)置別名主要是解決一個全類名很長的問題, 通過設(shè)置別名, 就可以使用一個別名來代替全類名; 并且不區(qū)分大小寫

  • 單個別名還是批量設(shè)置別名, 都是在 conf.xml 中的 <\configuration> 標(biāo)簽中通過 <\typeAliases> 標(biāo)簽來設(shè)置的

  • 設(shè)置單個別名: 就是將一個類單獨起一個別名, 且別名是忽略大小寫的

  • 批量設(shè)置別名: 就是將包下的所有類批量設(shè)置別名, 別名就是類名(不帶包名), 也是忽略大小寫的

    <!-- 設(shè)置單個/多個別名 -->
    <typeAliases>
        <!-- 單個別名 
        <typeAlias type="xyz.xmcs.Entity.Student" alias="Student" /> -->
        <!-- 批量設(shè)置別名 -->
        <package name="xyz.xmcs.Entity" />
    </typeAliases>

MyBatis自帶的別名

_byte=byte    _long=long    _short=short    _int-int    _integer=int    long=Long    int=Integer    integer=Integer    boolean=Boolean
decimal=BigDecimal    object=Object    hashmap=HashMap    arraylist=ArrayList    iterator=Iterator    _double=double    _float=float
_boolean=boolean    string=String    byte=Byte    short=Short    double=Double   float=Float    date=Date    bigdecimal=BigDecimal
map=Map    list=List    collection=Collection  

5. MyBatis類型處理器(類型轉(zhuǎn)換器)

  • MyBatis 自帶一些常見的類型處理器

自定義MyBatis類型處理器

  • 類型轉(zhuǎn)換就是Java代碼和JDBC類型之間的轉(zhuǎn)換

  • 以例子來學(xué)習(xí): 如Java實體類Student.java里true代表男生, false代表女生; 而在student表中以int類型來代表男女: 1代表男生, 0代表女生; 而需要做的就是實現(xiàn)讓true轉(zhuǎn)換為1, 而將false轉(zhuǎn)換為0

  • 自定義類型轉(zhuǎn)換器(將 boolean --> int)步驟

    • 創(chuàng)建轉(zhuǎn)換器(需要實現(xiàn)TypeHandler接口或者繼承TypeHandler的實現(xiàn)類BaseTypeHandler)

    • 在conf.xml中的configuration標(biāo)簽里面的typeHandlers標(biāo)簽中配置

  • 示例

  // BooleanAndIntConverter.java 實現(xiàn)的轉(zhuǎn)換器將JAVA的Boolean轉(zhuǎn)換為JDBC的int
  package xyz.xmcs.converter;
  import org.apache.ibatis.type.BaseTypeHandler;
  import org.apache.ibatis.type.JdbcType;
  import java.sql.CallableStatement;
  import java.sql.PreparedStatement;
  import java.sql.ResultSet;
  import java.sql.SQLException;

  public class BooleanAndIntConverter extends BaseTypeHandler<Boolean> {
      // java(boolean) --> DB(int)
      /**
       * @param preparedStatement: preparedStatement對象
       * @param i : preparedStatement對象操作參數(shù)的位置
       * @param aBoolean: java的值
       * @param jdbcType : JDBC操作的數(shù)據(jù)庫類型
       * @throws SQLException
       */
      @Override
      public void setNonNullParameter(PreparedStatement preparedStatement, int i, Boolean aBoolean, JdbcType jdbcType) throws SQLException {
          // 思路: 就是判斷 aBoolean 是true還是false; 如果true, 就通過preparedStatement的setInt()方法將i位置設(shè)為1; 否則設(shè)為0
          if(aBoolean) {
              // 1
              preparedStatement.setInt(i, 1);
          } else {
              // 0
              preparedStatement.setInt(i, 0);
          }
      }
      // DB(int) --> java(boolean) 按照列名拿值
      @Override
      public Boolean getNullableResult(ResultSet resultSet, String s) throws SQLException {
          int sexNum = resultSet.getInt(s);
          return sexNum == 1? true : false;
      }
      // DB(int) --> java(boolean) 按列數(shù)拿值
      @Override
      public Boolean getNullableResult(ResultSet resultSet, int i) throws SQLException {
          int sexNum = resultSet.getInt(i);
          return sexNum == 1? true : false;
      }
      // DB(int) --> java(boolean) 存儲過程或存儲函數(shù)拿值
      @Override
      public Boolean getNullableResult(CallableStatement callableStatement, int i) throws SQLException {
          int sexNum = callableStatement.getInt(i);
          return sexNum == 1 ? true : false;
      }
  }
    <!-- conf.xml:配置轉(zhuǎn)換器 -->
    <typeHandlers>
        <!-- 指定轉(zhuǎn)換器的全類名, 并且說明java 和 jdbc 轉(zhuǎn)換的類型 
      需要注意: jdbcType類型 需要全大寫 INTEGER
      -->
        <typeHandler handler="xyz.xmcs.converter.BooleanAndIntConverter" javaType="Boolean" jdbcType="INTEGER"/>
    </typeHandlers>

測試自定義類型轉(zhuǎn)換

  • 在寫好自定義類型轉(zhuǎn)換類以及在conf.xml配置的基礎(chǔ)上測試自定義類型轉(zhuǎn)換

  • 在MySQL數(shù)據(jù)庫MyBatisTest的 student 表中增加 stuSex 列來記錄學(xué)生的性別: alter table student add stuSex int(2);

  • 在 Entity 包下的Student.java 中增加 boolean stuSex 屬性, 并增加setter和getter以及修改構(gòu)造器

  • 查詢學(xué)生的SQL標(biāo)簽來測試自定義類型轉(zhuǎn)換

    • 在 StudentMapper.xml 中增加id=queryStudentByStuNoWithConverter的 select標(biāo)簽; 需要注意的是接收的返回值需要(int --> boolean)類型轉(zhuǎn)換, 所以不能使用resultType而是使用resultMap; 并且需要單獨定義 <\resultMap> 標(biāo)簽; 并且在 resultMap標(biāo)簽許需要轉(zhuǎn)換的標(biāo)簽中添加 jdbcType 和 javaType; 然后在queryStudentByStuNoWithConverter 中指定此 resultMap 的 id 即可

    • 在 StudentMapper.java 接口中增加方法: Student queryStudentByStuNoWithConverter(int stuNo);

    • 在測試類中實現(xiàn)此方法來測試

  • 增加學(xué)生的SQL標(biāo)簽來測試自定義類型轉(zhuǎn)換

    • 在 StudentMapper.xml 中增加id=addStudentWithConverter的 insert標(biāo)簽, 只是在 sql語句中需要類型轉(zhuǎn)換的參數(shù)后面增加 javaType=java類型, jdbcType=INTEGER(JDBC類型)

    • 在 StudentMapper.java 接口中增加方法:void addStudentWithConverter(Student student);

    • 在測試類中實現(xiàn)此方法來測試

測試代碼示例

  • StudentMapper.xml 中增加的標(biāo)簽
  <resultMap type="Student" id="studentResult">
        <!-- 分為主鍵和非主鍵: id 指定主鍵, result 指定非主鍵 -->
        <id property="stuNo" column="stuNo"/>
        <result property="stuName" column="stuName"/>
        <result property="stuAge" column="stuAge"/>
        <result property="graName" column="graName"/>
        <!--  在需要類型轉(zhuǎn)換的地方, 通過 jdcbType 和 JavaType 來指定類型  -->
        <result property="stuSex" column="stuSex" jdbcType="INTEGER" javaType="boolean"/>
    </resultMap>
    
    <!-- 查詢: 使用了類型轉(zhuǎn)換器
    如果類中的屬性和表中的字段類型能夠合理識別(String - varchar), 則可以使用resultType; 否則(boolean - int)使用resultMap
    如果類中的屬性名和表中的字段名能夠一一對應(yīng)(stuNo - stuno), 則可以使用resultType; 否則(stuNo - id)使用resultMap
    -->
    <select id="queryStudentByStuNoWithConverter" resultMap="studentResult" parameterType="int">
        select * from student where stuNo = #{stuNo}
    </select>

    <!-- 增加: 帶類型轉(zhuǎn)換 -->
    <insert id="addStudentWithConverter"  parameterType="Student" >
        insert into student(stuNo, stuName, stuAge, graName, stuSex) values(#{stuNo}, #{stuName}, #{stuAge}, #{graName}, #{stuSex,  javaType=boolean, jdbcType=INTEGER})
    </insert>
  • StudentMapper.java 接口中新增的方法
  // 查詢一個學(xué)生, 使用了類型轉(zhuǎn)換
    Student queryStudentByStuNoWithConverter(int stuNo);

    // 增加學(xué)生 帶類型轉(zhuǎn)換
    void addStudentWithConverter(Student student);
  • TestStudent.java 中的測試方法
  // 測試類型轉(zhuǎn)換查詢
  public static Student queryStudentByStuNoWithConverter(int stuNo) throws IOException {
        SqlSession sqlSession = getSqlSession();
        StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
        Student student = mapper.queryStudentByStuNoWithConverter(stuNo);
        System.out.println(student);
        sqlSession.commit();
        sqlSession.close();
        return student;
    }
    // 測試類型轉(zhuǎn)換增加
    public static void addStudentWithConverter() throws IOException {
        SqlSession sqlSession = getSqlSession();
        Student student1 = new Student(3, "ju", 20, "class3", false);
        StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
        mapper.addStudentWithConverter(student1);
        sqlSession.commit();
        sqlSession.close();
    }

SQL 標(biāo)簽中 resultType 和 resultMap 的區(qū)別

  • 如果類中的屬性和表中的字段類型能夠合理識別(String - varchar), 則可以使用resultType; 否則(boolean - int)使用resultMap

  • 如果類中的屬性名和表中的字段名能夠一一對應(yīng)(stuNo - stuno), 則可以使用resultType; 否則(stuNo - id)使用resultMap

  • 總結(jié): resultMap 可以實現(xiàn)類型轉(zhuǎn)換和 屬性與字段的映射關(guān)系兩個功能

  • resultMap 標(biāo)簽示例

  <resultMap type="Student" id="studentResult">
    <!-- 當(dāng)Been中的屬性名和表中的字段名不一致的時候, 就要通過下面這樣的方式來指定(property: Been中的屬性名, column:表中的字段名)
    <id property="id" column="stuNo"/>
    -->
        <!-- 分為主鍵和非主鍵 -->
        <id property="stuNo" column="stuNo"/>
        <result property="stuName" column="stuName"/>
        <result property="stuAge" column="stuAge"/>
        <result property="graName" column="graName"/>
        <!-- stuSex 需要類型轉(zhuǎn)換, 則需要在標(biāo)簽內(nèi)注明 -->
        <result property="stuSex" column="stuSex" jdbcType="INTEGER" javaType="boolean"/>
    </resultMap>

6. 輸入?yún)?shù): parameterType

parameterType 類型為簡單類型: (8個基本類型+String)

  • #{任意值} 中 {} 花括號里面在標(biāo)簽中可以是任意值; 但是在接受實參時 #{} 會自動給String類型加上 ('') 單引號(自動類型轉(zhuǎn)換; 這樣可以防止SQL注入)

  • ${value} 中 {} 花括號的標(biāo)識符只能是 value; ${} 是原樣輸出(傳遞什么值就是什么值), 所以String字段的話, 需要手工加上單引號, 但是適合于動態(tài)排序(動態(tài)字段)


parameterType 類型為對象類型

  • #{} 和 ${} 花括號里面只能是屬性名

#{} 和 ${} 的相同之處

  • 都可以獲取對象的值, (甚至是嵌套類型的對象)

    • 如使用like進行模糊查詢: 使用#{}時, 傳值時需要傳入"%值%"; 而使用${}時, 在配置時就配置成 '%${stuName}%', 所以傳值時可以直接傳遞字符串

    • 如通過級聯(lián)屬性來查詢: 使用#{}時, 如 #{address.homeAddress} ; 使用${}時, 如 '${address.schoolAddress}'


parameterType 類型為 HashMap

  • 原理就是用Map中的key匹配占位符#{}中的字段, (所以就要求key的值和占位符得到內(nèi)容要一致)

  • StudentMapper.xml 中只需要修改 parameterType 的類型為 HashMap

  • 重寫相關(guān)的接口方法, 將接受參數(shù)類型改為 Map<Stirng, Object> 類型

  • 在實際使用中就是創(chuàng)建Map實例, 添加需要的參數(shù), 參數(shù)的 key 就是 SQL語句中需要的字段, 而 value 就是需要查詢的字段


MyBatis 調(diào)用存儲過程(輸入?yún)?shù)HashMap)

  • 在數(shù)據(jù)庫編寫兩個存儲過程
-- 查詢某個年級的學(xué)生總數(shù)
delimiter $ -- 將結(jié)束符改成$
CREATE procedure queryCountByGradeWithProcedure(OUT sCount int(10), IN gName varchar(255))
BEGIN
select count(1) INTO sCount from student where graName = gName;
END$

-- 根據(jù)學(xué)號刪除學(xué)生
CREATE procedure deleteStudentByStuNo(In stuno int(10))
begin 
  delete from student where stuNo = stuno;
end$
  • 在 studentMapper.xml 中編寫 select 以及 delete 標(biāo)簽; 需要注意的是存儲過程的調(diào)用方法以及顯式的聲明statementType="CALLABLE"; 輸入?yún)?shù)使用Map傳遞
  <!-- 調(diào)用[存儲過程]實現(xiàn)查詢  statementType默認(rèn)是STATEMENT, 如果是存儲過程, 就特別指定;
        存儲過程的輸入?yún)?shù), 在MyBatis中用Map(HashMap)來傳遞
    -->
    <select id="queryCountByGradeWithProcedure" statementType="CALLABLE" parameterType="HashMap">
        {call queryCountByGradeWithProcedure(#{gName, jdbcType=VARCHAR, mode=IN},#{sCount, jdbcType=INTEGER, mode=OUT})}
    </select>

    <delete id="deleteStudentByStuNoWithProcedure" parameterType="HashMap" statementType="CALLABLE">
        {call deleteStudentByStuNo(#{stuno mode=IN jdbcType=INTEGER})}
    </delete>
  • 編寫StudentMapper.java 接口的方法
  // 根據(jù)存儲過程查詢某個年級的學(xué)生總數(shù)
    void queryCountByGradeWithProcedure(Map<String, Object> map);
    // 根據(jù)存儲過刪除某個stuno學(xué)號的學(xué)生
    void deleteStudentByStuNoWithProcedure(Map<String, Object> map);
  • 編寫測試類測試上述存儲過程; 通過map的put傳入輸入?yún)?shù),通過map的get方法獲取輸出參數(shù)的值
    // 根據(jù)存儲過程查詢某個年級的學(xué)生總數(shù)
    public static void queryCountByGradeWithProcedure() throws IOException {
        SqlSession sqlSession = getSqlSession();
        StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
        Map<String, Object> map = new HashMap<>();
        map.put("gName", "class1");
        mapper.queryCountByGradeWithProcedure(map); // 調(diào)用存儲過程并傳入輸入?yún)?shù)
        Object sCount = map.get("sCount");
        System.out.println(map.size());
        System.out.println(map);
        System.out.println(map.get("gName") + " 年級的學(xué)生總數(shù)是: " + sCount);
        sqlSession.commit();
        sqlSession.close();
    }

    // 根據(jù)存儲過程刪除某個stuno的學(xué)生
    public static void deleteStudentByStuNoWithProcedure() throws IOException {
        SqlSession sqlSession = getSqlSession();
        StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
        Map<String, Object> map = new HashMap<>();
        map.put("stuno", 9);
        mapper.deleteStudentByStuNoWithProcedure(map);
        sqlSession.commit();
        sqlSession.close();
    }
  • 注意:

    • 如果報錯: No enum constant org.apache.ibatis.type.JdbcType.xx: 說明MyBatis不支持xx類型, 需要檢查類型

    • 存儲過程無論輸入?yún)?shù)是什么值, 語法上都需要用map 來傳遞該值

    • 主配置文件 conf.xml 中 transactionManager 配置的是 JDBC 的話, 增刪改都需要調(diào)用SqlSession對象的commit()方法手工提交事務(wù)


7. 輸出參數(shù): resultType

  • resultType 為簡單類型(8基本+String)

    • 在xxxMapper.xml 的SQL標(biāo)簽中定義 resultType為簡單類型或者字符串; 接口中增加抽象方法, 其返回值為resultType定義的簡單類型, 編寫測試方法測試
  • resultType 為對象/對象集合類型

    • 注意: 不管輸出的是對象還是對象類型的的集合, resultType 中只寫對象, 不寫集合類型的對象

    • 使用方法類似于使用基本類型

  • resultType 為HashMap

    • 指定resultType輸出類型為 HashMap, 并給SQL語句的每個字段加上別名

    • 接口的抽象方法中, 返回值類型為 Map<String, Object>

    • 測試方法中, 根據(jù) Mapper.xml 中SQL語句中定義的別名作為 Map 的 key 來獲取對應(yīng)的 value

    • 注意1: 一個 HashMap 對應(yīng)一個學(xué)生的多個元素(也就是多個屬性); 如果要查詢多個學(xué)生, 就需要多個HashMap

    • 注意2: 一般情況下使用 resultType, 但是實體類屬性和數(shù)據(jù)表的字段存在類型, 名字不一致時, 使用 resultMap

    • 注意3: 當(dāng)屬性名和字段名不一致時, 除了使用resultMap 之外, 還可以受用 resultType + HashMap

    • 注意4: 如果發(fā)現(xiàn)獲取的值中某個字段始終是默認(rèn)值, 則可能是表的字段和屬性的值類型或名字不一致導(dǎo)致的, 需要解決類型不一致或名字不一致問題

  • 使用 resultMap 解決屬性和字段不一致問題以及指定 resultMap 標(biāo)簽

  <!-- type:指定返回值的類型 id:唯一標(biāo)識符
    區(qū)分主鍵和非主鍵, 主鍵通過id指定, 非主鍵通過result指定
  先將數(shù)據(jù)庫字段的字段名改了: 
    alter table student rename column stuNo to id;
    alter table student rename column stuName to name;
   -->
  <resultMap id="resultMap01" type="Student">
        <id property="stuNo" column="id"/>
        <result property="stuName" column="name"/>
        <result property="stuAge" column="StuAge"/>
    </resultMap>

    <!-- 使用 resultMap: 需要指定resultMap 標(biāo)簽 -->
    <select id="queryStudentByIdWithResultMap" parameterType="int" resultMap="resultMap01">
        select id, name, stuAge from student where id = #{id}
    </select>

    <!--接著:寫接口, 寫接口的測試類 -->
  • 使用 resultType + HashMap 解決 屬性和字段不一致問題
    <!-- 使用resultType + HashMap 解決屬性和字段不一致問題; 原理就是給SQl的字段加別名-->
    <select id="queryStudentByIdWithHashMap" parameterType="int" resultType="HashMap">
        select id 'stuNo', name 'StuName', stuAge 'age' from student where stuNo = #{stuNo}
    </select>
    <!-- 
    1. 接口中的返回值需要設(shè)置成Map 或者 List<Map<String, Map>> 的形式
    2. 獲取值的時候, 通過 別名獲取
     -->

8. 動態(tài)SQL

if, where 以及 trim 標(biāo)簽

  • <\where> 標(biāo)簽會自動處理拼接SQL中第一個<\if>標(biāo)簽中的and, 但不會處理之后<\if>中的and

  • <\trim> 可以處理拼接SQL中第一個和最后一個and; 如: <\trim prefix="where" prefixOverrides="and">

    • prefix="where" 表示給拼接SQL加 where

    • prefixOverrides="and" 表示處理拼接SQL中第一個and

    • suffixOverrides="and" 表示處理拼接SQL中最后結(jié)尾多出來的and

  • if 標(biāo)簽中 test 測試的就是parameterType中指定的屬性

  <!-- SQL動態(tài)標(biāo)簽: 根據(jù)名字或年齡來查詢學(xué)生信息 -->
    <!-- SQL 語句中采用條件判斷, 根據(jù)測試方法傳入的值來決定后面跟那句話 -->
    <select id="queryStuByNaOrAgWithTag" parameterType="Student" resultType="Student">
        select stuNo, stuAge, stuName from student 
        <where>
            <if test="stuName != null and stuName != ''">
                and stuName = #{stuName} 
          </if>
          <if test="graName != null and graName != ''">
              and graName = #{graName} 
          </if>
          <if test="stuAge != null and stuAge != ''">
              and stuAge = #{stuAge}
          </if>
        </where>
    </select>

    <!-- 
    <trim> 標(biāo)簽可以處理拼接SQL中第一個和最后一個and; 也可以添加前綴
     -->
    <!-- prefixOverrides: 會自動處理多出來的第一個and -->
    <trim prefix="where" prefixOverrides="and">
      <if test="stuName != null and stuName != ''">
            and stuName = #{stuName} 
        </if>
        <if test="graName != null and graName != ''">
            and graName = #{graName} 
        </if>
        <if test="stuAge != null and stuAge != ''">
            and stuAge = #{stuAge}
        </if>
    </trim>

    <!-- suffixOverrides: 會自動處理多出來的最后一個and -->
    <trim prefix="where" suffixOverrides="and">
      <if test="stuName != null and stuName != ''">
            stuName = #{stuName} and 
        </if>
        <if test="graName != null and graName != ''">
           graName = #{graName} and 
        </if>
        <if test="stuAge != null and stuAge != ''">
           stuAge = #{stuAge} and 
        </if>
    </trim>

trim 處理update語句的示例

  • .mapper.xml示例
  <!-- 修改: 使用trim實現(xiàn)可以隨意修改 -->
    <update id="updateStudentByStuNoTrim" parameterType="Student">
        update student
            <trim prefix="set" suffixOverrides=",">
                <if test="stuName != null and stuName != ''">
                    stuName=#{stuName} ,
                </if>
                <if test="stuAge != null and stuAge != ''">
                    stuAge=#{stuAge} ,
                </if>
                <if test="graName != null and graName != ''">
                    graName=#{graName} ,
                </if>
                <if test="stuSex != null and stuSex != ''">
                    stuSex=#{stuSex} ,
                </if>
            </trim>
        where stuNo=#{stuNo}
    </update>
  • 接口及測試方法
    // 接口: 修改: 使用trim實現(xiàn)可以隨意修改
    Boolean updateStudentByStuNoTrim(Student student);

    // 測試方法: trim: 實現(xiàn)所以的修改
    public static void updateStudentByStuNoTrim() throws IOException {
        SqlSession sqlSession = getSqlSession();
        StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
        Student student = new Student();
        student.setStuNo(2);
//        student.setStuName("ls");
//        student.setStuAge(10);
        student.setGraName("class2");
        Boolean aBoolean = mapper.updateStudentByStuNoTrim(student);
        if(aBoolean){
            System.out.println("修改成功!");
        }
        sqlSession.commit();
        sqlSession.close();
    }

foreach 標(biāo)簽

  • foreach 可以迭代的類型: 數(shù)組, 對象數(shù)組, 集合, 屬性(Grade類, 類里面的屬性的類型就是一個List)

foreach 迭代屬性

  • foreach 標(biāo)簽迭代屬性時, 是將屬性的類型作為集合來迭代

  • foreach 標(biāo)簽屬性:

    • collection: 要迭代的屬性的集合

    • open: 即要迭代的語句的前部分, 左雙引號內(nèi)的左邊需要加空格(加了空格才能組成SQL語句, 否則兩個單詞連起來的, 就錯了)

    • close: 即要迭代的語句的后半部分 也是雙引號內(nèi)的右邊需要加空格

    • item: 就是給每次迭代出來的值起的名, 在方法體中需要#{item}來獲取值

    • separator: 就是分隔符, 沒有分隔符的話兩次迭代的item的值會拼接在一起, 所以必須指定分隔符

  <!-- 將多個元素放在對象的屬性中 -->
  <select id="queryStudentsWithNoInGrade" parameterType="Grade" resultType="Student">
        select * from student
        <where>
            <if test="array != null and array.size >0">
                <foreach collection="array" open=" and stuNo in (" close=") " item="stuNo" separator=",">
                    #{stuNo}
                </foreach>
            </if>
        </where>
    </select>

foreach 迭代簡單類型的數(shù)組

  • 無論編寫代碼時, 傳遞的是什么參數(shù)名, 在 mapper.xml 中必須使用 array 代替該簡單數(shù)組

  • 當(dāng)使用 foreach 遍歷數(shù)組的時候, 在if條件, foreach 中, 參數(shù)名固定寫法是 array

    <!-- 將多個元素放在數(shù)組中 -->
    <select id="queryStudentsWithArray" parameterType="int[]" resultType="Student">
        select * from student
        <where>
            <if test="array != null and array.length >0">
                <foreach collection="array" open=" and stuNo in (" close=")" item="stuNo" separator=",">
                    #{stuNo}
                </foreach>
            </if>
        </where>
    </select>

foreach 迭代集合

  • 無論編寫代碼時, 傳遞的是什么參數(shù)名, 在 mapper.xml 中必須使用 list 代替該集合
  <!-- 將多個元素放在集合中 List<Integer> -->
    <select id="queryStudentsWithList" parameterType="list" resultType="Student">
        select * from student
        <where>
            <if test="list != null and list.size >0">
                <foreach collection="list" open=" and stuNo in (" close=")" item="stuNo" separator=",">
                    #{stuNo}
                </foreach>
            </if>
        </where>
    </select>

foreach 迭代對象的數(shù)組

  • 無論編寫代碼時, 傳遞的是什么參數(shù)名, 在 mapper.xml 中必須使用 object[] 代替該對象的數(shù)組

  • 在使用時使用的是對象, 所以在具體的#{}中,使用的是對象.屬性

  <!-- 將多個元素放在對象數(shù)組中 -->
    <select id="queryStudentsWithObjectArray" parameterType="object[]" resultType="Student">
        select * from student
        <where>
            <if test="array != null and array.length >0">
                <foreach collection="array" open=" and stuNo in (" close=")" item="student" separator=",">
                    #{student.stuNo}
                </foreach>
            </if>
        </where>
    </select>

SQL片段(將相似的代碼提取出來)

  • 第一步: 提取相似代碼, 寫在 <sql> 標(biāo)簽中, 通過 id 來讓需要的地方引用

  • 第二部: 引用: 在SQL標(biāo)簽中通過<include>標(biāo)簽的refid來引用即可; 跨文件引用需要加上前綴 namespace

  <!-- 提起代碼片段 -->
    <sql id="baseSql" >
        select * from student
    </sql>

    <!-- 引用代碼片段 -->
    <select id="testBaseSql" resultType="Student">
        <include refid="baseSql" />
    </select>

9. 關(guān)聯(lián)查詢

  • MyBatis主要學(xué)習(xí)兩個, 一對一和一對多, (MyBatis認(rèn)為多對多的本質(zhì)就是一對多的變化)

一對一

  • 一對一通過類型擴展類和resultMap來實現(xiàn)一對一

業(yè)務(wù)擴展類實現(xiàn)一對一(不適用于大型項目)

  • 業(yè)務(wù)擴展類實現(xiàn)一對一的核心就是通過一個擴展類實現(xiàn)兩個類的屬性(其實也就是兩個表中的全部字段)

  • 這樣做的原因是resultType所代表的返回值類型不能寫兩個實體類

  • 先設(shè)計數(shù)據(jù)庫表以及實現(xiàn)表的映射類

  -- 創(chuàng)建 cardinfo 表
  create table studentCard (
    cardid int(10) primary key,
    cardinfo varchar(20)
    );
  -- 增加數(shù)據(jù)
  insert into studentCard values(1, 'zs info ...');
  insert into studentCard values(2, 'ls info ...'); 
  -- student 表增加cardid 欄
  alter table student add column cardid int(10);
  -- 增加外鍵關(guān)聯(lián)
  alter table student add constraint fk_student_stundetCard_cardid foreign key (cardid) references studentCard (cardid);
  -- Student 表增加cardid 數(shù)據(jù)
  update student set cardid = 1 where stuNo = 1;
  update student set cardid = 2 where stuNo = 2;
  • studentMapper.xml

    • 因為是要返回多個類的數(shù)據(jù), 但是 resultType 只能寫一個類, 所以就想辦法, 將兩個類的屬性整合到一個類當(dāng)中; (實現(xiàn)辦法就是繼承一個屬性多的類, 而重寫一個屬性少的類)
<!-- 利用業(yè)務(wù)擴展類實現(xiàn)一對一 -->
<select id="queryStudentByOneToOne" resultType="StudentBusiness" parameterType="int">
        select s.*, c.* from student s inner join studentcard c
            on s.cardid = c.cardid
            where s.stuNo = #{stuNo}
    </select>
  • 創(chuàng)建 StudentBusiness 類繼承Student類, 自然就繼承了Student的所有屬性, 并在此類中重寫 StudentCard 類的屬性; 這樣StudentBusiness 類就有了兩個類的屬性, 也就相當(dāng)于有了兩個數(shù)據(jù)表的字段
  public class StudentBusiness extends Student {
      private int cardId;
      private String cardInfo;

      public int getCardId() {
          return cardId;
      }

      public void setCardId(int cardId) {
          this.cardId = cardId;
      }

      public String getCardInfo() {
          return cardInfo;
      }

      public void setCardInfo(String cardInfo) {
          this.cardInfo = cardInfo;
      }

      @Override
      public String toString() {
          return "StudentBusiness{" +
                  "cardId=" + cardId +
                  ", cardInfo='" + cardInfo + '\'' +
                  "} " + super.toString();
      }
  }
  • 寫測試方法測試該接口

resultMap 實現(xiàn)一對一(重點在于 resultMap 標(biāo)簽)

  • 業(yè)務(wù)擴展類是通過一個擴展類包含兩個表的字段來完成一對一

  • resultMap 是通過兩個類互相持有對方為屬性成員來將兩個類關(guān)聯(lián), 就相當(dāng)于數(shù)據(jù)表中通過外鍵將兩個表關(guān)聯(lián)

  • 使用 resultMap 來指定返回值類型是, 需要單獨指定resultMap標(biāo)簽(type代表返回值實體類, 該實體類需要包含兩張表的所有字段), 并且主鍵使用 id, 非主鍵使用 result, 對象成員使用 association, 其中 association 的 javaType 指定該屬性的類型, property 指定屬性名

  • studentMapper.xml

  <resultMap id="student_card_map" type="Student">
        <!-- 學(xué)生的信息 -->
        <id property="stuNo" column="stuNo"/>
        <result property="stuName" column="stuName" />
        <result property="stuAge" column="stuAge"/>
        <result property="stuSex" column="stuSex"/>
        <result property="graName" column="graName"/>
        <!-- 一對一時, 對象成員使用 association映射, javaType指定該屬性的類型 -->
        <association property="studentCard" javaType="StudentCard">
            <id property="cardId" column="cardId"/>
            <result property="cardInfo" column="cardInfo"/>
        </association>
    </resultMap>

    <!-- resultMap 來完成一對一, 兩個類互相持有對方的成員變量 -->
    <select id="queryStudentByRMap" parameterType="int" resultMap="student_card_map">
        select s.stuNo, s.stuName, s.stuAge, s.stuSex, s.graName, c.cardid, c.cardinfo  from student s
          inner join studentcard c on s.cardid = c.cardid
          where s.stuNo = #{stuNo}
    </select>
  • 接口的方法中, 返回值是 Student, 測試時, 通過 this.studentCard.getCardId 就能獲取關(guān)聯(lián)表的屬性

一對多

  • 先設(shè)計數(shù)據(jù)庫表; 一個班級可以對應(yīng)多個學(xué)生, 品一下昨天想返回多個resultMap; 兩張表是通過 classId 的外鍵來關(guān)聯(lián)的
  -- 創(chuàng)建 StudentClass 表
  create table studentClass (
    classId int(10) primary key,
    className varchar(20)
    );
  -- 增加數(shù)據(jù)
  insert into studentClass values(1, 'g1');
  insert into studentClass values(2, 'g2'); 
  -- student 表增加classId 欄
  alter table student add column classId int(10);
  -- 增加外鍵關(guān)聯(lián)
  alter table student add constraint fk_student_stundetClass_classId foreign key (classId) references stundetClass (classId);
  -- Student 表增加classId數(shù)據(jù)
  update student set classId = 1 where stuNo = 1;
  update student set classId = 2 where stuNo = 2;
  • 創(chuàng)建 StudentClass.java 的實體類, 通過List<Student> students來完成一個班級對應(yīng)多個學(xué)生的映射關(guān)系

  • studentMapper.xml 中, 通過resultMap 指定返回的類型

    • 注意1: tpye 描述的是返回的類型, 所以type類型寫的是誰, resultMap就把誰當(dāng)作主類來配置

    • 注意2: 一對多的成員屬性的配置標(biāo)簽是 collection, 區(qū)別于一對一使用 association 來配置(記住)

    • 注意3: 成員屬性的類型是 List<Student>, 但是 javaType 不可以這樣配置, 所以就使用 ofType來配置該屬性的元素的類型

    • 注意4: 元素的類型使用javaType, 元素的屬性的類型使用 ofType

  <!-- 下面是一對多的表關(guān)聯(lián), 一對多通過resultMap 來完成關(guān)聯(lián)
        類 - 表的對應(yīng)關(guān)系
     -->
    <resultMap id="student_class_map" type="StudentClass">
        <!-- 因為type返回的主類是 studentClass, 所以先配置 StudentClass -->
        <id property="classId" column="classId"/>
        <result property="className" column="className"/>
        <!-- 配置成員屬性, 一對一是association, 一對多是 collection -->
        <!-- ofType: 描述該屬性的元素類型; javaType: 描述該屬性的類型
                    這里的屬性類型是 List<Student>, 但是 javaType里面不能這樣寫, 所以這里使用 ofType來寫該屬性的元素的類型
        -->
        <collection property="students" ofType="Student">
            <id property="stuNo" column="stuNo"/>
            <result property="stuName" column="stuName"/>
            <result property="stuAge" column="stuAge"/>
            <!-- 這里還可以接著配 Student 內(nèi)的 studentCard的信息, 使用的標(biāo)簽是 association -->
        </collection>
    </resultMap>
    <!-- 查詢g1班的信息, 和g1班的所有學(xué)生的信息 -->
    <select id="queryClassAndStudents"   resultMap="student_class_map" parameterType="int">
        select c.*, s.* from student s inner join studentclass c
                on s.classId = c.classId
                where c.classId = #{classId}
    </select>
  • 接口抽象方法及測試類
    // 表關(guān)聯(lián), 一對多, 通過resultMap de collection 子標(biāo)簽
    StudentClass queryClassAndStudents(int classId);

     // 查詢班級和班級對應(yīng)的學(xué)生
    public static StudentClass queryClassAndStudents(int classId) throws IOException {
        SqlSession sqlSession = getSqlSession();
        StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
        StudentClass studentClass = mapper.queryClassAndStudents(classId);
        System.out.println(studentClass);
        List<Student> students = studentClass.getStudents();
        for (Student student : students) {
            System.out.println(student.getStuNo() + " - "+ student.getStuName() +" - "+ student.getStuAge());
        }
        sqlSession.close();
        return studentClass;
    }

10. 使用 log4j 日志

  • 導(dǎo)入 log4j-1.2.17.jar (下載的MyBatis壓縮包的lib目錄中已有)

  • 在 conf.xml 通過配置開啟日志; 如果不開啟就會按照 (SJF4J -> Apache CommonsLogging -> Log4j 2 -> log4j -> JDK logging) 的順序來尋找日志

    <!-- 設(shè)置全局參數(shù)-->
    <settings>
        <!-- 開啟日志, 并指定使用的具體日志 -->
        <setting name="logImpl" value="LOG4J"/>
    </settings>
  • 編寫日志輸出文件 log4j.properties

    • log4j日志級別(從高到低): ERROR, WARN, INFO, DEBUG

    • 開發(fā)階段使用 DEBUG, 在生產(chǎn)階段使用 WARN 或者更高的級別

  ### 設(shè)置Logger輸出級別和輸出目的地 ###
  log4j.rootLogger=DEBUG, Console

  ### 生產(chǎn)環(huán)境使用
  # log4j.rootLogger=WARN, FILE

  #### Console 日志輸出到控制臺
  log4j.appender.Console=org.apache.log4j.ConsoleAppender
  log4j.appender.Console.layout=org.apache.log4j.PatternLayout
  ## 帶時間的
  # log4j.appender.Console.layout.ConversionPattern=%d [%t] %-5p [%c] - %m%n
  log4j.appender.Console.layout.ConversionPattern=[%t] %-5p - %m%n
  log4j.appender.Console.encoding=UTF-8

  #### File  日志輸出到文件; 需要再 rootLogger 后面增加: , File
  #log4j.appender.File = org.apache.log4j.FileAppender
  #log4j.appender.File.File = D://File//Java//JAVA_WEB//Log4J_Log
  #log4j.appender.File.layout = org.apache.log4j.PatternLayout
  #log4j.appender.File.layout.ConversionPattern =%d [%t] %-5p [%c] - %m%n 
  • 使用: 使用log4j日志的好處是可以觀察到MyBatis實際執(zhí)行SQL語句的過程以及SQL執(zhí)行時的參數(shù)及返回的結(jié)果等信息

11. 延遲加載(懶加載)

  • 如果不采用延遲加載(就是立即加載), 查詢時會將 一 和 多 都查詢, 如班級, 班級中的所有學(xué)生; 如果想要暫時只查詢 一, 而多的一方先不查詢, 而是在需要的時候再去查詢, 這個過程就是延遲加載

  • 理解: 延遲加載就是關(guān)聯(lián)查詢時, 通過配置實現(xiàn)按需查詢, 并不是將所有關(guān)聯(lián)的數(shù)據(jù)一次性都查出來, 而是在需要的時候再查詢(可以通過DEBUG來查看)


一對一延遲加載例子

  • 延遲加載, 需要將延遲查詢的表單獨放在一個Mapper.xml 中, 在 resultMap 的 association 標(biāo)簽內(nèi)通過 select屬性指定 namespace.id; 以及在column兩個表之間關(guān)聯(lián)的外鍵
  <!-- studentCardMapper.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">
  <mapper namespace="xyz.xmcs.Mapper.StudentCardMapper">

      <!-- 查詢學(xué)生證信息 -->
      <select id="queryCardById" parameterType="int" resultType="StudentCard">
          <!-- 查詢學(xué)生對應(yīng)的學(xué)生證
              根據(jù)cardId 查詢學(xué)生證的SQL在: xyz.xmcs.Mapper.StudentCardMapper.queryCardById
          -->
          select * from studentcard where cardid = #{cardId}
      </select>
  </mapper>

  <!-- StudentMapper.xml 中延遲加載一對一的resultMap 及 select 標(biāo)簽 -->
  <resultMap id="student_card_lazyLoad_map" type="Student">
        <!-- 學(xué)生的信息 -->
        <id property="stuNo" column="stuNo"/>
        <result property="stuName" column="stuName" />
        <result property="stuAge" column="stuAge"/>
        <result property="stuSex" column="stuSex"/>
        <result property="graName" column="graName"/>
        <!-- 一對一時, 對象成員使用 association映射, javaType指定該屬性的類型
            采用延遲加載, 在查詢學(xué)生是, 并不立即加載學(xué)生證
        -->
        <!-- 學(xué)生證 在需要的時候通過select查詢學(xué)生證信息-->
        <association property="studentCard" javaType="StudentCard" select="xyz.xmcs.Mapper.StudentCardMapper.queryCardById" column="cardId">
          <!--  <id property="cardId" column="cardId"/>
            <result property="cardInfo" column="cardInfo"/>-->
        </association>
    </resultMap>
    <!-- 下面的例子是延遲加載, 一對一的延遲加載 -->
    <select id="queryStudentOtOWithLazyLoad" parameterType="int" resultMap="student_card_lazyLoad_map">
        select * from student
    </select>

  • 在 conf.xml 總配置文件中配置延遲加載
  <!-- 設(shè)置全局參數(shù)-->
    <settings>
        <!-- 開啟延遲加載 -->
        <setting name="lazyLoadingEnabled" value="true"/>
        <!-- 關(guān)閉立即加載 -->
        <setting name="aggressiveLazyLoading" value="false"/>
        <!-- 上面的配置沒錯, 但是運行就報 Error creating lazy proxy.  Cause: java.lang.NullPointerException
             的異常, 看了網(wǎng)上的解決方案是將 MyBatis的jar包更新到3.5.1
        -->
    </settings>

    <!-- 將 StudentMapper.xml 添加進來 -->
    <mapper resource="xyz/xmcs/Mapper/studentCardMapper.xml"/>
  • 編寫接口方法及實現(xiàn)測試方法
    // 延遲加載, 一對一
    List<Student> queryStudentOtOWithLazyLoad();

  // 延遲加載, 一對一, 查詢?nèi)繉W(xué)生, 并延遲加載學(xué)生的學(xué)生證
    public static List<Student>  queryStudentOtOWithLazyLoad() throws IOException {
        SqlSession sqlSession = getSqlSession();
        StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
        List<Student> students = mapper.queryStudentOtOWithLazyLoad();
        for (Student student : students) {
            System.out.println(student.getStuNo() + " - "+student.getStuName()+ " - "+student.getStuAge());
            // 根據(jù)學(xué)生查詢學(xué)生證
             StudentCard card = student.getStudentCard();
             System.out.println(card.getCardId() + " - "+ card.getCardInfo());
        }
        sqlSession.close();
        return students;
    }

一對多延遲加載

  • 一對多和一對一的原理是一樣的, 首先確保再總配置文件中開啟了延遲加載, 然后在配置一的一方的select以及resultMap, 并且在resultMap中配置 collection子標(biāo)簽

  • 配置多的一方的 select 標(biāo)簽; 在一的一方的 connection 標(biāo)簽的 select 屬性中通過 namespace.id 引入多的一方的 select 標(biāo)簽; 在 collection 標(biāo)簽中, 通過 property指定多的一方的屬性名, 通過 column指定關(guān)聯(lián)的外鍵名, 通過ofType指定多的一方屬性的元素的類型 (應(yīng)為JavaType 不能指定集合類型)

  • studentClassMapper.xml 以及 studentMapper.xml 中的 select 標(biāo)簽

  <?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 指的是 StudentClassMapper 接口的全類名 -->
  <mapper namespace="xyz.xmcs.Mapper.StudentClassMapper">

      <resultMap id="student_class_lazyLoad_map" type="StudentClass">
        <!-- 因為type返回的主類是 studentClass, 所以先配置 StudentClass -->
        <id property="classId" column="classId"/>
        <result property="className" column="className"/>
        <collection property="students" ofType="Student" select="xyz.xmcs.Mapper.StudentMapper.queryAllStudentLazyByStuNo" column="classId">
            <!-- 再記一遍, 一對一: association; 一對多: collection
                 因為是對多, 所以類中的屬性是 List<Xxx> 類型的, 所以collection中不能用javaType; 而只能使用表示元素的類型的 ofType
                 select中指定需要延遲加載的SQL語句的 namespace.id; column指定外鍵欄; property 指定的是多的屬性名(類文件中的)
                -->
        </collection>
    </resultMap>
     <!-- 延遲加載: 一對多 -->
    <select id="queryClassAndStudentsLazy"   resultMap="student_class_lazyLoad_map">
      <!-- 先查班級, 以及全部班級的學(xué)生 -->
      select * from studentclass
    </select>
  </mapper>

  <!-- studentMapper.xml select 標(biāo)簽 -->
   <!-- 一對多: 延遲加載需要的: 查詢班級中的所有學(xué)生 -->
    <select id="queryAllStudentLazyByStuNo" resultType="Student"  parameterType="int">
        select * from student where classId = #{classId}
    </select>
  • 接口以及測試類
    // 延遲加載: 一對多; StudentClassMapper.java
    List<StudentClass> queryClassAndStudentsLazy();

    // 測試一對多延遲加載; TestStudent.java
    public static List<StudentClass> queryClassAndStudentsLazy() throws IOException {
        SqlSession sqlSession = getSqlSession();
        StudentClassMapper mapper = sqlSession.getMapper(StudentClassMapper.class);
        List<StudentClass> studentClasses = mapper.queryClassAndStudentsLazy();
        // 班級信息
        for (StudentClass aClass : studentClasses) {
            System.out.println(aClass);
            // 學(xué)生信息
            for(Student student : aClass.getStudents()){
                System.out.println(student.getStuNo() + " -- " + student.getStuName() + " -- " + student.getStuAge());
            }
        }
        sqlSession.close();
        return studentClasses;
    }

12. 查詢緩存

  • Mybatis 框架提供了緩存策略谜酒,通過緩存策略可以減少查詢數(shù)據(jù)庫的次數(shù)出牧,提升系統(tǒng)性能; 在 Mybatis 框架中 緩存分為一級緩存和二級緩存

一級緩存

  • 一級緩存: 第一次查詢后, 將查詢的結(jié)果保存在SqlSession對象里放在內(nèi)存中, 在接下來查詢時, 會先在內(nèi)存中的SqlSession對象查找是不是有緩存, 有則使用緩存, 沒有則再查詢數(shù)據(jù)庫

  • 一級緩存是 sqlSession 范圍的緩存淤年,只能在同一個 sqlSession 內(nèi)部有效; 使用commit會清空一級緩存

  • MyBatis 默認(rèn)是開啟一級緩存的, 如果用同一個SqlSession對象查詢相同的數(shù)據(jù), 則只會在第一次查詢時向數(shù)據(jù)庫發(fā)送SQL語句, 并將結(jié)果放在SqlSession中(作為緩存存在); 后續(xù)再次查詢該同樣的對象時, 則直接從緩存中查詢該對象即可(即省略了數(shù)據(jù)庫的訪問, 從而提高性能)


二級緩存

MyBatis 自帶二級緩存

  • 二級緩存: 同一個namespace生成的mapper對象; MyBatis 默認(rèn)沒有開啟二級緩存, 需要再 settings 中手動配置打開

    • 手動打開二級緩存: <\setting name="cacheEnabled" value="true"/> 再具體的Mapper.xml 中聲明開啟二級緩存 <\cache/>

    • 回顧: namespace 的值就是接口的全類名(包名.類名); 通過接口可以產(chǎn)生代理對象(即接口的對象), 所以即namespace決定了接口產(chǎn)生對象的產(chǎn)生

  • 二級緩存是將對象緩存在硬盤當(dāng)中(序列化:就是從內(nèi)存到硬盤; 反序列化:就是從硬盤到內(nèi)存); 所以準(zhǔn)備緩存的對象必須實現(xiàn)序列化(并且該對象的父類, 級聯(lián)屬性都需要實現(xiàn)序列化)

  • 觸發(fā)將對象寫入二級緩存的時機: SqlSession 對象的 closee() 方法

  • 只要產(chǎn)生的XxxMapper對象來自于同一個namespace, 則這些對象共享二級緩存(即用來區(qū)分兩個對象是否共享二級緩存: 只看是否來自于同一個namespace, 如果多個Mapper.xml的namespace值相同, 則它們之間也是共享二級緩存)

  • 如果同一個SqlSession對象進行多次相同的查詢, 則直接進入一級緩存; 如果不是同一個SqlSession對象進行多次相同的查詢(但均來自同一個nameSpace),則進入二級緩存


禁用, 清理二級緩存

  • 禁用某一條select的二級緩存, 只需要在相關(guān)的SQL語句中添加 usecache="false"

  • 禁用二級緩存示例

<!-- useCache: false表示該SQL語句禁用二級緩存, 不寫默認(rèn)就是true -->
    <select id="queryStudentByStuNo" resultType="Student" parameterType="int" useCache="false">
        select * from student where stuNo = #{stuNo}
    </select>
  • 清理緩存:

    • 第一種方式: 一級,二級緩存, 都是在 commit 的時候清理; 執(zhí)行增刪改的時候執(zhí)行commit后, 會清理掉緩存(目的(這樣設(shè)計的原因): 為了防止讀取臟數(shù)據(jù))

      • 注意: 這個 commit 不能說查詢自身的 commit, 而是 增刪改的 commit
    • 第二種方式: 在相關(guān)的select標(biāo)簽中添加 flushCache="true"

-命中率: 1:0.0 2:0.5 3:0.666 4:0.75

第三方二級緩存(ehcache)

  • 想要整個第三方(或者自定義)的二級緩存, 必須實現(xiàn)org.apache.ibatis.cache.Cache接口, 該接口的默認(rèn)實現(xiàn)類是PerpetalCache

  • 整合 EhCahce 需要三個jar包: Ehcache-core.jar; mybatis-Ehcache.jar; slf4j-api.jar

  • 編寫配置文件: Ehcache.xml

<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../config/ehcache.xsd">
  <!-- 當(dāng)二級緩存的對象超過內(nèi)存限制時 (緩存對象的個數(shù)>maxElementsInMemory),  存放入硬盤文件 -->
  <diskStore path="D:\Ehcache"/>
  <!-- 
    maxElementsInMemory: 設(shè)置內(nèi)存中緩存對象的個數(shù)
    maxElementsOnDisk: 設(shè)置在硬盤中緩存對象的個數(shù)
    eternal: 設(shè)置緩存是否永遠不過期
    overflowToDisk: 當(dāng)內(nèi)存中緩存的對象個數(shù)超過 maxElementsInMemory 的時候, 是否轉(zhuǎn)移到硬盤中
    timeToIdleSeconds: 當(dāng)兩次訪問超過該值的時候, 將緩存對象失效
    timeTlLiveSeconds: 一個緩存對象做多存放的時間(生命周期)
    diskExpiryThreadIntervalSeconds設(shè)置每隔多長時間, 通過一個線程來清理硬盤中的緩存
    memoryStoreEvictionPolicy: 當(dāng)內(nèi)存中緩存對象達到最大數(shù), 如果有新的對象加入緩存時, 移除已有緩存對象的策略;默認(rèn)是LRU(最近最少使用); LFU(最不常使用); FIFO(先進先出)
   -->
  <defaultCache
    maxElementsInMemory="1000"
    maxElementsOnDisk="1000000"
    eternal="false"
    overflowToDisk="false"
    timeToIdleSeconds="100"
    timeTlLiveSeconds="100"
    diskExpiryThreadIntervalSeconds="120"
    memoryStoreEvictionPolicy="LRU">
  </defaultCache>
</ehcache>
  • 開啟EhCahce二級緩存
<!-- StudentMapper.xml -->
<cache type="org.mybatis.caches.encache.EhcacheCache">
  <!-- 還可以設(shè)置自定義值, 覆蓋 Ehcache.xml 中的值 -->
  <property name="maxElementsInMemory" value="2000" />
  <property name="maxElementsOnDisk" value="3000" />
</cache>

13. MyBatis逆向工程

  • 表, 類, 接口, mapper.xml 四者密切相關(guān), 因此當(dāng)知道一個的時候, 其他三個理論上應(yīng)該可以自動給生成

由表 -> 生成其他的(實現(xiàn)步驟)

  • 需要下載: mybatis-generator-core.jar

  • 逆向工程配置文件: generator.xml

<!--from: 藍橋?qū)W院/mybatiis/逆向工程 -->
<?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>
    <context id="DB2Tables" targetRuntime="MyBatis3">
      <commentGenerator>
        <!--
        suppressAllComments屬性值:
          true:自動生成實體類腿时、SQL映射文件時沒有注釋
          true:自動生成實體類顶伞、SQL映射文件寸莫,并附有注釋
        -->
        <property name="suppressAllComments" value="true" />
      </commentGenerator>
    <!-- 數(shù)據(jù)庫連接信息 : Oracle
    <jdbcConnection driverClass="oracle.jdbc.OracleDriver"
        connectionURL="jdbc:oracle:thin:@127.0.0.1:1521:XE" 
        userId="system"  password="sa">
    </jdbcConnection>-->
    <!-- 數(shù)據(jù)庫鏈接信息: Mysql -->
    <jdbcConnection driverClass="com.mysql.cj.jdbc.Driver"
      connectionURL="url=jdbc:mysql://127.0.0.1:3306/MyBatisTest"
      userId="root" password="root">
    </jdbcConnection>
      <!-- 
      forceBigDecimals屬性值: 
        true:把數(shù)據(jù)表中的DECIMAL和NUMERIC類型开仰,解析為JAVA代碼中的java.math.BigDecimal類型 
        false(默認(rèn)):把數(shù)據(jù)表中的DECIMAL和NUMERIC類型脐往,解析為解析為JAVA代碼中的Integer類型 
      -->
    <javaTypeResolver>
      <property name="forceBigDecimals" value="false" />
    </javaTypeResolver>
      <!-- 
        targetProject屬性值:實體類的生成位置  
        targetPackage屬性值:實體類所在包的路徑
      -->
    <javaModelGenerator targetPackage="xyz.xmcs.entity"
                             targetProject=".\src">
      <!-- trimStrings屬性值:
        true:對數(shù)據(jù)庫的查詢結(jié)果進行trim操作
        false(默認(rèn)):不進行trim操作
       -->
    <property name="trimStrings" value="true" />
    </javaModelGenerator>
      <!-- 
        targetProject屬性值:SQL映射文件的生成位置  
        targetPackage屬性值:SQL映射文件所在包的路徑
      -->
    <sqlMapGenerator targetPackage="xyz.xmcs.mapper" 
      targetProject=".\src">
    </sqlMapGenerator>
    <!-- 生成動態(tài)代理的接口  -->
    <javaClientGenerator type="XMLMAPPER" targetPackage="xyz.xmcs.mapper" targetProject=".\src">
    </javaClientGenerator>
    <!-- 指定數(shù)據(jù)庫表  -->
    <table tableName="Student"> </table>
    <table tableName="studentCard"> </table>
    <table tableName="studentClass"> </table>
  </context>
</generatorConfiguration>
  • 編寫測試類
  public class Test {
    public static void main(Stirng[] args) {
      File file = new File("src/generator.xml"); // 配置文件
      List<String> warnings = new ArrayList<>(); // 警告的集合
      ConfigurationParser cp = new ConfigurationParser(warnings);
      Configuration config = cp.parseConfiguration(file);
      DefaultShellCallback callBack = new DefaultShellCallback(true);
      // 逆向工程核心類
      MyBatisGenerator generator = new MyBatisGenerator(config, callBack, warnings);
      generator.generate(null);
    } 
  }
  // 逆向工程說實在的不實用

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末蕊肥,一起剝皮案震驚了整個濱河市谒获,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌壁却,老刑警劉巖批狱,帶你破解...
    沈念sama閱讀 217,185評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異儒洛,居然都是意外死亡精耐,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,652評論 3 393
  • 文/潘曉璐 我一進店門琅锻,熙熙樓的掌柜王于貴愁眉苦臉地迎上來卦停,“玉大人,你說我怎么就攤上這事恼蓬【辏” “怎么了?”我有些...
    開封第一講書人閱讀 163,524評論 0 353
  • 文/不壞的土叔 我叫張陵处硬,是天一觀的道長小槐。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么凿跳? 我笑而不...
    開封第一講書人閱讀 58,339評論 1 293
  • 正文 為了忘掉前任件豌,我火速辦了婚禮,結(jié)果婚禮上控嗜,老公的妹妹穿的比我還像新娘茧彤。我一直安慰自己,他們只是感情好疆栏,可當(dāng)我...
    茶點故事閱讀 67,387評論 6 391
  • 文/花漫 我一把揭開白布曾掂。 她就那樣靜靜地躺著,像睡著了一般壁顶。 火紅的嫁衣襯著肌膚如雪珠洗。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,287評論 1 301
  • 那天若专,我揣著相機與錄音许蓖,去河邊找鬼。 笑死富岳,一個胖子當(dāng)著我的面吹牛蛔糯,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播窖式,決...
    沈念sama閱讀 40,130評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼蚁飒,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了萝喘?” 一聲冷哼從身側(cè)響起淮逻,我...
    開封第一講書人閱讀 38,985評論 0 275
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎阁簸,沒想到半個月后爬早,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,420評論 1 313
  • 正文 獨居荒郊野嶺守林人離奇死亡启妹,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,617評論 3 334
  • 正文 我和宋清朗相戀三年筛严,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片饶米。...
    茶點故事閱讀 39,779評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡桨啃,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出檬输,到底是詐尸還是另有隱情照瘾,我是刑警寧澤,帶...
    沈念sama閱讀 35,477評論 5 345
  • 正文 年R本政府宣布丧慈,位于F島的核電站析命,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜鹃愤,卻給世界環(huán)境...
    茶點故事閱讀 41,088評論 3 328
  • 文/蒙蒙 一簇搅、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧昼浦,春花似錦馍资、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,716評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽乌妙。三九已至使兔,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間藤韵,已是汗流浹背虐沥。 一陣腳步聲響...
    開封第一講書人閱讀 32,857評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留泽艘,地道東北人欲险。 一個月前我還...
    沈念sama閱讀 47,876評論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像匹涮,于是被迫代替她去往敵國和親天试。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,700評論 2 354

推薦閱讀更多精彩內(nèi)容

  • 1. 簡介 1.1 什么是 MyBatis 然低? MyBatis 是支持定制化 SQL喜每、存儲過程以及高級映射的優(yōu)秀的...
    笨鳥慢飛閱讀 5,519評論 0 4
  • 使用原生jdbc的問題 數(shù)據(jù)庫連接, 使用時就創(chuàng)建雳攘,不使用就立即釋放带兜,對數(shù)據(jù)庫進行頻繁地鏈接開啟和關(guān)閉,造成數(shù)據(jù)庫...
    wtmxx閱讀 722評論 1 3
  • 編寫日志輸出環(huán)境配置文件 在開發(fā)過程中吨灭,最重要的就是在控制臺查看程序輸出的日志信息刚照,在這里我們選擇使用 log4j...
    我沒有三顆心臟閱讀 6,557評論 0 33
  • 最近學(xué)習(xí)MyBatis這個輕量型持久層框架,感覺入門很簡單喧兄,但是深層次細節(jié)配置很多无畔。本篇筆記從 配置文件->例子入...
    Super超人閱讀 649評論 0 1
  • 1.mybatis 中 #{}和 ${}的區(qū)別是什么? 1. #將傳入的數(shù)據(jù)都當(dāng)成一個字符串繁莹,會對自動傳入的數(shù)據(jù)加...
    久伴_不離閱讀 3,811評論 0 4