1. 程式人生 > 實用技巧 >[Re] MyBatis-2

[Re] MyBatis-2

動態 SQL

如果你有使用 JDBC 或其他類似框架的經驗,你就能體會到根據不同條件拼接 SQL 語句有多麼痛苦。拼接的時候要確保不能忘了必要的空格,還要注意省掉列名列表最後的逗號。利用動態 SQL 這一特性可以徹底擺脫這種痛苦。

  • 動態 SQL 是 MyBatis 強大特性之一。極大的簡化我們拼裝 SQL 的操作。
  • 動態 SQL 元素和使用 JSTL 或其他類似基於 XML 的文字處理器相似。
  • MyBatis 採用功能強大的基於 OGNL 的表示式來簡化操作。

if、where

<!-- List<Teacher> getTeacherByConditions(Teacher teacher); -->
<select id="getTeacherByConditions" resultMap="teacherMap">
    SELECT * FROM teacher
    <!-- <where>可以刪除前面多餘的 AND:SQL 查詢條件的 AND 一定要寫前面 -->
    <!-- <bind> 可以將 OGNL 表示式的值繫結到一個變數中,方便後來引用這個變數的值(沒用) -->
    <bind name="_tname" value="'%'+tname+'%'">
    <where>
        <!-- ↓ 判斷條件,取出傳入的JavaBean的屬性,判斷其是否為空 -->
        <if test="tname!=null">
            AND id > #{_tname}
        </if>
        <if test="subject!=null &amp;&amp; !subject.equals(&quot;&quot;)">
            AND subject LIKE #{subject}
        </if>
        <if test="birthday!=null">
            AND birth_date &lt; #{birthday}
        </if>
    </where>
</select>

trim、sql (include)

<!-- 抽取可重用 SQL -->
<sql id="selectTeacher">SELECT * FROM teacher</sql>

<!-- List<Teacher> getTeacherByConditions(Teacher teacher); -->
<select id="getTeacherByConditions" resultMap="teacherMap">
    <!--引用可重用 SQL-->
    <include refid="selectTeacher"></include>
    <!-- <trim> 自定義字串擷取規則
        prefix: 新增一個字首
        suffix: 新增一個字尾
        prefixOverrides: 如果字串開頭為指定值,則去除
        suffixOverrides: 如果字串尾部為指定值,則去除
    -->
    <trim prefix="WHERE" suffixOverrides="AND">
        <if test="tid!=null">
            id > #{tid} AND
        </if>
        <if test="subject!=null &amp;&amp; !subject.equals(&quot;&quot;)">
            subject LIKE #{subject} AND
        </if>
        <if test="birthday!=null">
            birth_date &lt; #{birthday} AND
        </if>
    </trim>
</select>

choose (when, otherwise)

<!-- List<Teacher> getTeacherByOneCondition(Teacher teacher) -->
<select id="getTeacherByOneCondition" resultMap="teacherMap">
    SELECT * FROM teacher
    <where>
    <choose>
        <when test="tid!=null">
            id > #{tid}
        </when>
        <when test="subject!=null &amp;&amp; !subject.equals(&quot;&quot;)">
            subject LIKE #{subject}
        </when>
        <when test="birthday!=null">
            birth_date &lt; #{birthday}
        </when>
        <otherwise>1=1</otherwise>
    </choose>
    </where>
</select>

set

<!--
// 帶哪些欄位,更新哪些欄位 → 致使 @ModelAttribute 無使用場景
public int updateTeacher(Teacher teacher);
 -->
<update id="updateTeacher">
    UPDATE teacher
    <!--  UPDATE teacher SET <trim suffix="WHERE" suffixOverrides=","> -->
    <!-- set 去除末尾多餘的',' -->
    <set>
        <if test="tname!=null &amp;&amp; !tname.equals(&quot;&quot;)">
            tname=#{tname},
        </if>

        <if test="subject!=null &amp;&amp; !subject.equals(&quot;&quot;)">
            subject=#{subject},
        </if>

        <if test="address!=null &amp;&amp; !address.equals(&quot;&quot;)">
            address=#{address},
        </if>

        <if test="birthday!=null">
            birth_date=#{birthday},
        </if>
    </set>
    <where>
        tid = #{tid}
    </where>
</update>

foreach

動態 SQL 的另外一個常用的必要操作是需要對一個集合進行遍歷,通常是在構建 IN 條件語句的時候。

<!-- List<Teacher> getTeacherByIdIn(List<Integer> ids) -->
<select id="getTeacherByIdIn" resultMap="teacherMap">
    SELECT * FROM teacher WHERE tid IN
    <!--
    collection="" 要遍歷的集合
        遍歷的是 List:
        > index 儲存當前索引
        > item 將當前遍歷出的元素賦值給指定的變數
        遍歷的是 Map:
        > index 指定的變數就是儲存當前遍歷的 entry 的 key
        > item 指定的變數儲存當前遍歷的 entry 的 value
    ·······················
    separator="" 元素之間的分隔符
    open="" 以什麼開始
    close="" 以什麼結束
    -->
    <if test="ids.size > 0">
        <foreach collection="list" separator="," index="" item="id" open="(" close=")">
            #{id}
        </foreach>
    </if>
</select>

<!-- void addTeachers(@Param("teachers") List<Teacher> teachers) -->
<insert id="addTeachers">
    INSERT INTO teacher(tname, subject) VALUES
    <foreach collection="teachers" separator="," item="teacher">
        (#{teacher.tname}, #{teacher.subject})
    </foreach>
</insert>

<!--
MYSQL:
    除了將 VALUES 後的語句迴圈執行外,還可以將整條 SQL 語句都放入迴圈體中,語句末尾用';'
    除此之外,連線 DB 的 url 末尾還必須要增添一個連線屬性:allowMultiQueries=true
Oracle: 多條插入 SQL 要放在 begin...end; 中
 -->

OGNL

在 MyBatis 中,傳入的引數可以用來做判斷,額外的還有兩個內建引數:
    _parameter:
        若傳入了單個引數,就代表這個引數
        若傳入了多個引數,就代表這多個引數封裝起來的Map
    _databaseId: 如果配置了 databaseIdProvider,則該引數代表當前環境
        <if test="_databaseId == 'mysql'"> ... </if>
        <if test="_databaseId == 'oracle'"> ... </if>

快取機制

快取:暫時儲存一些資料,可以極大的提升查詢效率。MyBatis 系統中預設定義了兩級快取。

  • 一級快取:執行緒級別的快取;基於 SqlSession 級別的,也稱為本地快取;預設開啟
  • 二級快取:全域性範圍的快取;基於 namespace 級別的快取。除過當前執行緒的 SqlSession 能用外,其他也可以二級快取。需要手動開啟和配置

一級快取

簡述

  • 一級快取(local cache),即本地快取,作用域預設為 SqlSession。當 Session flush 或 close 後,該 Session 中的所有 Cache 將被清空。
  • 本地快取不能被關閉,但可以呼叫 clearCache() 來清空本地快取, 或者改變快取的作用域。
  • 在 Mybatis 3.1 之後,可以配置本地快取的作用域(在 mybatis.xml 中配置)

演示

@Test
public void test() {
    SqlSession sqlSession = sqlSessionFactory.openSession(true);
    TeacherMapper mapper = sqlSession.getMapper(TeacherMapper.class);
    Teacher teacher1 = mapper.getTeacherByTid(1);
    System.out.println(teacher1);
    Teacher teacher2 = mapper.getTeacherByTid(1);
    System.out.println(teacher2);
    System.out.println(teacher1 == teacher2); // true
}

同一次會話期間只要查詢過的資料都會儲存在當前 SqlSession 的一個 HashMap 中。

失效情況

每次查詢,先看一級快取(SqlSession 級別的快取) 中有沒有,如果沒有再去發新的 SQL。

  1. 不同的 SqlSession,使用不同的一級快取;只有在同一個 SqlSession 期間查詢到的資料會儲存在這個 SqlSession 的快取中;下次這個 SqlSession 再去查詢會先在它自己的快取中去找。
  2. 同一條 SQL 語句,但是引數不同,就會導致 key 有變化,所以還會發新的 SQL
  3. 在這個 SqlSession 執行期間執行任何一次增刪改操作,都會致使快取被清空
  4. 同一個 SqlSession 兩次查詢期間手動清空了快取

原始碼

public class PerpetualCache implements Cache {

  private String id;

  // 底層就是一個 Map
  private Map<Object, Object> cache = new HashMap<Object, Object>();

  public PerpetualCache(String id) {
    this.id = id;
  }

  @Override
  public String getId() {
    return id;
  }

  @Override
  public int getSize() {
    return cache.size();
  }

  @Override
  public void putObject(Object key, Object value) {
    cache.put(key, value);
  }

  @Override
  public Object getObject(Object key) {
    return cache.get(key);
  }

  @Override
  public Object removeObject(Object key) {
    return cache.remove(key);
  }

  @Override
  public void clear() {
    cache.clear();
  }

  @Override
  public ReadWriteLock getReadWriteLock() {
    return null;
  }

  @Override
  public boolean equals(Object o) {
    if (getId() == null) {
      throw new CacheException("Cache instances require an ID.");
    }
    if (this == o) {
      return true;
    }
    if (!(o instanceof Cache)) {
      return false;
    }

    Cache otherCache = (Cache) o;
    return getId().equals(otherCache.getId());
  }

  @Override
  public int hashCode() {
    if (getId() == null) {
      throw new CacheException("Cache instances require an ID.");
    }
    return getId().hashCode();
  }

}

BaseExecutor

@SuppressWarnings("unchecked")
@Override
public <E> List<E> query(MappedStatement ms, Object parameter
        , RowBounds rowBounds, ResultHandler resultHandler
        , CacheKey key, BoundSql boundSql) throws SQLException {
    ErrorContext.instance().resource(ms.getResource())
            .activity("executing a query").object(ms.getId());
    if (closed) {
      throw new ExecutorException("Executor was closed.");
    }
    if (queryStack == 0 && ms.isFlushCacheRequired()) {
      clearLocalCache();
    }
    List<E> list;
    try {
      queryStack++;
      // 快取中有沒有
      list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;
      if (list != null) { // 有
        handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
      } else { // 沒有
        list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
      }
    } finally {
      queryStack--;
    }
    if (queryStack == 0) {
      for (DeferredLoad deferredLoad : deferredLoads) {
        deferredLoad.load();
      }
      // issue #601
      deferredLoads.clear();
      if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
        // issue #482
        clearLocalCache();
      }
    }
    return list;
}

二級快取

簡述

  • 二級快取(second level cache),全域性作用域快取
  • MyBatis 提供二級快取的介面以及實現,快取實現要求 POJO 實現 Serializable 介面
  • 二級快取在 SqlSession 關閉或提交之後才會生效 → 這也就意味著不可能會出現一級快取和二級快取中有同一個資料!
    • 二級快取中:不關就會一直放在一級快取;只有一級快取關閉,資料才會被遷移至二級快取
    • 一級快取中:二級快取中沒有此資料,就會看一級快取,一級快取中沒有就會去查 DB。DB 的查詢結果放在一級快取中了
    • 任何時候都是先看二級快取,再看一級快取

原理

二級快取基於 namespace。沒有配置 <cache> 的 Mapper.xml 就沒有二級快取。並且,在二級快取中,不同 Mapper 的資料都是存放在各自的 Map 中。

快取相關屬性

  • eviction="LRU" 快取回收策略
    • LRU – 最近最少使用的:移除最長時間不被使用的物件。
    • FIFO – 先進先出:按物件進入快取的順序來移除它們。
    • SOFT – 軟引用:移除基於垃圾回收器狀態和軟引用規則的物件。
    • WEAK – 弱引用:更積極地移除基於垃圾收集器狀態和弱引用規則的物件。
  • flushInterval 重新整理間隔
    • 單位毫秒
    • 預設情況是不設定,也就是沒有重新整理間隔,快取僅僅呼叫語句時重新整理
  • size:引用數目
    • 正整數
    • 代表快取最多可以儲存多少個物件,太大容易導致記憶體溢位
  • readOnly:只讀
    • true:只讀快取;會給所有呼叫者返回快取物件的引用。因此這些物件不能被修改。這提供了很重要的效能優勢。
    • false:讀寫快取;會返回快取物件的拷貝(通過反序列化)。這會慢一些,但是安全,因此預設是 false。

快取有關設定

  1. 全域性 setting 的 cacheEnable:配置二級快取的開關
    <setting name="cacheEnabled" value="true"/>
    
  2. <select> 的 useCache 屬性:配置這個 select 是否使用二級快取 (一級快取一直是使用的)
  3. 增刪改標籤的 flushCache 屬性:增刪改預設 flushCache=true,查詢預設 flushCache=false。SQL 執行以後,會同時清空一級和二級快取
  4. sqlSession.clearCache() 只是用來清除一級快取
  5. 當在某一個作用域 (一級快取Session/二級快取Namespaces) 進行了 C/U/D 操作後,預設該作用域下所有 select 中的快取將被 clear。

整合第三方快取

  • MyBatis 定義了 Cache 介面方便我們進行自定義擴充套件。
  • EhCache 是一個純 Java 的程序內快取框架,具有快速、精幹等特點,是 Hibernate 中預設的 CacheProvider。
  • EhCache 使用步驟
    • 匯入 ehcache 包,以及整合包,日誌包
      ehcache-core-2.6.8.jar (ehcache 核心包)
      mybatis-ehcache-1.0.3.jar (ehcache 整合包)
      slf4j-api-1.6.1.jar
      slf4j-log4j12-1.6.2.jar
      
    • 編寫 ehcache.xml 配置檔案
      <?xml version="1.0" encoding="UTF-8"?>
      <ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:noNamespaceSchemaLocation="../config/ehcache.xsd">
          <!-- 磁碟儲存路徑 -->
          <diskStore path="U:\ehcache" />
          <defaultCache
              maxElementsInMemory="3000"
              maxElementsOnDisk="10000000"
              eternal="false"
              overflowToDisk="true"
              timeToIdleSeconds="120"
              timeToLiveSeconds="120"
              diskExpiryThreadIntervalSeconds="120"
              memoryStoreEvictionPolicy="LRU">
          </defaultCache>
      </ehcache>
      
    • 在 Mapper.xml 中配置 cache 標籤
      <cache type="org.mybatis.caches.ehcache.EhcacheCache"></cache>
      
  • Tips
    • POJO 可以不用實現 Serializable 介面
    • 參照快取:若想在名稱空間中共享相同的快取配置和例項。可用 <cache-ref> 來引用另外一個快取。
      <cache-ref namespace="cn.edu.nuist.mapper.TeacherMapper" />
      

SSM 整合

導包

Spring

[AOP核心]
    com.springsource.net.sf.cglib-2.2.0.jar
    com.springsource.org.aopalliance-1.0.0.jar
    com.springsource.org.aspectj.weaver-1.6.8.RELEASE.jar
    spring-aspects-4.0.0.RELEASE.jar

[IOC核心]
    commons-logging-1.1.3.jar
    spring-aop-4.0.0.RELEASE.jar
    spring-beans-4.0.0.RELEASE.jar
    spring-context-4.0.0.RELEASE.jar
    spring-core-4.0.0.RELEASE.jar
    spring-expression-4.0.0.RELEASE.jar

[JDBC核心]
    spring-jdbc-4.0.0.RELEASE.jar
    spring-orm-4.0.0.RELEASE.jar
    spring-tx-4.0.0.RELEASE.jar

SpringMVC

[Ajax]
    jackson-annotations-2.1.5.jar
    jackson-core-2.1.5.jar
    jackson-databind-2.1.5.jar

[資料校驗]
    hibernate-validator-5.0.0.CR2.jar
    hibernate-validator-annotation-processor-5.0.0.CR2.jar
    classmate-0.8.0.jar
    jboss-logging-3.1.1.GA.jar
    validation-api-1.1.0.CR1.jar

[上傳下載]
    commons-fileupload-1.2.1.jar
    commons-io-2.0.jar

[jstl]
    jstl.jar
    standard.jar

[SpringMVC核心]
    spring-web-4.0.0.RELEASE.jar
    spring-webmvc-4.0.0.RELEASE.jar

MyBatis

[MyBatis核心]
    mybatis-3.4.1.jar
    mybatis-spring-1.3.0.jar

[ehcache整合]
    ehcache-core-2.6.8.jar
    mybatis-ehcache-1.0.3.jar
    log4j-1.2.17.jar
    slf4j-api-1.7.21.jar
    slf4j-log4j12-1.7.21.jar

database

mysql-connector-java-5.1.37-bin.jar
c3p0-0.9.1.2.jar

寫配置

web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app ...>
    <welcome-file-list>
        <welcome-file>index.jsp</welcome-file>
    </welcome-file-list>

    <!-- 編碼過濾器 -->
    <filter>
        <filter-name>CharacterEncodingFilter</filter-name>
        <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
        <init-param>
            <param-name>encoding</param-name>
            <param-value>utf-8</param-value>
        </init-param>
        <init-param>
            <param-name>forceEncoding</param-name>
            <param-value>true</param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>CharacterEncodingFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

    <!-- REST 過濾器 -->
    <filter>
        <filter-name>HiddenHttpMethodFilter</filter-name>
        <filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>HiddenHttpMethodFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

    <!-- 配置 spring 容器啟動 -->
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:spring/applicationContext.xml</param-value>
    </context-param>

    <!-- Bootstraps the root web application context before servlet initialization -->
    <listener>
        <listener-class>
            org.springframework.web.context.ContextLoaderListener
        </listener-class>
    </listener>

    <!-- 配置 SpringMVC 前端控制器 -->
    <servlet>
        <servlet-name>springDispatcherServlet</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:spring/applicationContext-mvc.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>

    <!-- Map all requests to the DispatcherServlet for handling -->
    <servlet-mapping>
        <servlet-name>springDispatcherServlet</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>
</web-app>

SpringMVC 配置檔案

applicationContext-mvc.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans ...>
    <context:component-scan base-package="cn.edu.nuist" use-default-filters="false">
    <context:include-filter type="annotation"
            expression="org.springframework.stereotype.Controller"/>
    </context:component-scan>

    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/pages/"></property>
        <property name="suffix" value=".jsp"></property>
    </bean>

    <!-- 檔案上傳解析器 -->
    <bean id="multipartResolver"
            class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
        <property name="defaultEncoding" value="UTF-8"></property>
        <property name="maxUploadSize" value="#{1024*1024*20}"></property>
    </bean>

    <!-- 掃靜態 -->
    <mvc:default-servlet-handler/>
    <!-- 掃動態 -->
    <mvc:annotation-driven></mvc:annotation-driven>
</beans>

Spring配置檔案

applicationContext.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans ...>
    <!-- 1. 包掃描 -->
    <context:component-scan base-package="cn.edu.nuist">
        <context:exclude-filter type="annotation"
                expression="org.springframework.stereotype.Controller"/>
    </context:component-scan>

    <!-- 2. 匯入外部配置檔案 -->
    <context:property-placeholder location="classpath:dbconfig.properties"/>

    <!-- 3. 配資料來源 -->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="driverClass" value="${jdbc.driverClass}"/>
        <property name="jdbcUrl" value="${jdbc.jdbcUrl}"/>
        <property name="user" value="${jdbc.user}"/>
        <property name="password" value="${jdbc.password}"/>
        <property name="maxPoolSize" value="${jdbc.maxPoolSize}"></property>
        <property name="minPoolSize" value="${jdbc.minPoolSize}"></property>
    </bean>

    <!-- 4. 整合 MyBatis !!! -->
    <!-- 4.1 根據配置檔案,生產 SqlSessionFactory 的工廠 bean -->
    <bean id="sqlSessionFactoryBean" class="org.mybatis.spring.SqlSessionFactoryBean">
        <!-- 指定 MyBatis 全域性配置檔案的位置 -->
        <property name="configLocation" value="classpath:mybatis/mybatis-config.xml"></property>
        <!-- 引用 Spring 建立的資料來源 (事務控制是 Spring 在做) -->
        <property name="dataSource" ref="dataSource"></property>
        <!-- 指定 Mapper 對映檔案的位置 -->
        <property name="mapperLocations" value="classpath:mybatis/mapper/*.xml"></property>
    </bean>

    <!-- 4.2 該 bean 把每一個 Mapper<I> 代理實現加入到 IOC 容器中 -->
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <!-- 指定 Mapper<I> 所在包 -->
        <property name="basePackage" value="cn.edu.nuist.mapper"></property>
    </bean>
    <!-- <mybatis-spring:scan base-package="cn.edu.nuist.mapper"> -->

    <!-- 5. [事務控制] 配置事務管理器,讓其控制資料來源中連線的關閉和提交 -->
    <bean id="transactionManager"
            class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"></property>
    </bean>

    <!-- 6. 基於 XML 配置事務,詳見 [Re:Spring-2]#7.6 -->
    <aop:config>
        <!-- 配置切入點表示式((哪些方法切入事務) -->
        <aop:pointcut expression="execution(* cn.edu.nuist.service.*.*(..))" id="txPoint"/>
        <aop:advisor advice-ref="myTx" pointcut-ref="txPoint"/>
    </aop:config>

    <!-- 7. 事務增強(屬性)-->
    <tx:advice id="myTx" transaction-manager="transactionManager">
        <!-- 配置事務屬性 -->
        <tx:attributes>
            <tx:method name="*" rollback-for="java.lang.Exception"/>
            <tx:method name="get*" read-only="true"/>
        </tx:attributes>
    </tx:advice>
</beans>

mybatis 配置檔案

<?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>
    <plugins>
        <plugin interceptor="com.github.pagehelper.PageInterceptor"></plugin>
    </plugins>
</configuration>

其他配置檔案

dbconfig.properties

jdbc.user=root
jdbc.password=root
jdbc.jdbcUrl=jdbc:mysql:///test
jdbc.driverClass=com.mysql.jdbc.Driver
jdbc.maxPoolSize=100
jdbc.minPoolSize=10

測試

MBG

MyBatis Generator,簡稱 MBG,是一個專門為 MyBatis 框架使用者定製的程式碼生成器,可以快速的根據表生成對應的對映檔案、介面以及 bean 類。支援基本的增刪改查,以及 QBC 風格的條件查詢。但是表連線、儲存過程等這些複雜 SQL 的定義需要我們手工編寫。

mybatis-generator-core-1.3.2.jar

MBG 配置檔案

  • javaModelGenerator 配置 JavaBean 的生成策略
  • sqlMapGenerator 配置 SQL 對映檔案的生成策略
  • javaClientGenerator 配置 Mapper 介面的生成策略
  • table 配置要逆向解析的資料表
    • tableName:表名
    • domainObjectName:對應的 JavaBean 名字
<?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>
    <!--
    targetRuntime
        MyBatis3Simple:基礎版 CRUD
        MyBatis3:複雜版(帶動態 SQL) CRUD
     -->
    <context id="DB2Tables" targetRuntime="MyBatis3">
        <commentGenerator>
            <property name="suppressAllComments" value="true"/>
        </commentGenerator>

        <!-- jdbcConnection:指導連線到哪個資料庫 -->
        <jdbcConnection
                driverClass="com.mysql.jdbc.Driver"
                connectionURL="jdbc:mysql:///test"
                userId="root"
                password="root">
        </jdbcConnection>

        <javaTypeResolver>
            <property name="forceBigDecimals" value="false" />
        </javaTypeResolver>

        <!--
        javaModelGenerator:生成 POJO
            targetPackage:生成的 POJO 放在哪個包
            targetProject:放在哪個工程下
        -->
        <javaModelGenerator
                targetPackage="cn.edu.nuist.bean"
                targetProject=".\src">
            <property name="enableSubPackages" value="true" />
            <property name="trimStrings" value="true" />
        </javaModelGenerator>

        <!--sqlMapGenerator:SQL 對映檔案生成器;指定對映檔案生成的地方  -->
        <sqlMapGenerator
                targetPackage="cn.edu.nuist.mapper"
                targetProject=".\conf">
            <property name="enableSubPackages" value="true" />
        </sqlMapGenerator>

        <!-- javaClientGenerator:Mapper<I> 生成的地方 -->
        <javaClientGenerator type="XMLMAPPER"
                targetPackage="cn.edu.nuist.mapper"
                targetProject=".\src">
            <property name="enableSubPackages" value="true" />
        </javaClientGenerator>

        <!--
        table:指定要逆向生成哪個資料表
            tableName="t_cat":表名
            domainObjectName="":這個表對應的物件名
        -->
        <table tableName="teacher" domainObjectName="Teacher"></table>
        <table tableName="emp" domainObjectName="Employee"></table>
        <table tableName="dept" domainObjectName="Department"></table>
    </context>
</generatorConfiguration>

執行程式碼生成器生成程式碼

public class TestMBG {
    public static void main(String[] args) throws Exception {
        List<String> warnings = new ArrayList<String>();
        boolean overwrite = true;
        File configFile = new File("mbg.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);
        System.out.println("生成了!");
    }
}