万字长文!一篇搞定MyBatis!

281 阅读14分钟

ps:好吧,起这个名字就是引起你的注意,哈哈。

写这篇博文,主要是想温故知新,在回顾的同时,希望可以看到一些新的点;希望可以帮到你,如果有新的发现的话,还请点个赞。先上MyBatis官网地址:www.mybatis.org/mybatis-3/

一句话介绍MyBatis

MyBatis是一款优秀的基于ORM的半自动轻量级持久层框架。抓取关键字:基于ORM、半自动、轻量级、持久化,接下来一一解释。

基于ORM

何为ORM? ORM即Object Relational Mapping,对象关系映射,ORM完成了对象到数据库的映射。可以理解为程序对象和数据库中表之间的桥梁,ORM框架将面向对象的操作转化为了对数据库中表的操作,大大提高了开发效率。

半自动

有半自动化,就有自动化。全自动化框架比较典型的就是Hibernate,它无需手写SQL,直接操作对象即可,好处是大大降低了对象与数据库的耦合性,可移植性好,但是面对复杂业务,实现及优化困难;Mybatis需要手写SQL,更加灵活,可以使后端代码与SQL功能分离,边界清晰,但是可移植性差,做过数据库迁移的都懂哈。

轻量级

重量级的框架,在启动的时候需要消耗大量的资源,内存,CPU等,比如EJB,本身也有120M;而轻量级框架,比如我们讲的Mybatis,在启动时消耗的资源就少很多,自身也只有不到3M。

持久化

持久化,顾名思义,就是对持久层进行操作,将对象永久的保存在数据库中。

快速入门

上文了解了MyBatis的基本信息,现在开始准备第一个简单的demo。

准备张表

CREATE TABLE `t_company` (

 `id` int(11) NOT NULL AUTO_INCREMENT COMMENT 'id',

 `name` varchar(50) DEFAULT NULL COMMENT '公司名',

 `region` varchar(50) DEFAULT NULL COMMENT '地域',

 `boss` varchar(50) DEFAULT NULL COMMENT '老板',

 `creator` varchar(50) DEFAULT NULL COMMENT '创建者',

 `create_tm` timestamp DEFAULT NULL COMMENT '创建时间',

 `updater` varchar(50) DEFAULT NULL COMMENT '修改者',

 `update_tm` timestamp DEFAULT NULL COMMENT '修改时间',

 PRIMARY KEY (`id`)

) ENGINE=InnoDB AUTO_INCREMENT=81 DEFAULT CHARSET=utf8 COMMENT='公司'

创建springboot项目

创建一个springboot项目,并引入以下依赖

<dependency>
  <!--这是由mybatis提供的第三方starter,spring并未提供与mybatis整合的starter。后续我们也可以写个自己的starter-->
<groupId>org.mybatis.spring.boot</groupId>
   <artifactId>mybatis-spring-boot-starter</artifactId>
   <version>3.0.0</version>
</dependency>
<dependency>
   <groupId>com.mysql</groupId>
   <artifactId>mysql-connector-j</artifactId>
   <scope>runtime</scope>
</dependency>
<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-test</artifactId>
   <scope>test</scope>
</dependency>
<dependency>
   <groupId>org.projectlombok</groupId>
   <artifactId>lombok</artifactId>
</dependency>
<dependency>
   <groupId>commons-beanutils</groupId>
   <artifactId>commons-beanutils</artifactId>
   <version>1.8.3</version>
</dependency>

添加连接数据库相关配置信息,填写自己所连数据库信息

#数据库连接配置信息
spring.datasource.url=jdbc:mysql://****
spring.datasource.username=****
spring.datasource.password=****
spring.datasource.driver-class-name=com.mysql.jdbc.Driver

创建公司表的实体类

@Data
public class CompanyVO {

    /**
     * id
     */
    private int id;

    /**
     * 名称
     */
    private String name;

    /**
     * 区域
     */
    private String region;

    /**
     * 老板
     */
    private String boss;

    /**
     * 创建人
     */
    private String creator;

    /**
     * 创建时间
     */
    private Date create_tm;

    /**
     * 更新人
     */
    private String updater;

    /**
     * 更新时间
     */
    private String update_tm;
}

创建mapper类

@Mapper
public interface CompanyMapper {
    /**
     * 根据ID查询
     * @param id
     * @return
     */
    CompanyVO selectById(Integer id);

    /**
     * 插入一条数据
     * @param companyVO
     */
    void insertOne(CompanyVO companyVO);

}

创建mapper.xml,mapper建在resource目录下,路径需要和mapper类一致。注意:要一层一层建

<?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="com.example.demo.company.mapper.CompanyMapper">
    <resultMap id="BaseResultMap" type="com.example.demo.company.vo.CompanyVO">
        <id column="id" jdbcType="INTEGER" property="id" />
        <result column="name" jdbcType="VARCHAR" property="name" />
        <result column="region" jdbcType="VARCHAR" property="region" />
        <result column="boss" jdbcType="VARCHAR" property="boss" />
        <result column="creator" jdbcType="VARCHAR" property="creator" />
        <result column="create_tm" jdbcType="TIMESTAMP" property="createTm" />
        <result column="updater" jdbcType="VARCHAR" property="updater" />
        <result column="update_tm" jdbcType="TIMESTAMP" property="updateTm" />
    </resultMap>

    <!--根据ID查询-->
    <select id="selectById" parameterType="java.lang.Integer" resultMap="BaseResultMap">
        SELECT * FROM t_company
        WHERE id=#{id}
    </select>

    <!--插入数据-->
    <insert id="insertOne" parameterType="com.example.demo.company.vo.CompanyVO">
        INSERT INTO t_company
        (name, region, boss, creator,
        create_tm, updater, update_tm
        )
        VALUES
            ( #{name}, #{region}, #{boss},
            #{creator}, #{createTm}, #{updater}, #{updateTm})
    </insert>
</mapper>

创建service类

@Service
public class CompanyService {

    /**
     * mapper
     */
    @Autowired
    private CompanyMapper companyMapper;

    /**
     * 根据ID查询
     * @param id
     * @return
     */
    public CompanyVO selectById(Integer id){
        return companyMapper.selectById(id);
    }

    /**
     * 插入一条
     * @param companyVO
     */
    public void insertOne(CompanyVO companyVO){
        companyMapper.insertOne(companyVO);
    }

}

创建测试类,进行测试

@RunWith(SpringRunner.class)
@SpringBootTest
public class Test {

    @Autowired
    private CompanyService service;

    /**
     * selectById
     */
    @org.junit.Test
    public void selectById(){
        CompanyVO companyVO = service.selectById(1);
        System.out.println(companyVO.toString());
    }

    /**
     * 插入一条数据
     */
    @org.junit.Test
    public void insertOne(){
        CompanyVO companyVO = new CompanyVO();
        companyVO.setBoss("东哥");
        companyVO.setName("京东");
        companyVO.setRegion("北京");
        companyVO.setRegion("北京");
        companyVO.setCreator("anyu~");
        companyVO.setCreateTm(DateUtil.now());
        companyVO.setUpdater("anyu~");
        companyVO.setUpdateTm(DateUtil.now());
        service.insertOne(companyVO);

    }
}

执行测试类,为了方便查看执行的SQL语句,可以在配置文件中,加入下面配置

#日志打印
mybatis.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl 

执行插入,查看日志

image.png

插入后,数据库查一下

image.png 执行查询,查看日志

image.png

自定义插件

上面的demo中,创建时间和修改时间、创建人和修改人,每次都需要set,一点儿也不优雅,对于这种和业务无关每个表还需要的字段,有没有方法自动加上呢?当然有,我们可以自定义一个插件,自动去做。

MyBatis插件机制介绍

一般情况下,开源框架都会提供插件机制,供我们灵活拓展。显而易见,这样不仅增加了框架的灵活性,而且还可以结合实际需求,拓展定制化的功能,更好的完成开发工作。MyBatis作为一个优秀的ORM开源框架,也提供了相关组件,供我们灵活扩展。四大核心对象分别是

Executor:SQL执行器,完成对数据库的增删改查操作;允许拦截的方法包括(update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)

StatementHandler:封装了对JDBC Statement对象的操作,执行SQL语句,实现与数据库的交互;允许拦截的方法包括(prepare, parameterize, batch, update, query)

ParameterHandler:处理传入的SQL的参数,允许拦截的方法包括(getParameterObject, setParameters)

ResultSetHandler:处理SQL执行后返回的结果集,允许拦截的方法包括(handleResultSets, handleOutputParameters)

好,介绍完了mybatis的四个对象,那我们想实现自动化的填入创建人、创建时间等信息应该通过拦截哪个对象呢?思考一下,其实只需要在插入或修改数据时添加字段即可,实际上就是在SQL执行insert、update语句去设置。所以我们对SQL执行器Executor进行拦截操作即可。好,那么接下来具体实现下。

开始自己的第一个MyBatis插件

首先需要实现Interceptor接口,Interceptor接口中有三个方法,分别是:intercept(Invocation invocation);plugin(Object o);setProperties(Properties properties)。intercept方法是最终拦截的方法,我们的逻辑代码就是加在里面;plugin方法返回一个代理对象,我们直接调用MyBatis提供的Plugin.wrap即可;setProperties就是配置一些自定义属性。

在创建的类上需要加@Intercepts,标记为拦截器类;@Signature是@Intercepts的一个属性,表示拦截哪些方法,可以拦截配置多个,拦截多个方法;@Signature中有多个属性,分别是type(拦截的组件名称),method(拦截的方法名),args(方法的参数)。

开始写我们的demo

@Intercepts(
        @Signature(
                type = Executor.class,
                method = "update",
                args = {MappedStatement.class,Object.class}
        )
)
public class ExecutorInterceptor implements Interceptor {
    /**
     * intercept
     * @param invocation
     * @return
     * @throws Throwable
     */
    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        //接受SQL操作类型
        SqlCommandType sqlCommandType = null;

        Object[] args = invocation.getArgs();
        for (Object arg : args) {
            //获取操作类型
            if (arg instanceof MappedStatement){
                MappedStatement mappedStatement = (MappedStatement) arg;
                sqlCommandType = mappedStatement.getSqlCommandType();
                continue;
            }
            //根据参数类型赋值
            if(arg instanceof CompanyVO){
                Date now = DateUtil.now();
                if(SqlCommandType.INSERT.equals(sqlCommandType)){
                    BeanUtils.setProperty(arg,"createTm",now);
                    BeanUtils.setProperty(arg,"updateTm",now);
                    BeanUtils.setProperty(arg,"creator","anyu~");
                    BeanUtils.setProperty(arg,"updater","anyu~");
                }
                if(SqlCommandType.UPDATE.equals(sqlCommandType)){
                    BeanUtils.setProperty(arg,"updateTm",now);
                    BeanUtils.setProperty(arg,"updater","anyu~~~");
                }
            }
        }
        return invocation.proceed();
    }

    /**
     * plugin
     * @param o
     * @return
     */
    @Override
    public Object plugin(Object o) {
        return Plugin.wrap(o,this);
    }

    /**
     * setProperties
     * @param properties
     */
    @Override
    public void setProperties(Properties properties) {
    }
}

增加一个配置类,用来注入拦截器类;也可以使用配置文件,在这里不做赘述。

@Configuration
public class MyBatisConfiguration {

    /**
     * ExecutorInterceptor
     * @return
     */
    @Bean
    public ExecutorInterceptor executorInterceptor(){
        ExecutorInterceptor interceptor = new ExecutorInterceptor();
        return interceptor;
    }
}

我们是对新增和修改方法增加了拦截,所以增加一个updateById方法。下面的代码用到了标签,这是MyBatis的另一个强大的功能,动态SQL,我们在下一节去讲讲这个。

<!--根据id更新数据-->
<update id="updateById" parameterType="com.example.demo.company.vo.CompanyVO">
    UPDATE t_company
    <set>
        <if test="name != null">
            name = #{name},
        </if>
        <if test="region!=null">
            region = #{region},
        </if>
        <if test="boss!=null">
            boss = #{boss},
        </if>
        <if test="createTm!=null">
            creator = #{creator},
        </if>
        <if test="createTm!=null">
            create_tm = #{createTm},
        </if>
        <if test="updater!=null">
            updater = #{updater},
        </if>
        <if test="updateTm!=null">
            update_tm = #{updateTm},
        </if>
    </set>
    where id = #{id}
</update>

开始测试,先测试新增方法,测试代码如下

@org.junit.Test
public void insertOne(){
    CompanyVO companyVO = new CompanyVO();
    companyVO.setBoss("小马哥");
    companyVO.setName("腾讯");
    companyVO.setRegion("深圳");
    service.insertOne(companyVO);

}

image.png

image.png

然后测试修改方法,测试代码如下:

@org.junit.Test
public void updateById(){
    CompanyVO companyVO = new CompanyVO();
    companyVO.setId(1);
    companyVO.setRegion("北京-亦庄");
    service.updateById(companyVO);
}

image.png

image.png

从日志及MySQL可以看出,自定义插件对创建人、创建时间等字段进行了赋值,并成功插入或更新在数据库表中,符合预期。这样,我们的自定义插件就开发完了,可以减少一部分重复代码,以此为例子,可以思考一下当前的什么业务场景可以用呢?这些要靠自己发掘了。MyBatis也有很多非常好用的插件,比如分页插件PageHelper;通用Mapper插件,极其方便的实现单表增删改查;MyBatis-X可以快速生成实体类、mapper等等。

动态SQL

不知道大家有没有使用JDBC进行过开发,刚入行接手的第一个项目就是用的JDBC,因为业务要求,查询条件很多,要根据不同条件拼接不同的SQL,经常性的上百行,而且很容易哪边没拼好,就出错了,回想起那段时间那是相当的痛苦。MyBatis的动态SQL,彻底解决了这个麻烦。

动态SQL,即Dynamic SQL,通过各种标签对查询条件做出判断,实现动态拼接SQL语句,帮我们来实现部分逻辑;比如我们上一部分的标签,通过判断传入的值是否为null,决定是否拼接相应的SQL语句。

接下来我们介绍下常用的标签及用法。

If标签

if标签类似于Java中的if判断语句,该标签通常与where或者update连用,比如我们之前写的updateById语句,或者比如下面的查询语句:

<!--根据条件查询-->
<select id="selectByCondition" parameterType="com.example.demo.company.vo.CompanyVO" resultMap="BaseResultMap">
    select * from t_company
    <where>
        <if test="name != null">
            AND name = #{name}
        </if>
        <if test="region!=null">
            AND region = #{region}
        </if>
        <if test="boss!=null">
            AND  boss = #{boss}
        </if>
    </where>
</select>

foreach标签

foreach标签是对集合进行遍历,和Java语言中的遍历相似,经常与in语句连用。常用场景通常和批量相关,比如批量插入、批量删除,或者拼接查询语句的条件也常常用到。下面分别是一个查询和批量插入的例子

<!--根据id集合查询-->
<select id="selectByIds" parameterType="java.util.List" resultMap="BaseResultMap">
    select * from t_company where id in
    <foreach collection="list" index="index" open="(" close=")" separator="," item="id">
        #{id}
    </foreach>
</select>

<!--插入全部,之前的拦截器并不兼容批量操作,这部分就作业自行实现吧-->
<insert id="insertAll" parameterType="java.util.List">
    INSERT INTO t_company
    (name, region, boss, creator,
    create_tm, updater, update_tm
    )
    VALUES
    <foreach collection="list" item="item" separator="," index="index">
        (#{item.name}, #{item.region}, #{item.boss},
        #{item.creator}, #{item.createTm}, #{item.updater}, #{item.updateTm})
    </foreach>
</insert>

choose, when, otherwise标签

这些标签类似于Java语句中Switch,case,default;根据条件去选择需要拼接的SQL语句。比如,下面的语句,根据传入参数的判断,name不为空时,拼接name条件,若为空时则继续向下判断,一次类推。

<!--根据条件优先级查询-->
<select id="selectByPri" parameterType="com.example.demo.company.vo.CompanyVO" resultMap="BaseResultMap">
    select * from t_company where 1=1
    <choose>
        <when test="name != null">
            AND name = #{name}
        </when>
        <when test="region != null">
            AND region = #{region}
        </when>
        <when test="boss != null">
            AND boss = #{boss}
        </when>
        <otherwise>
            AND id = #{id}
        </otherwise>
    </choose>
</select>

sql,include标签

SQL和include标签,通常搭配使用;对于一些重复的SQL语句,比如查询的字段,查询条件等等,这两个搭配使用,可以有效减少代码量。下面的是修改之前的查询语句,以此为例

<!--查询字段语句块-->
<sql id="field">
    id,name,region,boss,creator,create_tm,updater,update_tm
</sql>

<!--根据ID查询-->
<select id="selectById" parameterType="java.lang.Integer" resultMap="BaseResultMap">
    SELECT
    <include refid="field"/>
    FROM t_company
    WHERE id=#{id}
</select>

where,set标签

where标签会自动生成where关键字,并且会将多余的and或or去掉,避免多余的and或or引起的SQL语句错误;和where标签类似,set标签会自动生成set关键字,并去掉多余的逗号,避免因为多余的逗号引起的错误。where标签和set标签,在上文中均有例子,就不在重复写了。

trim标签

trim标签是一个格式化的标签,是针对trim标签内的语句的首尾进行删除或添加的,表述的有点绕口,所以我们还是上案例吧。

<!--根据条件查询-->
<select id="selectByCondition" parameterType="com.example.demo.company.vo.CompanyVO" resultMap="BaseResultMap">
    select * from t_company
    <trim prefix="where" prefixOverrides="AND">
        <if test="name != null">
            AND name = #{name}
        </if>
        <if test="region!=null">
            AND region = #{region}
        </if>
        <if test="boss!=null">
            AND  boss = #{boss}
        </if>
    </trim>
</select>

image.png

从上面xml中的语句和打印的最终执行的SQL语句可以看出来,trim标签把第一个if标签中的and替换成了where,类似于Java中的replace方法,这相当于用trim标签做了一个where标签的功能。trim相当于一个字符串处理工具,当然它不仅可以处理句首,还可以处理句尾,我们看下面的例子。

<!--根据id更新数据-->
<update id="updateById" parameterType="com.example.demo.company.vo.CompanyVO">
    UPDATE t_company
    <trim prefix="set" prefixOverrides="" suffix="" suffixOverrides=",">
        <if test="name != null">
            name = #{name},
        </if>
        <if test="region!=null">
            region = #{region},
        </if>
        <if test="boss!=null">
            boss = #{boss},
        </if>
        <if test="createTm!=null">
            creator = #{creator},
        </if>
        <if test="createTm!=null">
            create_tm = #{createTm},
        </if>
        <if test="updater!=null">
            updater = #{updater},
        </if>
        <if test="updateTm!=null">
            update_tm = #{updateTm},
        </if>
    </trim>
    where id = #{id}
</update>

image.png

上面的例子,实现了一个set标签的功能,即在句首加了一个set关键字,并把句尾的逗号去掉了。trim标签实际用到的业务场景还是很少的,因为其他的标签就足够强大足够用了;但是,当我们绞尽脑汁想完成一个功能,其他标签也不满足的时候,也不要忘了它,没准会有意想不到的惊喜。

复杂映射开发

回顾我们之前的内容,可以发现,上面的所有例子全部是针对单表的操作,如果是面对表关联查询的场景呢?MyBatis提供了面对一对一、一对多等复杂业务场景的处理方式,下面我们就分开来唠唠。

一对一

我们需要再建一个表,就建一个boss表吧,建表语句如下

CREATE TABLE `t_boss` (
    `id` int(11) NOT NULL AUTO_INCREMENT COMMENT 'id',
    `name` varchar(50) DEFAULT NULL COMMENT '姓名',
    `sex` varchar(50) DEFAULT NULL COMMENT '性别',
    `creator` varchar(50) DEFAULT NULL COMMENT '创建者',
    `create_tm` timestamp DEFAULT NULL COMMENT '创建时间',
    `updater` varchar(50) DEFAULT NULL COMMENT '修改者',
    `update_tm` timestamp DEFAULT NULL COMMENT '修改时间',
    PRIMARY KEY (`id`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='老板'

直接插入几条数据

INSERT INTO t_boss
    ( name, sex, creator, create_tm, updater, update_tm)
VALUES( '东哥', '男',  'anyu~', '2023-03-30 17:12:36', 'anyu~~~', '2023-03-21 17:06:36');
INSERT INTO t_boss
    ( name, sex, creator, create_tm, updater, update_tm)
VALUES( '小马哥', '男',  'anyu~', '2023-03-30 17:12:36', 'anyu~~~', '2023-03-21 17:06:36');
INSERT INTO t_boss
    ( name, sex, creator, create_tm, updater, update_tm)
VALUES( '雷布斯', '男',  'anyu~', '2023-03-30 17:12:36', 'anyu~~~', '2023-03-21 17:06:36');
INSERT INTO t_boss
    ( name, sex, creator, create_tm, updater, update_tm)
VALUES( '风清扬', '男',  'anyu~', '2023-03-30 17:12:36', 'anyu~~~', '2023-03-21 17:06:36');

新增实体类

@Data
public class BossVO {
    /**
     * id
     */
    private Integer id;

    /**
     * 名称
     */
    private String name;

    /**
     * 区域
     */
    private String sex;

    /**
     * 公司
     */
    private CompanyVO companyVO;
}

查询方法和resultMap

<resultMap id="BaseResultMap" type="com.example.demo.company.vo.BossVO">
    <id column="id" jdbcType="INTEGER" property="id" />
    <result column="name" jdbcType="VARCHAR" property="name" />
    <result column="sex" jdbcType="VARCHAR" property="sex" />
    <association property="companyVO" javaType="com.example.demo.company.vo.CompanyVO">
        <result column="name" jdbcType="VARCHAR" property="name" />
        <result column="region" jdbcType="VARCHAR" property="region" />
        <result column="boss" jdbcType="VARCHAR" property="boss" />
    </association>
</resultMap>

<select id="findAll" resultMap="BaseResultMap">
    select tb.id,tb.name,tb.sex,tc.name ,tc.region ,tc.boss
    from t_boss tb left join t_company tc on tb.name = tc.boss
</select>

测试下代码,具体调就自己实现吧,测试日志如下

image.png

我们在一对一关联中,用到了association 标签,主要用于一对一的业务场景。当一对多的场景时,会用到另一个标签,collection,示例如下

一对多

针对一对多的业务场景,我们在t_company表在插入几条数据,插入语句如下

INSERT INTO t_company
( name, region, boss)
VALUES( '阿里云', '杭州', '风清扬');
INSERT INTO t_company
( name, region, boss)
VALUES('腾讯游戏', '深圳', '小马哥');
INSERT INTO t_company
(name, region, boss)
VALUES( '腾讯体育', '深圳', '小马哥');
INSERT INTO t_company
( name, region, boss)
VALUES('京东物流', '北京-亦庄', '东哥');
INSERT INTO t_company
(name, region, boss)
VALUES( '京东科技', '北京-亦庄', '东哥');

修改下BossVO实体类

@Data
public class BossVO {
    /**
     * id
     */
    private Integer id;

    /**
     * 名称
     */
    private String name;

    /**
     * 区域
     */
    private String sex;

    /**
     * 公司
     */
    private List<CompanyVO> companyVOS;
}

修改resultMap如下

<resultMap id="BaseResultMap" type="com.example.demo.company.vo.BossVO">
    <id column="id" jdbcType="INTEGER" property="id" />
    <result column="name" jdbcType="VARCHAR" property="name" />
    <result column="sex" jdbcType="VARCHAR" property="sex" />
    <collection property="companyVOS" ofType="com.example.demo.company.vo.CompanyVO">
        <result column="name" jdbcType="VARCHAR" property="name" />
        <result column="region" jdbcType="VARCHAR" property="region" />
        <result column="boss" jdbcType="VARCHAR" property="boss" />
    </collection>
</resultMap>

<select id="findAll" resultMap="BaseResultMap">
    select tb.id,tb.name,tb.sex,tc.name ,tc.region ,tc.boss
    from t_boss tb left join t_company tc on tb.name = tc.boss
</select>

查询结果日志如下

image.png

MyBatis的懒加载

懒加载也就是延时加载或者按需加载,当有使用时才会去加载。在关联查询的业务场景中,先查询主表,当有使用要求时,才会去关联查询子表,这样可以减轻对数据库的压力。我们改造一对多的例子,看下延时加载的

首先我们增加下延时加载的配置。

#懒加载
mybatis.configuration.lazy-loading-enabled=true

改造下之前一对多的查询代码

<resultMap id="BaseResultMap" type="com.example.demo.company.vo.BossVO">
    <id column="id" jdbcType="INTEGER" property="id" />
    <result column="name" jdbcType="VARCHAR" property="name" />
    <result column="sex" jdbcType="VARCHAR" property="sex" />
    <collection property="companyVOS" ofType="com.example.demo.company.vo.CompanyVO"
                select="selectCompanyByBoss" column="name" >
    </collection>
</resultMap>
<select id="findAll" resultMap="BaseResultMap">
    select tb.id,tb.name,tb.sex
    from t_boss tb 
</select>
<select id="selectCompanyByBoss" resultType="com.example.demo.company.vo.CompanyVO">
    select name,region,boss from t_company WHERE boss=#{name}
</select>

下面是测试代码及日志,还有日志说明

@org.junit.Test
public void findAll(){
    System.out.println("第一次查询开始~~~~~");
    List<BossVO> all = bossService.findAll();
    System.out.println("第一次查询结束~~~~~");

    for (BossVO bossVO : all) {
        System.out.println(bossVO.getName()+"  遍历,打印开始~~~~~");
        System.out.println(bossVO.getCompanyVOS().toString());
        System.out.println(bossVO.getName()+"  遍历,打印结束~~~~~");

    }
}

image.png

配置文件中的 mybatis.configuration.lazy-loading-enabled=true 是全局性的,对于个别查询语句有特殊要求时,可在局部配置,局部配置项的优先级是高于全局配置的,我们再在之前的查询语句作出修改,我们增加一个取消懒加载的配置,当然fetchType也可以单独配置懒加载

image.png

查看查询日志,显然进行了实时加载。

image.png

结语

就写到这里吧,十分感谢你能看懂这里,希望通过这篇文章,让大家对MyBatis有个初步的了解,还有一些没有提到的地方,比如注解开发、比如MyBatis的缓存,因为感觉平时用不到或很少用,就没写,如果大家感兴趣,可以找一下其他博文。示例有很多不规范的地方,请大家宽容。