快速入门MyBatis第二天(共两天)

1,025 阅读29分钟

今日内容

1.动态SQL语句:

(1) Xml方式。

(2) Annotation方式。

2. MyBatis的缓存。

3. MyBatis的关联查询-多表查询。

4. MyBatis逆向工程。

1. 动态SQL语句

1.1. 动态SQL是什么

相对与固定SQL语句。根据参数不同组成不同结构的SQL语句。这种根据参数的不同而不同的SQL语句,我们称为动态SQL语句。

1.2. 动态SQL有什么用

1.根据条件组装不同结构的SQL语句,可以提高SQL代码的重用性。

2.满足某些特定需求,如:条件判断查询。

1.3. 基于XML的实现

1.3.1. 标签包括

<sql>:用于声明SQL语句块,在操作标签中通过<include>标签引入。

<if>:类似java if(){},用于判断。

<foreach>:类似java的foreach循环,一般用于批量处理的SQL语句,如批量更新、插入、删除。

<trim>:切割标签,主要用于切割关键字的头和尾的字符。新版Mybatis的使用几率很少。

<set>:使用set标签就是SQL语言的set关键字,可以在update的时候,设置set关键字后面的更新字段,逗号可以自动忽略。

<where>:使用where标签作为SQL语言的where关键字,如果where后面的条件都不成立,忽略where关键字。

<choose> <when><otherwise> : java的if else if... else。

标签

属性

说明

<sql>

id属性

id:唯一标识一个sql语句

<include>

refid

引用的sql片段的id属性

<if>

test属性

test:判断条件

<where>

搭配if标签使用

<set>

搭配if标签使用

<trim>

prefix

prefixOverrides

suffixOverrides

前缀名(SETWHERE

过滤的前缀(ANDOR

过滤的后缀(,)

<foreache>

collecton属性、

item属性、
open属性、
close属性、
separator属性

collection:遍历的参数(集合)。

item:遍历到的元素。

open:遍历前拼接sql片段。

close:遍历后拼接的sql片段。

separator:每次遍历的分隔符。

1.3.2. 接口文件

package org.cjw.mapper;import org.apache.ibatis.annotations.Param;import org.cjw.pojo.User;import java.util.List;public interface UserMapper {    /**     * 根据条件查询结果     * @param user     * @return     */    List<User> selectByCondition(User user);    /**     * 根据条件查询结果总数     * @param user     * @return     */    Long selectTotalByCondition(User user);    /**     * 修改用户     * @param user     * @return     */    int updateUserByNotNull(User user);    /**     * 批量删除用户     * @param ids     * @return     */    int deleteByIds(@Param("ids")Integer[] ids);    /**     * 批量插入     * @param users     * @return     */    int insertByBatch(@Param("users")List<User> users);}

1.3.3. 映射文件

<?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="org.cjw.mapper.UserMapper">    <!-- 多行查询   resultType : 只是返回的当行数据封装的对象类型,无论单行还是多行查询都必须返回对应的实体类型User   由于配置文件中已经设置了别名,所以这里使用别名   -->    <select id="selectByCondition" parameterType="User" resultType="User">        <!--            select * from tb_user where name like concat('%',#{name},'%') or age = #{age}            上述SQL语句的语义来说,是一个静态SQL语句,一开始已经确定SQL的语义            不管有没有数据,都会对全部数据进行修改,如果某一个数据没有,name会自动设置为null            不符合实际场景            解决方案: 使用MyBatis的动态SQL语句         -->        select * from tb_user        <!--           <include refid=""></include>           包含引入sql片段               refid :被引入的sql片段的id值        -->        <include refid="condition_sql"/>    </select>    <select id="selectTotalByCondition" parameterType="User" resultType="Long">        select count(1) from tb_user        <include refid="condition_sql"/>    </select>    <!--        <sql id=""></sql>        抽取sql片段            id属性:片段的唯一标识,以供其他地方使用     -->    <sql id="condition_sql">        <!-- 动态SQL语句            <where>标签                在where内部编写条件,                1,如果只要满足一个条件<where>标签会自动拼接 WHERE 关键字拼接上对应的条件                2,如果条件前面有 OR|AND 关键字,但是是第一个条件,那么会自动删除出这个关键字,以保证语法正确                3,如果一个条件都没有,那么就相当于查询所有数据             -->        <where>            <if test="name != '' and name != null">                name like concat('%', #{name}, '%')            </if>            <if test="age != '' and age != null">                and age = #{age}            </if>            <if test="email != '' and email != null">                and email = #{email}            </if>            <if test="password != '' and password != null">                and password = #{password}            </if>        </where>        <!--            另一种写法是使用trim标签:            <trim>标签,开发者可以自定义条件,既可以指定where条件也可以指定set关键字条件            <trim prefix="WHERE" prefixOverrides="AND | OR">            prefix : 前缀,                当前如果是条件就用  WERHE                如果使用修改就用  SET            prefixOverrides :在 WHERE 关键字后面的第一个条件,如果是 AND|OR 会自动去掉         -->        <!--<trim prefix="WHERE" prefixOverrides="AND|OR">            <if test="name != '' and name != null">                name like concat('%', #{name}, '%')            </if>            <if test="age != '' and name != null">                and age = #{age}            </if>            <if test="email != '' and email != null">                and email = #{email}            </if>            <if test="password != '' and password != null">                and password = #{password}            </if>        </trim>-->    </sql>    <update id="updateUserByNotNull" parameterType="User">        <!--            update tb_user set name = #{name}, password= #{password}, age = #{age} where id = #{id}            上述SQL语句的语义来说,是一个静态SQL语句,一开始已经确定SQL的语义            不管有没有数据,都会对全部数据进行修改,如果某一个数据没有,name会自动设置为null            不符合实际场景            解决方案: 使用MyBatis的动态SQL语句,<set>标签、<trim>标签       -->        <!-- set标签会自动的过滤掉多余的逗号 -->        <!--update tb_user        <set>            <if test="name != '' and name != null">name = #{name},</if>            <if test="age != '' and age != null">age = #{age},</if>            <if test="email != '' and email != null">email = #{email},</if>            <if test="password != '' and password != null">password = #{password}</if>        </set>        where id = #{id}-->        <!--            prefix : 前缀,           当前如果是 条件就用  WERHE           如果使用修改 就用  SET           prefixOverrides :如果在 WHRE 关子健 后面的第一个条件,如果是 AND|OR 会自动去掉           suffixOverrides :如果是最后一个条件, 如果是多余的逗号(,) 会自动去掉         -->        update tb_user        <trim prefix="SET" suffixOverrides=",">            <if test="name != '' and name != null">name = #{name},</if>            <if test="age != '' and age != null">age = #{age},</if>            <if test="email != '' and email != null">email = #{email},</if>            <if test="password != '' and password != null">password = #{password}</if>        </trim>        where id = #{id}    </update>    <delete id="deleteByIds" parameterType="List">        <!-- delete from tb_user where id in (1,2,3) -->        <!--            <foreach collection="" open="" close="" item="" separator="">标签体内容</foreach>                MyBatis的for循环标签                collection:循环集合                open:起始括号(                close:结束括号 )                item:集合每次循环的数据对应的变量                separator:分割符号: (1,2,3) 数值与数值之间的分隔符 ,逗号         -->        delete from tb_user        where id in        <foreach collection="ids" item="id" open="(" close=")" separator=",">            #{id}        </foreach>    </delete>    <insert id="insertByBatch">        insert into tb_user (name, age, email, password)        values        <foreach collection="users" item="user" separator=",">            (#{user.name}, #{user.age}, #{user.email}, #{user.password})        </foreach>    </insert></mapper>

1.3.4. 测试代码

@Testpublic void testSelectByCondition() {    SqlSession session = MyBatisUtil.openSesion();    UserMapper userMapper = session.getMapper(UserMapper.class);    User user = new User();    user.setName("张");    List<User> users = userMapper.selectByCondition(user);    for (User temp : users) {        System.out.println(temp.getName());    }    session.close();}@Testpublic void testSelectTotalByCondition() {    SqlSession session = MyBatisUtil.openSesion();    UserMapper userMapper = session.getMapper(UserMapper.class);    User user = new User();    user.setName("张");    Long total = userMapper.selectTotalByCondition(user);    System.out.println("total: " + total);    session.close();}@Testpublic void testUpdateUserByNotNull() {    SqlSession session = MyBatisUtil.openSesion();    try {        UserMapper userMapper = session.getMapper(UserMapper.class);        User user = new User();        user.setId(1L);        user.setName("zhangsan");        int row = userMapper.updateUserByNotNull(user);        System.out.println("影响的行数: " + row);        session.commit();    } catch (Exception e) {        e.printStackTrace();        session.rollback();    } finally {        session.close();    }}@Testpublic void testDeleteByIds() {    SqlSession session = MyBatisUtil.openSesion();    try {        UserMapper userMapper = session.getMapper(UserMapper.class);        Integer[] ids = {1, 2, 3, 4};        int row = userMapper.deleteByIds(ids);        System.out.println("影响的行数: " + row);        session.commit();    } catch (Exception e) {        e.printStackTrace();        session.rollback();    } finally {        session.close();    }}@Testpublic void testInsertByBatch() {    SqlSession session = MyBatisUtil.openSesion();    try {        UserMapper userMapper = session.getMapper(UserMapper.class);        List<User> users = new ArrayList<>();        users.add(new User(null, "张三", 13, "zhangsan@126.com", "zhangsan123123"));        users.add(new User(null, "张四", 14, "zhangsi@163.com", "zhangsi123123"));        users.add(new User(null, "张五", 15, "zhangwu@qq.com", "zhangwu123123"));        users.add(new User(null, "赵六", 16, "zhangliu@126.com", "zhaoliu123123"));        userMapper.insertByBatch(users);        session.commit();    } catch (Exception e) {        e.printStackTrace();        session.rollback();    } finally {        session.close();    }}


1.3.5测试结果






1.4. 基于注解方式实现

动态sql除了支持xml方式以外,还支持使用纯注解的方式。主要一下五个注解对应动态sql语句的类文件。

1. @SelectProvider 动态查询SQL语句对应注解。

2. @InsertProvider 动态插入SQL语句对应注解。

3. @UpdateProvider 动态修改SQL语句对应注解。

4. @DeleteProvider 动态删除SQL语句对应注解。

5. @Param 动态获取参数

1.4.1. 接口映射文件

package org.cjw.mapper;import org.apache.ibatis.annotations.*;import org.cjw.pojo.User;import java.util.List;public interface UserMapper {    /**     * 根据条件查询结果     *     * @param user     * @return     */    @SelectProvider(type = UserProvider.class, method = "selectByCondition")    List<User> selectByCondition(User user);    /**     * 根据条件查询结果总数     *     * @param user     * @return     */    @SelectProvider(type = UserProvider.class, method = "selectTotalByCondition")    Long selectTotalByCondition(User user);    /**     * 修改用户,参数不为空的数据才会修改     *     * @param user     * @return     */    @UpdateProvider(type = UserProvider.class, method = "updateUserByNotNull")    int updateUserByNotNull(User user);    /**     * 批量删除用户     *     * @param ids     * @return     */    @DeleteProvider(type = UserProvider.class, method = "deleteByIds")    int deleteByIds(@Param("ids") Integer[] ids);    /**     * 批量插入     *     * @param users     * @return     */    @InsertProvider(type = UserProvider.class, method = "testInsertByBatch")    int insertByBatch(@Param("users") List<User> users);}

1.4.2. 动态sql语句文件

package org.cjw.mapper;import org.apache.ibatis.annotations.*;import org.cjw.pojo.User;import java.util.List;public interface UserMapper {    /**     * 根据条件查询结果     *     * @param user     * @return     */    @SelectProvider(type = UserProvider.class, method = "selectByCondition")    List<User> selectByCondition(User user);    /**     * 根据条件查询结果总数     *     * @param user     * @return     */    @SelectProvider(type = UserProvider.class, method = "selectTotalByCondition")    Long selectTotalByCondition(User user);    /**     * 修改用户,参数不为空的数据才会修改     *     * @param user     * @return     */    @UpdateProvider(type = UserProvider.class, method = "updateUserByNotNull")    int updateUserByNotNull(User user);    /**     * 批量删除用户     * 在程序运行过程中已经不存在ids参数,需要如果想要使用ids这个参数中包含的数据时,需要使用@Param注解来标记参数     * 因为在JDK1.7之前,不能通过反射获取方法参数名     * @param ids     * @return     */    @DeleteProvider(type = UserProvider.class, method = "deleteByIds")    int deleteByIds(@Param("ids") Integer[] ids);    /**     * 批量插入     * 在程序运行过程中已经不存在users参数,需要如果想要使用users这个参数中包含的数据时,需要使用@Param注解来标记参数     * 因为在JDK1.7之前,不能通过反射获取方法参数名     * @param users     * @return     */    @InsertProvider(type = UserProvider.class, method = "testInsertByBatch")    int insertByBatch(@Param("users") List<User> users);}package org.cjw.mapper;import org.apache.ibatis.annotations.*;import org.cjw.pojo.User;import java.util.List;public interface UserMapper {    /**     * 根据条件查询结果     *     * @param user     * @return     */    @SelectProvider(type = UserProvider.class, method = "selectByCondition")    List<User> selectByCondition(User user);    /**     * 根据条件查询结果总数     *     * @param user     * @return     */    @SelectProvider(type = UserProvider.class, method = "selectTotalByCondition")    Long selectTotalByCondition(User user);    /**     * 修改用户,参数不为空的数据才会修改     *     * @param user     * @return     */    @UpdateProvider(type = UserProvider.class, method = "updateUserByNotNull")    int updateUserByNotNull(User user);    /**     * 批量删除用户     * 在程序运行过程中已经不存在ids参数,需要如果想要使用ids这个参数中包含的数据时,需要使用@Param注解来标记参数     * 因为在JDK1.7之前,不能通过反射获取方法参数名     * @param ids     * @return     */    @DeleteProvider(type = UserProvider.class, method = "deleteByIds")    int deleteByIds(@Param("ids") Integer[] ids);    /**     * 批量插入     * 在程序运行过程中已经不存在users参数,需要如果想要使用users这个参数中包含的数据时,需要使用@Param注解来标记参数     * 因为在JDK1.7之前,不能通过反射获取方法参数名     * @param users     * @return     */    @InsertProvider(type = UserProvider.class, method = "testInsertByBatch")    int insertByBatch(@Param("users") List<User> users);}

1.4.3. 测试代码

同上

1.4.4. 测试结果

同上

1.4.5. SQL类拼接动态sql语句

SQL类提供的API都是用于拼接SQL语句的,每一个方法对应一个子句,换句话说SQL类拼接SQL语句的单位是子句。

查询涉及的子句:select、from、join、left join、right join、where、group by、having、order by、or、and。

更新涉及的子句:update、set、where。

插入涉及的子句:insert、values。

删除涉及的子句:delete、where。

在SQL类中对每一种子句都提供了一个对应的API。(详见Mybatis文档)在实际开发中,
95%的数据库操作都是查询操作,所以在sql语句的练习中,应该以查询sql语句为主。

1.4.6. 静态SQL语句和动态SQL语句的区别

静态SQL语句和动态SQL语句都可以通过XML配置文件和注解的形式实现。使用
xml配置文件时,需要在mybatis-config.xml文件的mappers标签中添加一个mapper标签,resource属性为配置文件路径。使用注解时,需要在mybatis-config.xml文件的mappers标签中添加一个mapper标签,class属性值为XXXMapper映射类的全限定名。如下:

<mappers>    <!-- xml配置文件 -->    <mapper resource="org/cjw/mapper/UserMapper.xml"/>    <!-- 配置类 -->    <mapper class="org.cjw.mapper.UserProvider"/></mappers>

一个接口对应一个mapper标签,在开发中可能存在几十个上百个接口,现在通过mapper标签来注册接口是不现实的,所以可以使用package标签一次性注册一个包中的所有接口。如下:

<mappers>    <!-- 配置包扫描,自动找寻xml配置文件或者注解类 -->    <package name="org.cjw.mapper" /></mappers>

静态SQL语句编码简单,但是灵活性不足。

动态SQL语句编码稍微复杂,但是灵活性强。

xml文件通过where、set、if、trim、sql、include标签来拼接动态SQL语句。

注解通过SQL类或原生Java代码来拼接动态SQL语句。

无论xml文件还是注解拼接动态SQL语句时,都需要使用OGNL语法来拼接,即xxx = #{XXX}
的形式或者#{XXX}形式,切勿直接把参数值直接拼接到动态SQL上,否则会出现运行问题。

动态SQL语法仅仅只是拼接了SQL语句,在拼接完SQL语句后可以将其看做静态SQL语句,所以静态SQL语句的规范,动态SQL语句也需要遵循,即OGNL语法。

xml文件形式的动态SQL语句的拼接在XML文件中完成。

注解形式的动态SQL语句的拼接在一个java类的方法中完成。

2. 缓存

在Mybatis里面,所谓的缓存就是将已经查询过的记录放在内存的缓冲区或文件上,这样如果再次查询,可以通过配置的策略,命中已经查询过的记录,从而提高查询的效率。因为将数据库表中的记录读取到内存中需要经过IO,而IO操作的效率是很低,在开发中应该尽量避免IO操作,所以衍生出缓存机制。

缓存作用:提高查询的效率。

2.1. 一级缓存

Mybatis的缓存分为一级缓存、二级缓存。

一级缓存:所谓的一级缓存就是会话(SqlSesion对象)级别的缓存,同一个会话中,如果已经查询过的记录会保存一份在内存中,如果会话没有关闭,再次调用同样的查询方法,不会再查询数据库,而是直接从缓存中取出之前查询的记录(类似get请求的缓存机制)。一级缓存默认是打开的,而且是关闭不了的。

2.1.1. 测试代码

@Testpublic void testSelectByCondition() {    SqlSession session = MyBatisUtil.openSesion();    UserMapper userMapper = session.getMapper(UserMapper.class);    User user = new User();    user.setName("张");    // 第一次查询数据库    List<User> users = userMapper.selectByCondition(user);    for (User temp : users) {        System.out.println(temp.getName());    }    // 第二次查询数据库    List<User> users2 = userMapper.selectByCondition(user);    for (User temp : users2) {        System.out.println(temp.getName());    }    session.close();}

通过日志信息来判断是否有缓存。


由图可以得知,执行了两次selectAll方法,但是只查询了一次数据库,验证了一级缓存。

如何清空一级缓存:

1.关闭会话close()。

2.进行了DML操作,提交了事务commit()。

3.手动清除缓存clearCache()。

@Testpublic void testSelectByCondition() {    SqlSession session = MyBatisUtil.openSesion();    UserMapper userMapper = session.getMapper(UserMapper.class);    User user = new User();    user.setName("张");    // 第一次查询数据库    List<User> users = userMapper.selectByCondition(user);    for (User temp : users) {        System.out.println(temp.getName());    }    session.close();    session = MyBatisUtil.openSesion();    userMapper = session.getMapper(UserMapper.class);    // 第二次查询数据库    List<User> users2 = userMapper.selectByCondition(user);    for (User temp : users2) {        System.out.println(temp.getName());    }    session.close();}


@Testpublic void testSelectByCondition() {    SqlSession session = MyBatisUtil.openSesion();    UserMapper userMapper = session.getMapper(UserMapper.class);    User user = new User();    user.setName("张");    // 第一次查询数据库    List<User> users = userMapper.selectByCondition(user);    for (User temp : users) {        System.out.println(temp.getName());    }    Integer[] ids = {4};    int rows = userMapper.deleteByIds(ids);    System.out.println("影响的行数: " + rows);    // 第二次查询数据库    List<User> users2 = userMapper.selectByCondition(user);    for (User temp : users2) {        System.out.println(temp.getName());    }    session.close();}


@Testpublic void testSelectByCondition() {    SqlSession session = MyBatisUtil.openSesion();    UserMapper userMapper = session.getMapper(UserMapper.class);    User user = new User();    user.setName("张");    // 第一次查询数据库    List<User> users = userMapper.selectByCondition(user);    for (User temp : users) {        System.out.println(temp.getName());    }// 清除一级缓存    session.clearCache();    // 第二次查询数据库    List<User> users2 = userMapper.selectByCondition(user);    for (User temp : users2) {        System.out.println(temp.getName());    }    session.close();}


2.2. 二级缓存

一级缓存是SqlSession对象级别,在每一次会话中有效。

二级缓存是SqlSessionFactory级别,在整个应用都有效,可以在多个会话有效。

MyBatis本身并没有实现二级缓存二级缓存需要第三方缓存提供商的支持:Ehcache -第三方缓存(Hibernate框架默认就是支持)

学习地址:
www.mybatis.org/ehcache-cac…

2.2.1. 下载ehcache

github.com/mybatis/ehc…

2.2.2. 配置开启二级缓存

MyBatis开启二级缓存,新版本已经默认支持开启二级缓存。可以不改。

添加<setting name="logImpl" value="STDOUT_LOGGING" />开启日志的配置。

<settings>    <!-- 开启二级缓存 -->    <setting name="cacheEnabled" value="true"/>    <!-- 开启日志 -->    <setting name="logImpl" value="STDOUT_LOGGING"/></settings>

2.2.3. 导入Ehcache.jar包


2.2.4. Ehcache依赖 slfj 日志框架,必须要导入slfj的两个jar包


2.2.5. 基于XML配置文件的二级缓存

基于XMl配置文件的时,需要使用XML配置。

【1】创建ehcache.xml配置文件Ehcache有自己的配置文件,在src下面创建ehcache.xml配置文件,如下。

<ehcache>    <!-- 缓存的磁盘位置 -->    <diskStore path="D:/mybatis_cache"/>    <!-- 默认的缓存策略: 如果开发者在某一个需要缓存的文件配置了自定义缓存,就不使用默认的,如果没有配置,就使用默认缓存策略-->    <defaultCache            maxElementsInMemory="10000"            eternal="false"            timeToIdleSeconds="120"            timeToLiveSeconds="120"            overflowToDisk="true"    /></ehcache>

【2】在映射文件中添加<cache>标签以及配置对应的缓存策略。

<mapper namespace="cn.zj.mybatis.dao.UserMapper">	<!-- 当前表的映射开启支持二级缓存,并设置相关的缓存提供商,以及缓存的相关配置 -->	<cache type="org.mybatis.caches.ehcache.EhcacheCache" >       <!--最大的空闲时间  -->       <property name="timeToIdleSeconds" value="10000"/>       <!-- 最大的在线时间 -->       <property name="timeToLiveSeconds" value="20000"/>       <!-- 内存的大小 b字节 m1 =1024k 1k=1024b -->       <property name="maxEntriesLocalHeap" value="2000000"/>       <!-- 文件的大小 b字节-->       <property name="maxEntriesLocalDisk" value="20000000"/>       <!-- 算法 LRU:最少使用优先, "LFU" or "FIFO:先进先出 -->       <property name="memoryStoreEvictionPolicy" value="LRU"/>    </cache> 	<select id="selectAll" resultType="User">		select * from user	</select></mapper>

PS:因为二级缓存可以缓存到文件(将对象序列化到本地),涉及到对象序列化,那么对应的javaBean对象就必须实现Serializable接口。

public class User implements Serializable {private static final long serialVersionUID = 1L;    // get、set方法}

2.2.6. 缓存的命中率

命中率=从缓存中获取数据的次数/查询的总次数

如:两次查询 从缓存中获取一次

0.5 = 1/2;

0.666666 = 2/3;

命中率越高缓存效果越好

因为ehcache是sqlSessionFactory级别的缓存,是针对不同的会话而言的缓存,所以如果想要测试ehcache的命中率,需要不断的关闭和开启会话进而模拟多个用户访问数据库。如果在一个会话中执行相同的SQL语句,那么使用的mybatis自带的一级缓存,仅仅只对当前会话有效,所以会出现二级缓存的命中率一直为0.0,因为使用的一直是一级缓存。

如下:

@Testpublic void testSelectByCondition() {    for (int i = 0; i < 10; i++) {        SqlSession session = MyBatisUtil.openSesion();        UserMapper userMapper = session.getMapper(UserMapper.class);        User user = new User();        user.setName("张");        userMapper.selectByCondition(user);        session.close();    }}

Cache Hit Ratio [org.cjw.mapper.UserMapper]: 0.0Opening JDBC ConnectionCreated connection 1472465.Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@1677d1]==>  Preparing: select * from tb_user WHERE name like concat('%', ?, '%') ==> Parameters: 张(String)<==    Columns: id, name, age, email, password<==        Row: 1, 张三, 13, zhangsan@126.com, zhangsan123123<==        Row: 2, 张四, 14, zhangsi@163.com, zhangsi123123<==        Row: 3, 张五, 15, zhangwu@qq.com, zhangwu123123<==      Total: 3DEBUG [main] - put added 0 on heapResetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@1677d1]Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@1677d1]Returned connection 1472465 to pool.Cache Hit Ratio [org.cjw.mapper.UserMapper]: 0.5Cache Hit Ratio [org.cjw.mapper.UserMapper]: 0.6666666666666666Cache Hit Ratio [org.cjw.mapper.UserMapper]: 0.75Cache Hit Ratio [org.cjw.mapper.UserMapper]: 0.8Cache Hit Ratio [org.cjw.mapper.UserMapper]: 0.8333333333333334Cache Hit Ratio [org.cjw.mapper.UserMapper]: 0.8571428571428571Cache Hit Ratio [org.cjw.mapper.UserMapper]: 0.875Cache Hit Ratio [org.cjw.mapper.UserMapper]: 0.8888888888888888Cache Hit Ratio [org.cjw.mapper.UserMapper]: 0.9

从另外一方面来看,用户的每一个查询数据库的操作,都会首先经过二级缓存,如果二级缓存中没有对应的查询结果,那么就查询数据库,并将查询的记录通过一级缓存存储起来,如果还设置了二级缓存,那么也会往二级缓存中存储一份。

2.2.7. 基于注解的二级缓存

基于注解配置时,需要使用配置类。在映射接口上打上@CacheNamespace(blocking = true)

package org.cjw.mapper;import org.apache.ibatis.annotations.*;import org.cjw.pojo.User;import java.util.List;@CacheNamespace(blocking = true)public interface UserMapper {    /**     * 根据条件查询结果     *     * @param user     * @return     */    @SelectProvider(type = UserProvider.class, method = "selectByCondition")    List<User> selectByCondition(User user);    /**     * 根据条件查询结果总数     *     * @param user     * @return     */    @SelectProvider(type = UserProvider.class, method = "selectTotalByCondition")    Long selectTotalByCondition(User user);    /**     * 修改用户,参数不为空的数据才会修改     *     * @param user     * @return     */    @UpdateProvider(type = UserProvider.class, method = "updateUserByNotNull")    int updateUserByNotNull(User user);    /**     * 批量删除用户     * 在程序运行过程中已经不存在ids参数,需要如果想要使用ids这个参数中包含的数据时,需要使用@Param注解来标记参数     * 因为在JDK1.7之前,不能通过反射获取方法参数名     * @param ids     * @return     */    @DeleteProvider(type = UserProvider.class, method = "deleteByIds")    int deleteByIds(@Param("ids") Integer[] ids);    /**     * 批量插入     * 在程序运行过程中已经不存在users参数,需要如果想要使用users这个参数中包含的数据时,需要使用@Param注解来标记参数     * 因为在JDK1.7之前,不能通过反射获取方法参数名     * @param users     * @return     */    @InsertProvider(type = UserProvider.class, method = "testInsertByBatch")    int insertByBatch(@Param("users") List<User> users);}

2.2.8. 缓存命中率

Cache Hit Ratio [org.cjw.mapper.UserMapper]: 0.0Opening JDBC ConnectionCreated connection 84739718.Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@50d0686]==>  Preparing: select * from tb_user where 1 = 1 and name like concat('%', ?,'%') ==> Parameters: 张(String)<==    Columns: id, name, age, email, password<==        Row: 1, 张三, 13, zhangsan@126.com, zhangsan123123<==        Row: 2, 张四, 14, zhangsi@163.com, zhangsi123123<==        Row: 3, 张五, 15, zhangwu@qq.com, zhangwu123123<==      Total: 3Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@50d0686]Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@50d0686]Returned connection 84739718 to pool.Cache Hit Ratio [org.cjw.mapper.UserMapper]: 0.5Cache Hit Ratio [org.cjw.mapper.UserMapper]: 0.6666666666666666Cache Hit Ratio [org.cjw.mapper.UserMapper]: 0.75Cache Hit Ratio [org.cjw.mapper.UserMapper]: 0.8Cache Hit Ratio [org.cjw.mapper.UserMapper]: 0.8333333333333334Cache Hit Ratio [org.cjw.mapper.UserMapper]: 0.8571428571428571Cache Hit Ratio [org.cjw.mapper.UserMapper]: 0.875Cache Hit Ratio [org.cjw.mapper.UserMapper]: 0.8888888888888888Cache Hit Ratio [org.cjw.mapper.UserMapper]: 0.9

3. MyBatis的对象关系映射(难点重点)

在实际开发中,一个业务可能可能需要查询多个数据表,而多表查询就涉及连接查询(
等值连接)。

等值连接:表与表之间有一个外键关联。

我们都知道一个表对应一个POJO对象,但是对象与对象之间是没有外键关系的,对象和对象之间只有依赖关系。

对象之间关系主要是四种:

一对一关系 one2one 一个人对应一个身份证号,一个QQ号对应一个QQ空间。

一对多关系 one2many 一个部门包含多个员工

多对一关系 many2one 多个员工属于一个部门

多对多关系 many2many 多个老师对应多个学生,多个学生选择多个课程

什么关系应该从哪个对象作为中心点来看一对多,以one方作为中心点

MyBatis框架支持多表查询封装对象之间关系,主要使用collection和associatiion标签。

<collection>一对多查询

<association>多对一和一对一查询

3.1. 准备多表,表之间有外键关系(员工表和部门表)

员工表CREATE TABLE `employee` (  `id` int(11) NOT NULL AUTO_INCREMENT,  `name` varchar(50) DEFAULT NULL,  `dept_id` int(11) DEFAULT NULL,  PRIMARY KEY (`id`)) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8;部门表CREATE TABLE `department` (  `id` int(11) NOT NULL AUTO_INCREMENT,  `name` varchar(255) DEFAULT NULL,  PRIMARY KEY (`id`)) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;

3.2. 基于XML配置的关系映射

3.2.1. 多对一查询

一对一查询是多对一查询的特例,使用同一套代码。

3.2.1.1. N+1方式

N+1:N 就是当前需要查询结果对应发送的SQL语句的条数

          +1 关联查询数据需要额外多发一条SQL语句才能查询出对应的结果

3.2.1.1.1. POJO

package org.cjw.pojo;public class Employee {    private Long id;    private String name;    // 以员工为中心:多个员工对应一个部门,多对一关系,many2noe    private Department dept;    public Long getId() {        return id;    }    public void setId(Long id) {        this.id = id;    }    public String getName() {        return name;    }    public void setName(String name) {        this.name = name;    }    public Department getDept() {        return dept;    }    public void setDept(Department dept) {        this.dept = dept;    }}
package org.cjw.pojo;public class Department {    private Long id;    private String name;    public Long getId() {        return id;    }    public void setId(Long id) {        this.id = id;    }    public String getName() {        return name;    }    public void setName(String name) {        this.name = name;    }}
3.2.1.1.2. 映射接口

package org.cjw.mapper;import org.cjw.pojo.Department;public interface EmployeeMapper {    Department selectEmployeeByPrimaryKey(Long id);}

3.2.1.1.3. 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="org.cjw.mapper.EmployeeMapper">    <!-- 查询主表一条sql -->    <select id="selectEmployeeByPrimaryKey" resultMap="emp_map">        select * from employee where id = #{emp_id}    </select>        <resultMap id="emp_map" type="Employee">        <id property="id" column="id"/>        <result property="name" column="name" />        <!--            association标签的作用:将主表某一列的值作为查询条件查询副表,并将查询结果封装成对象返回                properties属性:最后映射的对象名                column属性:副表查询条件         -->        <association property="dept" column="dept_id" select="org.cjw.mapper.EmployeeMapper.selectDeptById" />    </resultMap>    <!-- 查询副表N条sql -->    <select id="selectDeptById" resultType="Department">        select * from department where id = #{dept_id}    </select></mapper>

3.2.1.1.4. 测试代码

@Testpublic void testSelectEmployeeByPrimaryKey() {    SqlSession session = MyBatisUtil.openSession();    EmployeeMapper employeeMapper = session.getMapper(EmployeeMapper.class);    Employee employee = employeeMapper.selectEmployeeByPrimaryKey(1L);    System.out.println("姓名:" + employee.getName() + ", 部门:" + employee.getDept().getName());}

3.2.1.1.5. 测试结果

姓名:张三, 部门:销售部


3.2.1.2. 等值连接查询

3.2.1.2.1. 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="org.cjw.mapper.EmployeeMapper">    <!-- 查询主表一条sql -->    <select id="selectEmployeeByPrimaryKey" resultMap="emp_map">        select e.id e_id, e.name e_name, d.id d_id, d.name d_name from employee e left join department d on e.dept_id = d.id where e.id = #{emp_id}    </select>    <resultMap id="emp_map" type="Employee">        <id property="id" column="e_id"/>        <result property="name" column="e_name" />        <collection property="dept" ofType="Department">            <id property="id" column="d_id" />            <result property="name" column="d_name" />        </collection>    </resultMap></mapper>


3.2.1.2.2. 测试结果

姓名:张三, 部门:销售部

当<collection>标签需要映射的记录只有一条时,默认映射为泛型类型的对象,而不是一个集合。

3.2.2. 一对多查询

一对一查询是一对多查询的特例,使用同一套代码。

3.2.2.1. N+1方式

3.2.2.1.1. POJO

package org.cjw.pojo;public class Employee {    private Long id;    private String name;    private Long deptId;    public Long getId() {        return id;    }    public void setId(Long id) {        this.id = id;    }    public String getName() {        return name;    }    public void setName(String name) {        this.name = name;    }    public Long getDeptId() {        return deptId;    }    public void setDeptId(Long deptId) {        this.deptId = deptId;    }}
package org.cjw.pojo;import java.util.List;public class Department {    private Long id;    private String name;    // 以部门为中心:一个部门对应多个与员工,一对多关系    private List<Employee> emps;    public Long getId() {        return id;    }    public void setId(Long id) {        this.id = id;    }    public String getName() {        return name;    }    public void setName(String name) {        this.name = name;    }    public List<Employee> getEmps() {        return emps;    }    public void setEmps(List<Employee> emps) {        this.emps = emps;    }}

3.2.2.1.2. 映射接口

package org.cjw.mapper;import org.cjw.pojo.Department;public interface DepartmentMapper {    Department selectDeptById(Long id);}

3.2.2.1.3. 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="org.cjw.mapper.DepartmentMapper">    <!-- 查询主表一条sql -->    <select id="selectDeptById" resultMap="dept_map">        select * from department where id = #{dept_id}    </select>    <resultMap id="dept_map" type="Department">        <id property="id" column="id"/>        <result property="name" column="name" />        <!--            association标签:用于将主表查询结果的某列数据值作为查询副表的查询条件                properties属性:需要映射的对象/集合                column属性:查询条件值                select属性:执行的查询语句         -->        <association property="emps" column="id" select="org.cjw.mapper.DepartmentMapper.selectEmpsByDeptId"  />    </resultMap>    <select id="selectEmpsByDeptId" resultType="Employee">        select * from employee where dept_id = #{id}    </select></mapper>


3.2.2.1.4. 测试代码

@Testpublic void testSelectDeptById() {    SqlSession session = MyBatisUtil.openSession();    DepartmentMapper departmentMapper = session.getMapper(DepartmentMapper.class);    Department dept = departmentMapper.selectDeptById(1L);    for (Employee emp : dept.getEmps()) {        System.out.println(emp.getName());    }
}

3.2.2.1.5. 测试结果

张三李四

3.2.2.2. 等值连接查询

3.2.2.2.1. 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="org.cjw.mapper.DepartmentMapper">    <!-- 查询主表一条sql -->    <select id="selectDeptById" resultMap="dept_map">        select d.id d_id, d.name d_name, e.id e_id, e.name e_name from department d left join employee e on d.id = e.dept_id where d.id = #{dept_id}    </select>    <resultMap id="dept_map" type="Department">        <id property="id" column="d_id"/>        <result property="name" column="d_name" />        <collection property="emps" column="id" ofType="Employee">            <id property="id" column="e_id"/>            <result property="name" column="e_name" />            <result property="deptId" column="d_id" />        </collection>    </resultMap></mapper>


3.2.2.2.2. 测试结果

张三李四

3.3. 基于注解配置的关系映射

3.3.1. 多对一查询

3.3.1.1. N+1方式

3.3.1.1.1. POJO

package org.cjw.pojo;public class Employee {    private Long id;    private String name;    private Department dept;    public Long getId() {        return id;    }    public void setId(Long id) {        this.id = id;    }    public String getName() {        return name;    }    public void setName(String name) {        this.name = name;    }    public Department getDept() {        return dept;    }    public void setDept(Department dept) {        this.dept = dept;    }}
package org.cjw.pojo;public class Department {    private Long id;    private String name;    public Long getId() {        return id;    }    public void setId(Long id) {        this.id = id;    }    public String getName() {        return name;    }    public void setName(String name) {        this.name = name;    }}

3.3.1.1.2. 映射接口+注解配置

package org.cjw.mapper;import org.apache.ibatis.annotations.*;import org.cjw.pojo.Department;import org.cjw.pojo.Employee;public interface EmployeeMapper {    @Select("select * from employee where id = #{id}")    @Results({            @Result(id = true, property = "id", column = "id"),            @Result(property = "name", column = "name"),            @Result(property = "dept", column = "dept_id", many = @Many(select = "org.cjw.mapper.EmployeeMapper.selectDeptById"))    })    Employee selectEmployeeByPrimaryKey(Long id);    @Select("select * from department where id = #{dept_id}")    Department selectDeptById(Long id);}

3.3.1.1.3. 测试代码

@Testpublic void testSelectDeptById() {    SqlSession session = MyBatisUtil.openSession();    EmployeeMapper employeeMapper = session.getMapper(EmployeeMapper.class);    Employee employee = employeeMapper.selectEmployeeByPrimaryKey(1L);    System.out.println("姓名:" + employee.getName() + ",部门:" + employee.getDept().getName());}

3.3.1.1.4. 测试结果

姓名:张三,部门:销售部

3.3.1.2. 等值连接查询

连接查询暂时未发现解决方案,有知道的朋友可以评论留言告诉我。

3.3.2. 一对多查询

3.3.2.0.1. POJO

package org.cjw.pojo;public class Employee {    private Long id;    private String name;    private Long dept_id;    public Long getId() {        return id;    }    public void setId(Long id) {        this.id = id;    }    public String getName() {        return name;    }    public void setName(String name) {        this.name = name;    }    public Long getDept_id() {        return dept_id;    }    public void setDept_id(Long dept_id) {        this.dept_id = dept_id;    }}
package org.cjw.pojo;import java.util.List;public class Department {    private Long id;    private String name;    private List<Employee> emps;    public Long getId() {        return id;    }    public void setId(Long id) {        this.id = id;    }    public String getName() {        return name;    }    public void setName(String name) {        this.name = name;    }    public List<Employee> getEmps() {        return emps;    }    public void setEmps(List<Employee> emps) {        this.emps = emps;    }}

3.3.2.0.2. 映射接口+注解配置

package org.cjw.mapper;import org.apache.ibatis.annotations.Many;import org.apache.ibatis.annotations.Result;import org.apache.ibatis.annotations.Results;import org.apache.ibatis.annotations.Select;import org.cjw.pojo.Department;import org.cjw.pojo.Employee;import java.util.List;public interface DepartmentMapper {    @Select("select * from department where id = #{dept_id}")    @Results({            @Result(id = true, property = "id", column = "id"),            @Result(property = "name", column = "name"),            @Result(property = "emps", column = "id", many = @Many(select = "org.cjw.mapper.DepartmentMapper.selectEmpsByDeptId"))    })    Department selectDeptById(Long id);    @Select("select * from employee where dept_id = #{dept_id}")    List<Employee> selectEmpsByDeptId(Long id);}

3.3.2.0.3. 测试代码

@Testpublic void testSelectDeptById() {    SqlSession session = MyBatisUtil.openSession();    DepartmentMapper departmentMapper = session.getMapper(DepartmentMapper.class);    Department dept = departmentMapper.selectDeptById(1L);    for (Employee emp : dept.getEmps()) {        System.out.println("部门:" + dept.getName() + ",雇员名:" + emp.getName());    }}

3.3.2.0.4. 测试结果

部门:销售部,雇员名:张三部门:销售部,雇员名:李四

3.3.2.1. 等值连接查询

同上,有知道的小伙伴可以留言给我。

3.4. 对象管理映射个人理解

在开发中,一般pojo中的常见属性除了基本类型、包装类型、String之外,还可能出现对象属性、集合属性,对于基本、包装、String类型,可以直接进行映射,但是对象属性、集合属性并不能,因为内部还需要再指定字段和属性的映射关系。换言之,对象属性、集合属性本身就需要指定映射关系,而其又属于一个pojo对象,这个pojo对象本身也需要指定映射关系,因此对于存在pojo对象属性和集合类型属性的pojo对象的映射,咱们需要使用N+1策略或者等值连接策略。

N+1策略则是先把所有pojo对象查询出来,然后再根据外键字段去查询对象属性或者集合属性,使用association标签(property、column、select标签属性)。

等值连接策略则是通过连表查询,将pojo对象的属性相关信息都查询出来,然后在内存中根指定的映射关系将字段值赋值到属性上,对于pojo对象属性和集合类型属性,使用collection标签(property、ofType属性)来指定字段值和其属性的映射关系。

N+1策略操作数据库次数为记录数+1次,等值连接策略操作数据库次数为1次。

4. MyBatis的逆向工程

MyBatis的逆向工程能自动帮开发者生成数据库表对应的pojo实体文件,自动生成映射文件自动生成表的各种(CRUD)的sql语句,但是只能做单表操作,联合查询还得开发者自己改造。使用逆向工程得先在Eclipse安装逆向工程的插件。




4.1. 插件安装步骤

判断是否安装成功


4.2. eclipse逆向工程步骤

4.2.1. 新建一个普通java项目,导入mybatis.jar包和数据库驱动包


4.2.2. 配置生成文件


配置生成文件

<?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="context1">   <!-- 注释构建 -->    <commentGenerator>       <!-- 去掉所有的注释 -->    	<property name="suppressAllComments" value="true"/>    	<property name="suppressDate" value="true"/>    </commentGenerator>        <!-- 数据库四要素 -->    <jdbcConnection connectionURL="jdbc:mysql://localhost:3306/mybatis"     driverClass="com.mysql.jdbc.Driver"     password="root"     userId="root" />    <!-- 实体类 : pojo    	targetPackage : 实体类生成后存放的包    	targetProject : 存放的目录一般都放在 src下面      -->    <javaModelGenerator targetPackage="cn.zj.mybatis.pojo" targetProject="mybatis-generator/src" />    <!-- 映射文件 -->    <sqlMapGenerator targetPackage="cn.zj.mybatis.mapper" targetProject="mybatis-generator/src" />    <!-- 操作接口     	type 生成映射的形式    		ANNOTATEDMAPPER : 纯注解的,没有xml映射    		XMLMAPPER : 生成的有xml映射文件    -->    <javaClientGenerator  targetPackage="cn.zj.mybatis.mapper" targetProject="mybatis-generator/src" type="XMLMAPPER" />        <!-- 要生成对应表的配置    	tableName : 数据库表名    	//如果下面全部是true,mybatis直接可以使用纯面向对象开发    	enableCountByExample : 是否生成查询总数的 Example     	enableDeleteByExample : 是否生成删除的 Example     	enableSelectByExample : 是否生成查询集合的 Example     	enableUpdateByExample : 是否生成修改的 Example      -->    <table  tableName="user"  enableCountByExample="false" enableDeleteByExample="false" enableSelectByExample="true" enableUpdateByExample="false"></table>    <table  tableName="employee" enableCountByExample="false" enableDeleteByExample="false" enableSelectByExample="true" enableUpdateByExample="false"></table>    <table  tableName="department" enableCountByExample="false" enableDeleteByExample="false" enableSelectByExample="true" enableUpdateByExample="false"></table>  </context></generatorConfiguration>

4.2.3. 开始逆向工程

选中generatorConfig.xml逆向工程配置文件,点击鼠标右键。

 

4.3. idea的逆向工程步骤(基于maven)

4.3.1. 新建一个maven项目,依赖引入mybatis、mysql-connector-java、以及mybatis-generator-maven-plugin插件

<dependencies>        <dependency>            <groupId>org.mybatis</groupId>            <artifactId>mybatis</artifactId>            <version>3.4.6</version>        </dependency>        <dependency>            <groupId>mysql</groupId>            <artifactId>mysql-connector-java</artifactId>            <version>5.1.38</version>        </dependency>    </dependencies>     <build>        <plugins>        <!-- 逆向工程所需的插件 -->            <plugin>                <groupId>org.mybatis.generator</groupId>                <artifactId>mybatis-generator-maven-plugin</artifactId>                <version>1.3.2</version>                <configuration>                    <verbose>true</verbose>                    <overwrite>true</overwrite>                    <configurationFile>src/main/resources/generatorConfig.xml</configurationFile>                </configuration>            </plugin>        </plugins>    </build>

4.3.2. 编写generatorConfig.xml配置文件

<?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>    <!-- 配置驱动包的位置 -->    <classPathEntry            location="C:\Users\JackMi\.m2\repository\mysql\mysql-connector-java\5.1.38\mysql-connector-java-5.1.38.jar" />    <context id="context1">        <!-- 注释构建 -->        <commentGenerator>            <!-- 去掉所有的注释 -->            <property name="suppressAllComments" value="true"/>            <property name="suppressDate" value="true"/>        </commentGenerator>         <!-- 数据库四要素 -->        <jdbcConnection driverClass="com.mysql.jdbc.Driver"                        connectionURL="jdbc:mysql://localhost:3306/mybatis"                        userId="root"                        password="root"/>         <!-- 实体类 : pojo            targetPackage : 实体类生成后存放的包            targetProject : 存放的目录一般都放在 src下面          -->        <javaModelGenerator targetPackage="com.jackmi.pojo" targetProject="src/main/java"/>        <!-- 映射文件 -->        <sqlMapGenerator targetPackage="com.jackmi.mapper" targetProject="src/main/java"/>        <!-- 操作接口            type 生成映射的形式                ANNOTATEDMAPPER : 纯注解的,没有xml映射                XMLMAPPER : 生成的有xml映射文件        -->        <javaClientGenerator targetPackage="com.jackmi.mapper" targetProject="src/main/java"                             type="XMLMAPPER"/>         <!-- 要生成对应表的配置            tableName : 数据库表名            domainObjectName:生成的pojo对象的名字            // 如果下面全部是true,mybatis直接可以使用纯面向对象开发            enableCountByExample : 是否生成查询总数的 Example            enableDeleteByExample : 是否生成删除的 Example            enableSelectByExample : 是否生成查询集合的 Example            enableUpdateByExample : 是否生成修改的 Example         -->        <table tableName="user" domainObjectName="User" enableCountByExample="false" enableDeleteByExample="false" enableSelectByExample="true" enableUpdateByExample="false"></table>        <table tableName="employee" domainObjectName="Employee" enableCountByExample="false" enableDeleteByExample="false" enableSelectByExample="true" enableUpdateByExample="false"></table>        <table tableName="department" domainObjectName="Department" enableCountByExample="false" enableDeleteByExample="false" enableSelectByExample="true" enableUpdateByExample="false"></table>    </context></generatorConfiguration>

4.3.3. 双击mabatis-generator:generate插件



4.4. 逆向功能的缺点

逆向功能不能逆向多表,只能逆向单表操作,多表之间有外键对应java关联关系没办法映射,需要开发者手动编写对应代码。