[Re] MyBatis-2(动态SQL+缓存+SSM整合+MBG)

动态 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 会查找类路径下的 Mapper<I>[映射器],并自动将它们创建成 MapperFactoryBean -->
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <!-- 为映射器接口文件设置基本的包路径,每个映射器将会在指定的包路径中递归地被搜索到 -->
        <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>
public class MapperFactoryBean<T> extends SqlSessionDaoSupport implements FactoryBean<T>
  1. MapperFactoryBean 创建的代理类实现了 Mapper<I>,并且注入到应用程序中。因为代理创建在运行时环境中(Runtime),那么指定的映射器必须是一个接口,而不是一个具体的实现类。
  2. 数据映射器 Mapper<I> 不需要显示写一个注解交给 Spring 管理,然后在 Service 层进行注入。因为 Mapper<I> 其实是供 MapperFactoryBean 创建的代理类来实现的,然后自动放到 Spring 的 IOC 容器中,在 Service 实现中注入的是 MapperFactoryBean 创建的代理类。

https://www.cnblogs.com/Joe-Go/p/10256241.html
https://www.cnblogs.com/xiaolang8762400/p/7399274.html

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("生成了!");
    }
}
原文地址:https://www.cnblogs.com/liujiaqi1101/p/13696918.html