SSM第六讲 MyBatis的高级特性

MyBatis的高级特性

1 学习目标

1,理解Mybatis多对多关系

2,掌握Mybatis的延迟加载

3,掌握Mybatis缓存

4,掌握逆向工程生成mapper和实体类

2 Mybatis 延迟加载

2.1 延迟加载介绍

通过前面的学习,我们已经掌握了 Mybatis 中一对一,一对多,多对多关系的配置及实现,可以实现对象的关联查询。实际开发过程中有时候我们并不需要在加载用户信息时,就加载他的账户信息。 而是在使用用户账号的时候,再向数据库查询,此时就是我们所说的延迟加载。

即时加载:

// 条件查询
@Test
public void findAll() throws Exception {
     
    //  SELECT u.*,a.* FROM USER u LEFT JOIN account a ON a.UID=u.id;
    //  执行userDao.findAll()方法,执行查询用户、账户信息,一次加载所有的数据,这种叫做及时加载。
    List<User> list = userDao.findAll();
    for(User u : list){
     
        System.out.println(u);
        System.out.println(u.getAccounts());
    }
}

2.2 何为延迟加载?

延迟加载:就是在需要用到数据时才进行加载,不需要用到数据时就不加载数据。延迟加载也称懒加载.

好处: 先从单表查询,需要时再从关联表去关联查询,大大提高数据库性能,因为查询单表要比关联查询多张表速度要快。
坏处: 因为只有当需要用到数据时,才会进行数据库查询,这样在大批量数据查询时,因为查询工作也要消耗时间,所以可能造成用户等待时间变长,造成用户体验下降。

如何实现延迟加载:前面实现多表操作时,我们使用了resultMap 来实现一对一,一对多,多对多关系的操作。主要是通过 association、 collection 实现一对一及一对多映射。 association、 collection 具备延迟加载功能。

思考:什么是及时加载?什么是延时加载?

及时加载:一次加载所有数据。

延迟加载:也叫做懒加载,再需要用到数据时候再查询数据。

2.3 一对一实现延时加载

2.3.1 实现需求

​ 需求:查询Account账户信息时候,也要显示User用户信息,但User用户信息用到的时候再向数据库发送查询语句。

数据库中实现:

-- 一对一延迟加载
-- 需求:查询账户,同时也要显示用户。但用户信息是再用到的时候再查询.
-- 实现过程:
-- 1) 查询账户
SELECT * FROM account
-- 3) 使用用户对象数据时候,查询用户
SELECT * FROM USER WHERE id=46 

2.3.2 entity

public class Account {
     
    private int accountId;
    private int uid;
    private double money;
    // 一个账户对应一个用户
    private User user;
}

public class User {
     
    private int  id;
    private String username;
    private Date birthday;
    private String sex;
    private String address;
    // 一个用户,对应多个账户
    private List<Account> accounts;
}

2.3.3 dao

public interface AccountMapper {
     
    /**
     * 查询账户
     */
    List<Account> findAll();
}

public interface UserMapper {
     
    /**
     * 根据用户id查询
     */
    User findById(int id);
}

2.3.4 UserMapper.xml



<mapper namespace="com.itdfbz.dao.UserMapper" >
   <select id="findById" resultType="user">
      select * from user where id=#{id}
  select>
mapper>

2.3.5 AccountMapper.xml 延时加载关键配置



<mapper namespace="com.itdfbz.dao.AccountMapper">

   <resultMap id="accountResultMap" type="account">
       
       <id property="accountId" column="accountId">id>
       <result property="uid" column="uid">result>
       <result property="money" column="money">result>
       
       
       
       <association property="user" javaType="user" column="uid"
                    select="com.itdfbz.dao.UserMapper.findById">association>
   resultMap>

   <select id="findAll" resultMap="accountResultMap">
       SELECT * FROM account
   select>
mapper>

2.3.6 测试,观察问题

@Test
public void test1() {
     
    AccountMapper accountMapper = session.getMapper(AccountMapper.class);
    List<Account> accountList = accountMapper.findAll();
    for (Account account : accountList) {
     
        System.out.println(account.getAccountId());
    }
}

​ 执行结果:

SSM第六讲 MyBatis的高级特性_第1张图片

​ 发现,我们在测试中,只输出了账户信息,并没有使用用户信息,但还是查询了用户,并没有应用懒加载。如何解决?

2.3.7 解决方案

2.3.7.1 解决1:

查看mybatis官网

http://www.mybatis.org/mybatis-3/zh/configuration.html

打开页面,找到settings

SSM第六讲 MyBatis的高级特性_第2张图片

然后在SqlMapConfig.xml中配置开启懒加载支持

SSM第六讲 MyBatis的高级特性_第3张图片

最后,再次执行测试

1.测试类中没有使用Account账户的User信息

@Test
public void find() throws  Exception{
     
    List<Account> list = accountDao.findAccounts();
    for (Account a : list) {
      
        System.out.println(a.getAccountId() + ","+a.getUid() + "," +a.getMoney());
    }
}

测试结果

SSM第六讲 MyBatis的高级特性_第4张图片

2.测试类中有使用Account账户的User信息

@Test
public void test1() {
     
    AccountMapper accountMapper = session.getMapper(AccountMapper.class);
    List<Account> accountList = accountMapper.findAll();
    for (Account account : accountList) {
     
        System.out.println(account.getAccountId()+"----"+account.getUser());
    }
}

测试结果: 可以看到使用对象数据的时候,才向数据库发送查询的SQL
SSM第六讲 MyBatis的高级特性_第5张图片

2.3.7.2 解决2:

(1) 注释SqlMapConfig.xml中全局懒加载的的开启

SSM第六讲 MyBatis的高级特性_第6张图片

(2) 修改映射文件

SSM第六讲 MyBatis的高级特性_第7张图片

通过打印的SQL可以看到,懒加载一样生效:

SSM第六讲 MyBatis的高级特性_第8张图片

2.3.8 小结

通过本示例,我们可以发现 Mybatis 的延迟加载对于提升软件性能这是一个不错的手段。实现的关键: association 的配置

<resultMap id="accountResultMap" type="account">
    
    <id property="accountId" column="accountId">id>
    <result property="uid" column="uid">result>
    <result property="money" column="money">result>
    
    
    
    
    <association property="user" javaType="user" column="uid" fetchType="lazy"
                 select="com.itdfbz.dao.UserMapper.findById">association>
resultMap>

2.4 一对多实现延时加载

2.4.1 需求

查询用户信息,再使用用户账户时候再查询账户。

数据库实现:

-- 一对多延迟加载
-- 1) 查询用户
SELECT * FROM USER;
-- 2) 使用用户的账户对象数据时候再查询账户
SELECT * FROM account WHERE uid=46;

2.4.2 entity

public class User {
     
   private Integer id;
   private String username;
   private Date birthday;
   private String sex;
   private String address;
   // 一个用户,对应多个账户
   private List<Account> accounts;
}

2.4.3 dao

public interface UserMapper {
     
    /**
     * 查询所有用户
     */
    List<User> findAll();
}

public interface AccountMapper {
     
    /**
     * 根据用户的id查询
     */
    List<Account> findAccountByUserId(Integer userId);
}

2.4.4 AccountMapper.xml


<select id="findAccountByUserId" resultType="account">
   select * from account where uid=#{userId}
select>

2.4.5 UserMapper.xml 延时加载关键配置

<resultMap id="userResultMap" type="user">
   <id property="id" column="id">id>
   <result column="username" property="username">result>
   <result column="sex" property="sex">result>
   <result column="address" property="address">result>

   
   <collection property="accounts" column="id"
               ofType="account"
               select="com.itdfbz.dao.AccountMapper.findAccountByUserId">
   collection>
resultMap>
<select id="findAll" resultMap="userResultMap">
   select * from user
select>

2.4.6 开启延时加载支持

SSM第六讲 MyBatis的高级特性_第9张图片

2.4.7 测试

@Test
public void test1() {
     
    UserMapper userMapper = session.getMapper(UserMapper.class);
    List<User> userList = userMapper.findAll();
    for (User user : userList) {
     
        System.out.println(user.getSex());
    }
}

测试结果:

SSM第六讲 MyBatis的高级特性_第10张图片

// 查询用户
@Test
public void find() throws  Exception{
     
    List<User> list = userDao.findUsers();
    for (User u : list) {
     
        System.out.println(u.getId() + "," + u.getUsername());
        System.out.println(u.getAccounts());
    }
}

SSM第六讲 MyBatis的高级特性_第11张图片

2.5. 注解实现延迟加载开发

2.5.1 注解实现一对一映射及延迟加载

SSM第六讲 MyBatis的高级特性_第12张图片

2.5.1.1 entity

(1) 账户与用户,一对一

public class Account implements Serializable {
     
    private int accountId;
    
    private int uid;
    
    private double money;
    
    //一个账号对应一个账户
    private User user;
}

(2) 用户与账户,一对多

public class User implements Serializable {
     
    private Integer id;
    private String username;
    private Date birthday;
    private String sex;
    private String address;
    
    //一个用户对应多个账号
    private List<Account> accountList;
}

2.5.1.2 dao

public interface UserMapper {
     

    // 根据用户id查询
    @Select("select * from user where id=#{id}")
    User findById(int id);
}

package com.itdfbz.dao;

import com.itdfbz.entity.Account;
import com.itdfbz.entity.User;
import org.apache.ibatis.annotations.One;
import org.apache.ibatis.annotations.Result;
import org.apache.ibatis.annotations.Results;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.mapping.FetchType;

public interface AccountMapper {
     

    /*
    *
    * @One:代表一对一
    * select:要映射到本表属性的数据提供方法
    * user:本类中要映射的属性名
    * uid:外键,要传入findById方法的参数
    * fetchType: 配置懒加载
    *   LAZY:懒加载
    *   EAGER:立即加载
    *
    * */
    @Select("select * from account where accountId=#{id}")
    @Results({
     
            @Result(id=true,property = "accountId",column = "accountId"),
            @Result(property = "uid",column = "uid"),
            @Result(property = "money",column = "money"),
            @Result(property = "user",column = "uid",javaType = User.class,
                one = @One(select = "com.itdfbz.dao.UserMapper.findById",fetchType = FetchType.EAGER )

            )
    })
    Account findById(Integer id);
}

2.5.1.3 测试

@Test
public void test1(){
     
    AccountMapper mapper = session.getMapper(AccountMapper.class);
    Account account = mapper.findById(1);
    System.out.println(account.getAccountId());
    System.out.println(account.getUser());
}

2.5.2 注解实现一对多映射及延迟加载

2.5.1.1 dao 接口

/**
 * many:代表一对多
 * accountList:代表本类中的多方属性名
 * id:要传入findByUserId的参数
 * List.class:多方集合类型
 * FetchType.LAZY:懒加载
 * 
 */
@Select("select * from user where id=#{id}")
@Results({
     
        @Result(id = true,property = "id",column = "id"),
        @Result(property = "username",column = "username"),
        @Result(property = "birthday",column = "birthday"),
        @Result(property = "sex",column = "sex"),
        @Result(property = "address",column = "address"),
        @Result(property = "accountList",column = "id",javaType = List.class,
                many=@Many(select = "com.itdfbz.dao.AccountMapper.findByUserId",fetchType = FetchType.LAZY)
        )
})

2.5.1.2 测试

@Test
public void test2() {
     
    UserMapper userMapper = session.getMapper(UserMapper.class);

    User user = userMapper.findById(46);
    System.out.println(user.getUsername());
    List<Account> accountList = user.getAccountList();
    for (Account account : accountList) {
     
        System.out.println(account);
    }

}

3 Mybatis 缓存机制

3.1 缓存介绍

像大多数的持久化框架一样, Mybatis 也提供了缓存策略,通过缓存策略来减少数据库的查询次数,从而提高性能。Mybatis 中缓存分为一级缓存,二级缓存。

SSM第六讲 MyBatis的高级特性_第13张图片

从图中可以看出什么 ? 一级缓存是基于SqlSessoion的缓存,一级缓存的内容不能跨sqlsession。由mybatis自动维护。二级缓存是基于映射文件的缓存,缓存范围比一级缓存更大。不同的sqlsession可以访问二级缓存的内容。哪些数据放入二级缓存需要自己指定。

3.2 一级缓存测试

同一个SqlSession的一级缓存的测试:

@Test
public void test1() {
     
    SqlSession session = factory.openSession();

    UserMapper userMapper1 = session.getMapper(UserMapper.class);

    User user1 = userMapper1.findById(41);   //发送sql查询出来,放入一级缓存
    System.out.println(user1.getUsername());
    
    User user2 = userMapper1.findById(41);       //直接从一级缓存中查询(不发送sql)
    System.out.println(user2.getUsername());

    session.close();            //session关闭(此session的一级缓存销毁)

    SqlSession session2 = factory.openSession();
    UserMapper userMapper2 = session2.getMapper(UserMapper.class);
    User user3 = userMapper2.findById(41);          //发送sql查询一级缓存

    System.out.println(user3.getUsername());

}

​ 测试结果分析:

SSM第六讲 MyBatis的高级特性_第14张图片

小结

SSM第六讲 MyBatis的高级特性_第15张图片

tips:如果更改了一级缓存中的任何数据那么一级缓存会被清空

3.3 二级缓存

3.3.1 介绍

二级缓存是 mapper 映射级别的缓存,多个 SqlSession 去操作同一个 Mapper 映射的 sql 语句,多个SqlSession可以共用二级缓存,二级缓存是跨SqlSession 的。

二级缓存结构图:

SSM第六讲 MyBatis的高级特性_第16张图片

首先开启 mybatis的二级缓存。
sqlSession1去查询用户信息,查询到用户信息会将查询数据存储到二级缓存中。sqlSession2去查询与 sqlSession1 相同的用户信息, 首先会去缓存中找是否存在数据,如果存在直接从缓存中取出数据。如果 SqlSession3 去执行相同 mapper 映射下 sql,执行 commit 提交, 将会清空该 mapper映射下的二级缓存区域的数据。

3.3.2 测试二级缓存

3.3.2.1 第一步:开启二级缓存

SSM第六讲 MyBatis的高级特性_第17张图片

因为 cacheEnabled 的取值默认就为 true,所以这一步可以省略不配置。为 true代表开启二级缓存;为 false 代表不开启二级缓存。

官网介绍:

在这里插入图片描述

3.3.2.2 第二步:配置mapper

<mapper namespace="com.itdfbz.dao.UserMapper">

    
    <cache>cache>
    <select id="findById" parameterType="int" resultType="user" useCache="true">
        select * from user where id=#{id}
    select>
mapper>

将 UserMapper.xml 映射文件中的标签中设置 useCache=”true”代表当前这个 statement 要使用二级缓存,如果不使用二级缓存可以设置为 false。
注意: 针对每次查询都需要最新的数据 sql,要设置成useCache=false,禁用二级缓存。

3.3.2.3 第三步:实体类实现Serializable

当我们在使用二级缓存时,所缓存的类一定要实现 java.io.Serializable 接口:
SSM第六讲 MyBatis的高级特性_第18张图片

3.3.2.4 测试

public void test1() throws IOException {
     

    InputStream is = Resources.getResourceAsStream("SqlMapConfig.xml");

    SqlSessionFactoryBuilder builder=new SqlSessionFactoryBuilder();
    SqlSessionFactory factory = builder.build(is);

    SqlSession session1 = factory.openSession();

    UserMapper mapper1 = session1.getMapper(UserMapper.class);

    User user1 = mapper1.findById(41);          //发送sql查询,放入一级缓存
    System.out.println(user1);

    session1.close();           //关闭一级缓存,并将一级缓存的内容写入二级缓存

    SqlSession session2 = factory.openSession();
    UserMapper mapper2 = session2.getMapper(UserMapper.class);

    User user2 = mapper2.findById(41);          //从二级缓存查询(反序列化)直接返回(此时一级缓存还是空的)
    User user3 = mapper2.findById(41);      //查询二级缓存(反序列化)

    System.out.println(user3==user2);       //false

    session2.close();
    is.close();
}

测试结果

SSM第六讲 MyBatis的高级特性_第19张图片

二级缓存执行原理:

SSM第六讲 MyBatis的高级特性_第20张图片

tips:如果更改了任何数据那么二级缓存和一级缓存都将被清空

3.4 MyBatis缓存深入

一级缓存测试:

@Test
public void test2() throws IOException {
     
    InputStream is = Resources.getResourceAsStream("SqlMapConfig.xml");

    SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
    SqlSessionFactory factory = builder.build(is);

    SqlSession session1 = factory.openSession();

    UserMapper mapper1 = session1.getMapper(UserMapper.class);

    User user1 = mapper1.findById(41);          //发送sql查询,放入一级缓存
    user1.setAddress("NanChang");
    mapper1.update(user1);              //清空一级缓存 update delete 

    User user2 = mapper1.findById(41);          //再次发送sql

    session1.close();           //关闭一级缓存,并将一级缓存的内容写入二级缓存
}

二级缓存测试:

@Test
public void test3() throws IOException {
     
    InputStream is = Resources.getResourceAsStream("SqlMapConfig.xml");

    SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
    SqlSessionFactory factory = builder.build(is);

    SqlSession session1 = factory.openSession();

    UserMapper mapper1 = session1.getMapper(UserMapper.class);

    User user1 = mapper1.findById(41);          //发送sql查询,放入一级缓存
    System.out.println(user1);

    session1.close();           //关闭一级缓存,并将一级缓存的内容写入二级缓存

    SqlSession session2 = factory.openSession();
    UserMapper mapper2 = session2.getMapper(UserMapper.class);

    User user2 = mapper2.findById(41);          //从二级缓存查询(反序列化)直接返回(此时一级缓存还是空的)
    mapper2.update(user2);                      //清空二级缓存、一级缓存

    User user3 = mapper2.findById(41);      //查询二级缓存--->一级缓存--->数据库
    User user4 = mapper2.findById(41);      //查询二级缓存(空的)---->查询一级缓存---返回结果
    System.out.println(user3 == user4);       //true

    session2.close();
    is.close();
}

3.5 MyBatis缓存总结

一级缓存和二级缓存的执行顺序为:二级缓存---->一级缓存---->数据库

查询语句始终是由一级缓存发送的

一级缓存默认由MyBatis维护,我们只需了解使用即可

二级缓存需要我们开启:

1、在SqlMapConfig.xml中开启二级缓存(默认开启)

<settings>
    
    <setting name="cacheEnabled" value="true"/>
settings>

2、在mapper文件中加入标签代表本mapper文件的所有查询结果放入二级缓存,并保证useCache不为false(默认为true)

<cache>cache>
<select id="findById" parameterType="int" resultType="user" useCache="true">
    select * from user where id=#{id}
select>

3、MyBatis的二级缓存默认采用序列化/反序列化来保证对象的存取,所以所有的entity对象都应该实现serializable接口

3.6 mybatis自身缓存的弊

A. 分布式的项目架构下,也就是最少使用两个服务器,如果使用两个服务器mybatis的缓存技术就无法在两个服务器通用就是,也就是两个服务器无法达到数据通用,比如我在一个服务器存储了我的信息,但是我转跳到另一个服务器那使用mybatis数据就是需要从新加载,这里就是一个非常大的问题。

B. mybatis无法实现细粒度的缓存管理,当你查询大量数据的时候而且将数据存储到mybatis二级缓存中的时候,但是一旦队一个数据操作增加,删除,修改,这里二级缓存就全部清空,而mybatis无法实现对这里单个信息的修改

问题:如何解决这个缺陷?

答:使用第三方的缓存如:ehcache、redis

SSM第六讲 MyBatis的高级特性_第21张图片

3.7.ehcache的整合配置

ehcache需要slf4j这个日志包支持

3.7.1.导入包

<dependency>
        <groupId>log4jgroupId>
        <artifactId>log4jartifactId>
        <version>1.2.17version>
    dependency>

    <dependency>
        <groupId>org.slf4jgroupId>
        <artifactId>slf4j-log4j12artifactId>
        <version>1.7.25version>
    dependency>
    <dependency>
        <groupId>mysqlgroupId>
        <artifactId>mysql-connector-javaartifactId>
        <version>5.1.35version>
    dependency>
    <dependency>
        <groupId>org.mybatis.cachesgroupId>
        <artifactId>mybatis-ehcacheartifactId>
        <version>1.1.0version>
    dependency>

3.7.2.引入缓存配置文件

classpath下添加:ehcache.xml(EhcacheCache实现类的默认配置文件)


<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd"
  updateCheck="false">

<diskStore path="F:\temp\cache"/>

<defaultCache 
maxElementsInMemory="1000" 
maxElementsOnDisk="10000000" 
eternal="false" 
overflowToDisk="true" 
diskPersistent="false" 
timeToIdleSeconds="120" 
timeToLiveSeconds="120" 
memoryStoreEvictionPolicy="LRU">
defaultCache>
ehcache>

3.7.3.开启ehcache缓存

EhcacheCache 是ehcache对Cache接口的实现;修改mapper.xml文件,在cache标签的type属性中指定EhcacheCache。覆盖其默认实现

根据需求调整缓存参数:(根据条件对EhcacheCache实现类的参数调整,可选)

    
       
       
       
       
       
       
       
       
       
       
     

结果:观察缓存命中,且在写入磁盘配置路径处可以看到生成文件

SSM第六讲 MyBatis的高级特性_第22张图片

4 逆向工程

idea的逆向工程,通过maven的插件来实现

4.1.创建maven项目

SSM第六讲 MyBatis的高级特性_第23张图片

SSM第六讲 MyBatis的高级特性_第24张图片

4.2. 引入插件mybatis-generator-maven-plugin

在maven项目的pom.xml 添加mybatis-generator-maven-plugin 插件

<build>

    <plugins>
        <plugin>
            <groupId>org.mybatis.generatorgroupId>
            <artifactId>mybatis-generator-maven-pluginartifactId>
            <version>1.3.2version>
        plugin>
    plugins>
build>

更新下载依赖包

SSM第六讲 MyBatis的高级特性_第25张图片

4.3. generatorConfig.xml

在maven项目下的src/main/resources 目录下建立名为 generatorConfig.xml的配置文件,作为mybatis-generator-maven-plugin 插件的执行目标,模板如下:




<generatorConfiguration>
    
    <properties resource="mybatis.properties"/>
    
    <classPathEntry location="D:\.m2\repository\mysql\mysql-connector-java\5.1.35\mysql-connector-java-5.1.35.jar" />
    
    <context id="testTables" targetRuntime="MyBatis3">
        <commentGenerator>
            
            <property name="suppressAllComments" value="true" />
        commentGenerator>
        
        <jdbcConnection driverClass="${jdbc.driver}"
                        connectionURL="${jdbc.url}" userId="${jdbc.username}"
                        password="${jdbc.password}">
        jdbcConnection>
        
        

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

        
        <javaModelGenerator targetPackage="cn.nyse.entity"
                            targetProject="src/main/java">
            
            <property name="enableSubPackages" value="false" />
            
            <property name="trimStrings" value="true" />
        javaModelGenerator>
        
        <sqlMapGenerator targetPackage="cn.nyse.mapper"
                         targetProject="src/main/java">
            
            <property name="enableSubPackages" value="false" />
        sqlMapGenerator>
        
        <javaClientGenerator type="XMLMAPPER"
                             targetPackage="cn.nyse.mapper"
                             targetProject="src/main/java">
            
            <property name="enableSubPackages" value="false" />
        javaClientGenerator>
        
        <table tableName="${jdbc.table.items}">table>

      

        
    context>
generatorConfiguration>

mybatis.properties:


jdbc.driver = com.mysql.jdbc.Driver
jdbc.url = jdbc:mysql://localhost:3306/mybatis
jdbc.username = root
jdbc.password = 123456

#数据库中要生成的表
jdbc.table.items = student

项目目录如下

在这里插入图片描述

4.4. 使用maven运行mybatis-generator-maven-plugin插件

SSM第六讲 MyBatis的高级特性_第26张图片

观察控制台输出结果:

SSM第六讲 MyBatis的高级特性_第27张图片

生成的文件:

SSM第六讲 MyBatis的高级特性_第28张图片

    




mybatis.properties:

```properties

jdbc.driver = com.mysql.jdbc.Driver
jdbc.url = jdbc:mysql://localhost:3306/mybatis
jdbc.username = root
jdbc.password = 123456

#数据库中要生成的表
jdbc.table.items = student

项目目录如下

在这里插入图片描述

4.4. 使用maven运行mybatis-generator-maven-plugin插件

SSM第六讲 MyBatis的高级特性_第29张图片

观察控制台输出结果:

SSM第六讲 MyBatis的高级特性_第30张图片

生成的文件:

SSM第六讲 MyBatis的高级特性_第31张图片

你可能感兴趣的:(SSM技术栈详解及面试题汇总,java,mybatis)