Mybatis之女朋友看了都会的超详细保姆级笔记!看完不会算我输!

1. 简介

1.1 Mybatis介绍

  1. MyBatis是一款优秀的持久层框架
  2. 它支持定制化SQL、存储过程以及高级映射。
  3. MyBatis避免了几乎所有JDBC代码和手动设置参数以及获取结果集。MyBatis可以使用简单的XML或注解来配置和映射原生类型、接口和Java的POJO(Plain Old Java Objects,普通老式Java对象)为数据库中的记录。


1.2 安装Mybatis

  • Maven仓库

    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis</artifactId>
        <version>3.5.2</version>
    </dependency>
    


1.3 持久层

  1. 进行持久化的代码块
  2. 层界限十分明显


1.4 为什么需要Mybatis

优点:

  1. 简单易学
  2. 灵活
  3. sql和代码的分离,提高了可维护性。
  4. 提供映射标签,支持对象与数据库的orm字段关系映射
  5. 提供对象关系映射标签,支持对象关系组建维护
  6. 提供xml标签,支持编写动态sql



2. 第一个Mybatis程序

环境搭建

  1. 数据库

    CREATE DATABASE `mybatis`;
    USE `mybatis`;
    
    CREATE TABLE `user`(
    `id` INT(20) NOT NULL PRIMARY KEY,
    `name` VARCHAR(30) DEFAULT NULL,
    `pwd` VARCHAR(30) DEFAULT NULL
    )ENGINE=INNODB DEFAULT CHARSET=utf8;
    
    INSERT INTO `user` (`id`,`name`,`pwd`) VALUES
    (1,'狂神','123456'),
    (2,'张三','123456'),
    (3,'李四','123456')
    
  2. 新建Maven项目:Mybatis-Study,删除src作为父工程,导入依赖

    <dependencies>
        <!--Mysql驱动-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.46</version>
        </dependency>
        <!--Mybatis-->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.5.2</version>
        </dependency>
        <!--junit-->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>
        <!--lombok-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.18</version>
        </dependency>
    </dependencies>
    

第一个Mybatis程序

创建一个module,mybatis-01:

  1. 编写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>
        <environments default="development">
            <environment id="development">
                <transactionManager type="JDBC"/>
                <dataSource type="pOOLED">
                    <property name="driver" value="com.mysql.jdbc.Driver"/>
                    <property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=true&amp;useUnicode=true&amp;characterEncoding=UTF-8"/>
                    <property name="username" value="root"/>
                    <property name="password" value="admin"/>
                </dataSource>
            </environment>
        </environments>
    </configuration>
    
  2. 编写mybatis工具类,获取sqlsessionFactory和sqlSession

    //sqlSessionFactory  -----> sqlSession
    public class MybatisUtils {
          
          
        private static SqlSessionFactory sqlSessionFactory;
    
        static {
          
          
            try {
          
          
                // 第一步:获取sqlSessionFactory对象!
                String resource = "mybbatis-config.xml";
                InputStream inputStream = Resources.getResourceAsStream(resource);
                sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
    
            } catch (IOException e) {
          
          
                e.printStackTrace();
            }
        }
    
        // 创建sqlSession,SqLSession完全包含了面向数据库执行SQL命令所需的所有方法。
        public static SqlSession getSqlSession(){
          
          
            return sqlSessionFactory.openSession();
        }
    }
    
  3. 编写代码:

    • 实体类

      @Data
      @AllArgsConstructor
      @NoArgsConstructor
      public class User {
              
              
          private int id;
          private String name;
          private String pwd;
      }
      
    • Dao及其实现类,接口实现类由原来JDBC的impl转换为一个Mapper配置文件

      public interface UserDao {
              
              
          List<User> getUserList();
      }
      
      <!--命名空间绑定一个对应的Dao接口-->
      <mapper namespace="com.kuang.dao.UserDao">
          <!--resultType:返回类型,如果接口返回的是集合,只要写集合泛型中的东西的全限定名!!!-->
          <select id="selectBlog" resultType="com.kuang.pojo.User">
              select * from mybatis.user
          </select>
      </mapper>
      
  4. Junit测试:

    public class UserDaoTest {
          
          
        @Test
        public void test(){
          
          
            // 1.获取SqlSession
            SqlSession sqlSession = MybatisUtils.getSqlSession();
    
            // 方式一:getMapper
            UserDao userDao = sqlSession.getMapper(UserDao.class);
            List<User> userList = userDao.getUserList();
    
            //方式二:
            List<User> userList = sqlSession.selectList("com.kuang.dao.UserDao.getUserList");
            for (User user : userList) {
          
          
                System.out.println(user);
            }
    
            // 3.关闭sqlSession
            sqlSession.close();
        }
    }
    
    • 报错:没有配置mapper文件

      [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8dK7W2dT-1619756230249)(E:\学习笔记\图片\image-20210427145403888.png)]

      <!--核心配置文件-->
      <configuration>
          <!--每一个Mapper.xml都需要在Mybatis核心配置文件中注册-->
          <mappers>
              <mapper resource="com/kuang/dao/UserMapper.xml"/>
          </mappers>
      </configuration>
      
    • 还是报错:可能存在错误,是因为配置文件没有生效,我们在子pom和父pom中都加入如下:

      [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qIz0c4oo-1619756230252)(E:\学习笔记\图片\image-20210427150516944.png)]

      <!--在bui7d中配置resources,来防止我们资源导出失败的问题-->
      <build>
          <resources>
              <resource>
                  <directory>src/main/resources</directory>
                  <includes>
                      <include>**/*.properties</include>
                      <include>**/*.xml</include>
                  </includes>
                  <filtering>true</filtering>
              </resource>
              <resource>
                  <directory>src/main/java</directory>
                  <includes>
                      <include>**/*.properties</include>
                      <include>**/*.xml</include>
                  </includes>
                  <filtering>true</filtering>
              </resource>
          </resources>
      </build>
      
    • 报错:[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IOEaH9t7-1619756230253)(E:\学习笔记\图片\image-20210427151831651.png)]

      解决方案:把mybatis-config.xml文件上文件头的encoding="UTF-8"改为encoding="UTF8"

    • 报错:[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mljAgIKy-1619756230256)(E:\学习笔记\图片\image-20210427152011796.png)]

      解决方案:数据库连接的时候加上时区:

      <property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=true&amp;useUnicode=true&amp;characterEncoding=UTF-8&amp;serverTimezone=UTC"/>
      

      成功:




3. 增删改查实现

查询语句

  1. 标签为
  2. id:就是对应的namespace中绑定接口的方法名
  3. resultType:Sql语句执行的返回值!如果返回的是集合,一定要写集合里的泛型!
  4. parameterType:Sql语句需要传递的参数类型

增加语句

标签为

//插入用户int addUser(User user);

在Mapper.xml中,如果没有传参,他还是可以拿到实体类的属性,直接通过#{}即可。

<insert id="addUser" parameterType="com.kuang.pojo.User">
    insert into mybatis.user(id,name,pwd) values (#{id},#{name},#{pwd})
</insert>

增删改一定要提交事务!!!!!!!!!

public void test2(){
    
    
    SqlSession sqlSession = MybatisUtils.getSqlSession();
    UserDao mapper = sqlSession.getMapper(UserDao.class);
    mapper.addUser(new User(4,"ran","123456"));

    //提交事务
    sqlSession.commit();
    sqlSession.close();
}

修改语句

标签为

//修改用户
int updateUser(User user);
<update id="updateUser" parameterType="com.kuang.pojo.User">
    update mybatis.user set name = #{name},pwd = #{pwd} where id = #{id}
</update>
@Test
public void test3(){
    
    
    SqlSession sqlSession = MybatisUtils.getSqlSession();
    UserDao mapper = sqlSession.getMapper(UserDao.class);
    mapper.updateUser(new User(4,"ranhaifeng","123456"));
    //提交事务
    sqlSession.commit();
    sqlSession.close();
}

删除语句

标签为

//删除用户
int deleteUser(int id);
<delete id="deleteUser" parameterType="int">
    delete from mybatis.user where id = #{id}
</delete>
@Test
public void test4(){
    
    
    SqlSession sqlSession = MybatisUtils.getSqlSession();
    UserDao mapper = sqlSession.getMapper(UserDao.class);
    mapper.deleteUser(4);
    //提交事务
    sqlSession.commit();
    sqlSession.close();
}



4. ParameterType:Map

背景

在插入和修改的时候,如果传入一个User,而User的字段全限定名非常长,那么就会十分冗余。

实现

  1. Map具有以下优点:

    • 不用写插入数据类型的全限定名
    • 可以自定义插入的数值名,即自己的key,SQL语句中是自己的key,然后我们向map中的对应的key设值,值就会被传入sql语句中!
    • 方法有多个参数的时候,只能用map
  2. Map实现插入:

    int insertUser(Map<String,Object> map);
    
     <insert id="insertUser" parameterType="map">
         insert into mybatis.user(id,name,pwd) values (#{helloid},#{helloname},#{hellopassword})
     </insert>
    
     public class UserDaoTest {
          
          
         @Test
         public void test(){
          
          
             // 1.获取SqlSession
             SqlSession sqlSession = MybatisUtils.getSqlSession();
    
             UserDao userDao = sqlSession.getMapper(UserDao.class);
             Map<String, Object> map = new HashMap<String, Object>();
             map.put("helloid",4);
             map.put("helloname","ranhaifeng");
             map.put("hellopassword","123456");
    
             userDao.insertUser(map);
             System.out.println(map);
             sqlSession.commit();
             sqlSession.close();
         }
     }
    



5. 模糊查询

public interface UserDao {
    
         List<User> selectUser(String val);}

方式1:直接在传入参数时带上通配符

List<User> users = userDao.selectUser("%张%");
<select id="selectUser" parameterType="String" resultType="com.kuang.pojo.User">
        select * from mybatis.user where name like #{val}
</select>

会有sql注入的危险!

方式2:直接在sql语句中写死(推荐)

List<User> users = userDao.selectUser("张");
<select id="selectUser" parameterType="String" resultType="com.kuang.pojo.User">
    select * from mybatis.user where name like "%"#{val}"%"
</select>



6. 配置解析

核心配置文件

  1. 核心配置文件是Mybatis-config.xml


6.1 环境配置

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6bFn5dDK-1619756230257)(E:\学习笔记\图片\image-20210427211229532.png)]

  1. 事务管理器有JDBC(默认)和MANAGED两种,后者一般没啥用
  2. 数据源的三种:
    • UNPOOLED:每次被请求时打开和关闭连接,很慢
    • POOLED(默认)
    • JNDI


6.2 属性优化

我们可以通过properties属性来实现引用配置文件,上述的property中的value值都是可以动态替换的,即可以在典型的Java属性文件中配置,也可通过Properties元素的子元素来传递。

  1. 编写一个配置文件db.properties

    driver=com.mysql.cj.jdbc.Driver
    url=jdbc:mysql://localhost:3306/mybatis?useSSL=true&amp;useUnicode=true&amp;characterEncoding=UTF-8&amp;serverTimezone=UTC
    username=admin
    password=root
    
  2. 在Mybatis核心配置文件中引入:

    • 可以直接引入外部文件

    • 可以在其中增加一些properties

      driver=com.mysql.cj.jdbc.Driverurl=jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTCusername=rootpassword=admin
      
      <!--核心配置文件-->
      <configuration>
          <properties resource="db.properties"/>
      </configuration>
      
    • 当外部配置文件和核心配置文件内的properties冲突时,优先外部配置文件



6.3 别名优化

问题引入

  1. 类型别名是为Java类型设置一个短的名字
  2. 存在的意义是用来减少类完全限定名的冗余

实现

  1. 普通起别名方法

    <!-- 给实体类起别名 -->
    <typeAliases>
        <typeAlias type="com.kuang.pojo.User" alias="User"/>
    </typeAliases>
    
    <mapper namespace="com.kuang.dao.UserDao">
        <select id="selectUser" parameterType="String" resultType="User">
            select * from mybatis.user where name like "%"#{val}"%"
        </select>
    </mapper>
    
  2. 也可以指定一个包名,Mybatis会在扫描这个包的Java Bean,在没有注解的情况下,默认别名就是这个类首字母小写的类名(其实大写也跑的出来!)

    <!-- 给实体类起别名 -->
    <typeAliases>
        <package name="com.kuang.pojo"/>
    </typeAliases>
    
  3. 直接在实体类上通过注解**@Alias**也可以指定别名(一定要做第二步,不然会失效!

    @Alias("hello")
    public class User {
          
          
        private int id;
        private String name;
        private String pwd;
    }
    


6.4 设置settings

介绍

这是Mybatis中及其重要的调整设置!

在这里插入图片描述



6.5 映射器

  1. 方式一:

     <!--每一个Mapper.xml都需要在Mybatis核心配置文件中注册-->
    <mappers>
        <mapper resource="com/kuang/dao/UserMapper.xml"/>
    </mappers>
    
  2. 方式二:使用class文件绑定注册:

    <mappers>
        <mapper class="com.kuang.dao.UserDao"/>
    </mappers>
    

    注意点:

    • 接口和它的Mapper配置文件必须同名且在同一个包下
  3. 方式三:使用扫描包进行绑定:(接口和它的Mapper配置文件必须同名且在同一个包下)

    <mappers>
    <!-- <mapper resource="com/kuang/dao/UserMapper.xml"/> -->
    <!--        <mapper class="com.kuang.dao.UserDao"/>-->
            <package name="com.kuang.dao"/>
    </mappers>
    



7. 生命周期和作用域

SqlSessionFactoryBuilder

一旦创建就不需要了,局部变量

SqlSessionFactory

  1. 可以想象为数据库连接池
  2. 一旦创建,没有任何理由丢弃它或重新创建一个实例
  3. 最佳作用域是应用作用域
  4. 最简单的使用就是使用单例模式或者静态单例模式

SqlSession

  1. 连接到连接池的一个请求!
  2. 需要开启跟关闭
  3. 不是线程安全,用完之后需要关闭,否则资源被占用




8. ResultMap结果集映射

属性名和字段名不一致问题

@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
    
    
    private int id;
    private String name;
    private String password;
}
<mapper namespace="com.kuang.dao.UserDao">
    <select id="getUserById" parameterType="int" resultType="com.kuang.pojo.User">
        select * from mybatis.user where id = #{id}
    </select>
</mapper>

测试出现问题:

在这里插入图片描述

解决方式1:起别名!

select id,name,pwd as password from mybatis.user where id = #{id}

解决方式2:resultMap结果集映射

结果集映射:

id	name	pwdid	name 	pwssword

  1. ResultMap的设计思想是,对于简单的语句根本不需要结果映射,复杂的语句只需要描述他们的关系

  2. ResultMap最优秀的地方在于,不需要显式指定字段名一致的属性:(只写字段名不同的属性




9. 日志

9.1 日志工厂

介绍

如果一个数据库操作出现了问题,我们需要拍错,日志就是最好的助手。在Mybatis中具体使用哪一个日志实现,在setting中指定!

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CqoPvSfS-1619756230259)(E:\学习笔记\图片\image-20210428114011671.png)]

  • LOG4J
  • STDOUT_LOGGING(标准日志输出)

标准日志工厂实现

<!--核心配置文件-->
<configuration>
    <settings>
        <setting name="logImpl" value="STDOUT_LOGGING"/>
    </settings>
</configuration>

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dmvkI6Oy-1619756230260)(E:\学习笔记\图片\image-20210428114741565.png)]



9.2 Log4j

介绍

  1. 通过Log4j,我们可以控制日志信息输送的目的地是控制台、文件、GUI组件
  2. 可以控制每一条日志的输出格式
  3. 通过定义每一条日志信息的级别,能够更加细致地控制日志的生成过程
  4. 通过一个配置文件来灵活配置,而不需要修改应用代码。

使用

  1. 先导入包

    <dependencies>
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.17</version>
        </dependency>
    </dependencies>
    
  2. log4j.properties

    #将等级为DEBUG的日志信息输出到console和file这两个目的地,console和file的定义在下面的代码
    log4j.rootLogger=DEBUG,console,file
    
    #控制台输出的相关设置
    log4j.appender.console = org.apache.log4j.ConsoleAppender
    log4j.appender.console.Target = System.out
    log4j.appender.console.Threshold=DEBUG
    log4j.appender.console.layout = org.apache.log4j.PatternLayout
    log4j.appender.console.layout.ConversionPattern=[%c]-%m%n
    
    #文件输出的相关设置
    log4j.appender.file = org.apache.log4j.RollingFileAppender
    log4j.appender.file.File=./log/kuang.log
    log4j.appender.file.MaxFileSize=10mb
    log4j.appender.file.Threshold=DEBUG
    log4j.appender.file.layout=org.apache.log4j.PatternLayout
    log4j.appender.file.layout.ConversionPattern=[%p][%d{yy-MM-dd}][%c]%m%n
    
    #日志输出级别
    log4j.logger.org.mybatis=DEBUG
    log4j.logger.java.sql=DEBUG
    log4j.logger.java.sql.Statement=DEBUG
    log4j.logger.java.sql.ResultSet=DEBUG
    log4j.logger.java.sql.PreparedStatement=DEBUG
    
  3. 配置log4j为日志的实现:

    <!--核心配置文件-->
    <configuration>
        <settings>
            <setting name="logImpl" value="LOG4J"/>
        </settings>
    </configuration>
    
  4. 使用!直接测试运行刚才的查询

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rlzMSMto-1619756230261)(E:\学习笔记\图片\image-20210428135431902.png)]

  5. 简单使用:

    • 在要使用log4j的类中,导入包import org.apache.log4j.Logger

    • 日志对象,参数为当前类的class对象

      static Logger logger = Logger.getLogger(UserDaoTestTwo.class);
      
    • 打不开.log文件?

    • 测试

      public class UserDaoTestTwo {
              
              
      
          static Logger logger = Logger.getLogger(UserDaoTestTwo.class);
      
          @Test
          public void getUserList(){
              
              
              logger.info("info:进入了testLog4j");
              logger.debug("info:进入了testLog4j");
              logger.error("info:进入了testLog4j");
          }
      }
      

      [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4ChyiHw5-1619756230262)(E:\学习笔记\图片\image-20210428141209774.png)]




10. 分页

为什么分页?

减少数据的处理量

Limit分页

select * from user limit 0,2;	//从下标第0个开始,每页显示2个人
select * from user limit 3;	//从下标第0个开始,每页显示3个人,相当于[0,3]


10.1 Limit分页

Mybatis分页

思路:分页需要传2个limit参数过去,多个参数优先考虑map,通过往map中放入2个键值对,然后在SQL语句中取map的key,从而拿到2个参数。

  1. 接口

    List<User> getUserByLimit(Map<String,Object> map);
    
  2. Mapper.xml

    <select id="getUserByRowBounds" resultType="user">
       select * from mybatis.user
    </select>
    
  3. 测试

    @Test
    public void getUserList(){
          
          
         SqlSession sqlSession = MybatisUtils.getSqlSession();
         //RowBounds实现
         RowBounds rowBounds = new RowBounds(1, 2);
         List<User> userList = sqlSession.selectList("com.kuang.dao.UserDao.getUserByRowBounds", null, rowBounds);
         for (User user : userList) {
          
          
             System.out.println(user);
         }
         sqlSession.close();
     }
    


10.2 RowBounds分页(了解)

  1. 接口

    List<User> getUserByRowBounds(Map<String,Object> map);
    
  2. mapper

    <select id="getUserByRowBounds" resultType="user">
        select * from mybatis.user
    </select>
    
  3. 测试

    @Test
    public void getUserList(){
          
          
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        //RowBounds实现
        RowBounds rowBounds = new RowBounds(1, 2);
        List<User> userList = sqlSession.selectList("com.kuang.dao.UserDao.getUserByRowBounds", null, rowBounds);
        for (User user : userList) {
          
          
            System.out.println(user);
        }
        sqlSession.close();
    }
    


10.3 分页插件PageHelper(了解)




11. 使用注解开发

实现

  1. 底层使用了反射实现!
  2. 几乎在有参数的情况下,都需要加一个@Param,即传到SQL的参数名
<!--每一个Mapper.xml都需要在Mybatis核心配置文件中注册-->
<mappers>
 	<mapper class="com.kuang.dao.UserDao"/>
</mappers>
@Select("select * from mybatis.user limit #{startIndex},#{pageSize}")
List<User> getUserByLimit(@Param("startIndex") int startIndex,@Param("pageSize") int pageSize);
@Test
public void getUserList(){
    
    
    SqlSession sqlSession = MybatisUtils.getSqlSession();
    UserDao userDao = sqlSession.getMapper(UserDao.class);
    List<User> userByLimit = userDao.getUserByLimit(0,2);
    System.out.println(userByLimit);

    sqlSession.close();
}

@Param

  1. 基本类型或者String类型,都要加上@Param
  2. 引用类型不需要加
  3. 如果只有一个基本类型的话,可以忽略,但是还是必须加上,代码规范
  4. 在SQL中引用的就是@Param中的属性名!

#{}和${}

  1. #{}可以防止SQL注入
  2. 一 般 用 于 传 入 数 据 库 对 象 , 比 如 数 据 库 表 名 , 而 且 传 入 的 数 据 直 接 显 示 在 生 成 的 S Q L 中 , {}一般用于传入数据库对象,比如数据库表名,而且传入的数据直接显示在生成的SQL中, SQL{abc}直接显示abc

实现自动提交

//sqlSessionFactory  -----> sqlSession
public class MybatisUtils {
    
    
    private static SqlSessionFactory sqlSessionFactory;

    static {
    
    
        try {
    
    
            // 第一步:获取sqlSessionFactory对象!
            String resource = "mybatis-config.xml";
            InputStream inputStream = Resources.getResourceAsStream(resource);
            sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        } catch (IOException e) {
    
    
            e.printStackTrace();
        }
    }

    // 创建sqlSession,SqLSession完全包含了面向数据库执行SQL命令所需的所有方法。
    // 设置为true,实现自动提交
    public static SqlSession getSqlSession(){
    
    
        return sqlSessionFactory.openSession(true);
    }
}

问题

使用注解开发无法解决字段名和实体类名不一致的问题!




12. Mybatis执行流程




13. 复杂查询(一对多多对一)

介绍

  1. 多个学生对应一个老师,即多对一。
  2. 对于学生这边而言,多个学生关联一个老师。
  3. 对于老师这边而言,一个老师集合很多学生。

环境搭建

  1. 数据库

    CREATE TABLE `teacher` (  `id` INT(10) NOT NULL,  `name` VARCHAR(30) DEFAULT NULL,  PRIMARY KEY (`id`)) ENGINE=INNODB DEFAULT CHARSET=utf8INSERT INTO teacher(`id`, `name`) VALUES (1, '秦老师'); CREATE TABLE `student` (  `id` INT(10) NOT NULL,  `name` VARCHAR(30) DEFAULT NULL,  `tid` INT(10) DEFAULT NULL,  PRIMARY KEY (`id`),  KEY `fktid` (`tid`),  CONSTRAINT `fktid` FOREIGN KEY (`tid`) REFERENCES `teacher` (`id`)) ENGINE=INNODB DEFAULT CHARSET=utf8INSERT INTO `student` (`id`, `name`, `tid`) VALUES (1, 小明, 1); INSERT INTO `student` (`id`, `name`, `tid`) VALUES (2, '小红', 1); INSERT INTO `student` (`id`, `name`, `tid`) VALUES (3, '小张', 1); INSERT INTO `student` (`id`, `name`, `tid`) VALUES (4, '小李', 1); INSERT INTO `student` (`id`, `name`, `tid`) VALUES (5, '小王', 1);
    
  2. 实体类:

    • 学生

      @AllArgsConstructor
      @NoArgsConstructor
      @Data
      public class Student {
              
              
          private int id;
          private String name;
      
          //学生关联一个老师
          private Teacher teacher;
      }
      
    • 老师

      @AllArgsConstructor
      @NoArgsConstructor
      @Data
      public class Teacher {
              
              
          private int id;
          private String name;
      }
      
  3. dao接口:StudentMapper、TeacherMapper

  4. mapper文件:StudentMapper.xml、TeacherMapper.xml



13.1 多对一处理

需求

查出所有学生的信息和对应老师的信息

问题

<mapper namespace="com.kuang.dao.StudentMapper">
    <select id="getStudent" resultType="student">
        select s.id,s.name,t.name from student s,teacher t where s.tid = t.id
    </select>
</mapper>

SQL语句在控制台验证正确,但是结果却错误!

思路

teacher是null,变相相当于是字段名和数据库名不一致问题,返回类型应该是resultmap

按照结果嵌套处理

<!--命名空间绑定一个对应的Dao接口-->
<mapper namespace="com.kuang.dao.StudentMapper">
    <select id="getStudent" resultMap="StudentTeacher">
        <!--起别名sid,sname,tname作为列-->
        select s.id sid,s.name sname,t.id ttid,t.name tname from student s,teacher t where s.tid = t.id
    </select>

    <resultMap id="StudentTeacher" type="Student">
        <result property="id" column="sid"/>
        <result property="name" column="sname"/>
        <association property="teacher" column="tid" javaType="Teacher">
            <result property="id" column="ttid"/>
            <result property="name" column="tname"/>
        </association>
    </resultMap>
</mapper>

按照过程嵌套处理

  1. 查询所有的学生
  2. 根据查询出所有学生的tid,寻找对应的id
<!--命名空间绑定一个对应的Dao接口-->
<mapper namespace="com.kuang.dao.StudentMapper">
    <select id="getStudent" resultMap="StudentTeacher">
        <!--起别名sid,sname,tname作为列-->
       select * from student
    </select>

    <resultMap id="StudentTeacher" type="student">
        <result property="id" column="id"/>
        <result property="name" column="name"/>
        <!--如果所包含的是一个对象:用association,如果包含的是一个集合,用collections-->
        <association property="teacher" column="tid"  javaType="Teacher" select="getTeacher"/>
    </resultMap>

    <select id="getTeacher" resultType="Teacher">
        select * from teacher where id = #{tid}
    </select>

</mapper>


13.2 一对多处理

比如:一个老师拥有多个学生!

对于老师而言,就是一对多的关系!

环境搭建

  1. 学生

    @AllArgsConstructor
    @NoArgsConstructor
    @Data
    public class Student {
          
          
        private int id;
        private String name;
    
        private int tid;
    }
    
  2. 老师

    @AllArgsConstructor
    @NoArgsConstructor
    @Data
    public class Teacher {
          
          
        private int id;
        private String name;
    
        //一个老师拥有多个学生
        private List<Student> students;
    }
    
  3. 接口

    public interface TeacherMapper {
          
          
        //获取指定老师下的所有学生及老师的信息
        Teacher getTeacher(@Param("tid") int id);
    }
    

按结果嵌套查询

<!--命名空间绑定一个对应的Dao接口-->
<mapper namespace="com.kuang.dao.TeacherMapper">
    <select id="getTeacher" resultMap="TeacherStudent">
        select s.name sname,s.id sid,t.name tname,t.id tid
        from student s,teacher t
        where s.tid = t.id and t.id = #{tid}
    </select>

    <resultMap id="TeacherStudent" type="Teacher">
        <result property="id" column="tid"/>
        <result property="name" column="tname"/>
        <collection property="students" ofType="Student">
            <result property="id" column="sid"/>
            <result property="name" column="sname"/>
            <result property="tid" column="tid"/>
        </collection>
    </resultMap>
</mapper>

子查询方式(按照过程嵌套处理)

<!--命名空间绑定一个对应的Dao接口-->
<mapper namespace="com.kuang.dao.TeacherMapper">
    <select id="getTeacher" resultMap="TeacherStudent">
       select * from mybatis.teacher where id = #{tid}
    </select>

    <resultMap id="TeacherStudent" type="Teacher">
        <result property="id" column="tid"/>
        <result property="name" column="name"/>
        <collection property="students" ofType="Student" javaType="ArrayList" select="getStudent" column="id"/>
    </resultMap>

    <select id="getStudent" resultType="Student">
        select * from mybatis.student where tid = #{tid}
    </select>
</mapper>


13.3 小结

  1. 关联:association:多对一
  2. 集合:collection:一对多
  3. javaType:指定实体类中属性的类型
  4. ofType:用来指定映射到List或者集合中的pojo类型,泛型中的约束类型!

注意

  1. 保证SQL的可读性,尽量保证通俗易懂
  2. 注意一对多和多对一中属性名和字段的问题,如果问题不好排查,可以使用日志,建议log4j



14. 动态SQL

介绍

动态SQL就是根据不同条件生成不同的SQL语句

环境搭建

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TKXmCIE9-1619756230263)(E:\学习笔记\图片\image-20210428223158545.png)]

开启驼峰命名:

<?xml version="1.0" encoding="UTF8"?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<!--核心配置文件-->
<configuration>
    <settings>
        <setting name="mapUnderscoreToCamelCase" value="true"/>
    </settings>
</configuration>
  1. 实体类

     @AllArgsConstructor
     @NoArgsConstructor
     @Data
     public class Blog {
          
          
         private int id;
         private String title;
         private String author;
         private Date createTime;
         private int views;
     }
    
  2. 接口

    public interface BlogMapper {
          
          
         // 插入数据
       int addBlog(Blog blog);
    }
    
  3. Mapper.xml

    <!--命名空间绑定一个对应的Dao接口-->
    <mapper namespace="com.kuang.dao.BlogMapper">
        <insert id="addBlog" parameterType="blog">
            insert into mybatis.blog(id,title,author,create_time,views)
            values (#{id},#{title},#{author},#{createTime},#{views})
        </insert>
    </mapper>
    
  4. 测试

    @Test
    public void TeacherTest(){
          
          
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        BlogMapper blogMapper = sqlSession.getMapper(BlogMapper.class);
    
        Blog blog = new Blog();
        blog.setId(IdUtils.getId());
        blog.setTitle("Mybatis");
        blog.setAuthor("Q");
        blog.setCreateTime(new Date());
        blog.setViews(9999);
    
        blogMapper.addBlog(blog);
        blog.setId(IdUtils.getId());
        blog.setTitle("Spring");
        blogMapper.addBlog(blog);
    
        blog.setId(IdUtils.getId());
        blog.setTitle("Java");
        blogMapper.addBlog(blog);
    
    
        blog.setId(IdUtils.getId());
        blog.setTitle("微服务");
        blogMapper.addBlog(blog);
    
        sqlSession.close();
    
        }
    }
    


14.1 IF语句

实现

  1. 不确定前端到底是通过什么样的值进行查询。title?author?

    public interface BlogMapper {
          
          
        // 查询数据
        List<Blog> getBlogByIf(Map map);
    }
    
  2. mapper

        <!--命名空间绑定一个对应的Dao接口-->
     <mapper namespace="com.kuang.dao.BlogMapper">
         <insert id="addBlog" parameterType="blog">
             insert into mybatis.blog(id,title,author,create_time,views)
             values (#{id},#{title},#{author},#{createTime},#{views})
         </insert>
    
         <select id="getBlogByIf" parameterType="string" resultType="Blog">
             select * from mybatis.blog where 1=1
             <if test="biaoti != null">
                 and title = #{biaoti}
             </if>
         </select>
     </mapper>
    
    1. select * from mybatis.blog where 1=1是表示如果没有任何参数传进来,我们就查询所有的数据。
    2. <if test="biaoti != null">表示如果有客户想通过标题查询,即biaoti这个参数(Map的键,客户搜索的值)传进来,那么在前面的SQL语句后拼接判断条件,即select * from mybatis.blog where 1=1 and title = #{biaoti}
  3. 测试查询

    public class UserDaoTestTwo {
          
          
    
    @Test
    public void TeacherTest(){
          
          
       SqlSession sqlSession = MybatisUtils.getSqlSession();
       BlogMapper blogMapper = sqlSession.getMapper(BlogMapper.class);
       HashMap<String, Object> map = new HashMap();
       map.put("biaoti","java");	// 访问查询接口穿过去的参数是biaoti,值是java
    
       List<Blog> blogs = blogMapper.getBlogByIf(map);
       System.out.println(blogs);
    
       sqlSession.close();
         }
     }
    


14.2 常用标签

where

看一下下面的mapper中的SQL语句:

public class UserDaoTestTwo {
    
    

    @Test
    public void TeacherTest(){
    
    
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        BlogMapper blogMapper = sqlSession.getMapper(BlogMapper.class);
        HashMap<String, Object> map = new HashMap();
//        map.put("biaoti","java");
        map.put("guankan",3333);	
        List<Blog> blogs = blogMapper.getBlogByIf(map);
        System.out.println(blogs);

        sqlSession.close();
    }
}

在上面的语句中,如果我只传入guankan参数作为查询条件,而不传入biaoti,会出现什么问题呢?

public class UserDaoTestTwo {
    
    

@Test
  public void TeacherTest(){
    
    
      SqlSession sqlSession = MybatisUtils.getSqlSession();
      BlogMapper blogMapper = sqlSession.getMapper(BlogMapper.class);
      HashMap<String, Object> map = new HashMap();
//        map.put("biaoti","java");
      map.put("guankan",3333);	
      List<Blog> blogs = blogMapper.getBlogByIf(map);
      System.out.println(blogs);

      sqlSession.close();
  }
}

报错:[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-heBbEitk-1619756230264)(E:\学习笔记\图片\image-20210429131748077.png)]

针对上面这种情况,mybatis推出了<where></where>,他可以智能判断是否传入第一个查询参数,如果传入,会自动为后面的参数加入and,如果不传入,会去除第二个参数的and

<!--命名空间绑定一个对应的Dao接口-->
<mapper namespace="com.kuang.dao.BlogMapper">
    <insert id="addBlog" parameterType="blog">
        insert into mybatis.blog(id,title,author,create_time,views)
        values (#{id},#{title},#{author},#{createTime},#{views})
    </insert>

    <select id="getBlogByIf" parameterType="string" resultType="Blog">
        select * from mybatis.blog
        <where>
            <if test="biaoti != null">
                title = #{biaoti}
            </if>
            <if test="guankan!=null">
                views = #{guankan}
            </if>
        </where>
    </select>
</mapper>

choose

有时候我们不想应用到所有的条件语句,只想选择其中一项,那么用choose相当于switch,when相当于case

<mapper namespace="com.kuang.dao.BlogMapper">
    <insert id="addBlog" parameterType="blog">
        insert into mybatis.blog(id,title,author,create_time,views)
        values (#{id},#{title},#{author},#{createTime},#{views})
    </insert>

    <select id="getBlogByIf" parameterType="string" resultType="Blog">
        select * from mybatis.blog
        <where>
            <choose>
                <when test="biaoti!=null">
                    title = #{biaoti}
                </when>
                <when test="guankan!=null">
                    views = #{guankan}
                </when>
                <otherwise>
                    author = #{zuozhe}
                </otherwise>
            </choose>
        </where>
    </select>
</mapper>
public class UserDaoTestTwo {
    
    

@Test
  public void TeacherTest(){
    
    
      SqlSession sqlSession = MybatisUtils.getSqlSession();
      BlogMapper blogMapper = sqlSession.getMapper(BlogMapper.class);
      HashMap<String, Object> map = new HashMap();
      map.put("biaoti","java");
      map.put("guankan",5555);
      map.put("zuozhe","QQQ");
      List<Blog> blogs = blogMapper.getBlogByIf(map);
      System.out.println(blogs);

      sqlSession.close();
  }
}

即使我们传入个三个参数,但它只会选择一个,默认是自上而下,因此结果是根据title查询的:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rBqbpW6G-1619756230264)(E:\学习笔记\图片\image-20210429133306463.png)]

set

在更改某个数据的时候,我们通常使用 update xxx set a = #{a},b=#{b},如果第一个参数不传,逗号无法处理,在这个基础上,set可以完美解决。

<!--命名空间绑定一个对应的Dao接口-->
<mapper namespace="com.kuang.dao.BlogMapper">
    <insert id="addBlog" parameterType="blog">
        insert into mybatis.blog(id,title,author,create_time,views)
        values (#{id},#{title},#{author},#{createTime},#{views})
    </insert>

    <select id="getBlogByIf" parameterType="string" resultType="Blog">

    </select>

    <update id="updateBlog" parameterType="map">
        update mybatis.blog
        <set>
            <if test="biaoti !=null">
                title = #{biaoti},
            </if>
            <if test="guankan !=null">
                views = #{guankan}
            </if>
        </set>
        where id = #{idd}
    </update>
</mapper>

所谓的动态sql,本质还是sql语句,只是我们可以在SQL层面,增加逻辑代码!



14.3 Foreach

SQL片段

有的时候,我们有很多复用的SQL片段,这个时候我们可以进行SQL的抽离和引用:

  1. 使用SQL标签<sql></sql>抽取公共部分

    <sql id="if-biaoti-guankan">
     <if test="biaoti !=null">
         title = #{biaoti},
     </if>
     <if test="guankan !=null">
         views = #{guankan}
     </if>
    
```
  1. <include></include>标签引用

        <!--命名空间绑定一个对应的Dao接口-->
     <mapper namespace="com.kuang.dao.BlogMapper">
         <update id="updateBlog" parameterType="map">
             update mybatis.blog
             <set>
                 <include refid="if-biaoti-guankan"/>
             </set>
             where id = #{idd}
         </update>
     </mapper>
    
  2. 最好基于单边查询,且不要存在where标签

Foreach

  1. mapper

    <!--命名空间绑定一个对应的Dao接口-->
    <mapper namespace="com.kuang.dao.BlogMapper">
        <select id="getBlogByForeach" parameterType="map" resultType="blog">
            select * from mybatis.blog
            <where>
                <foreach collection="ids" item="item" open="and (" close=")" separator="or">
                    id = #{item}
                </foreach>
            </where>
        </select>
    </mapper>
    

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZexXKvlj-1619756230265)(E:\学习笔记\图片\image-20210429170508020.png)]

  2. 测试:

    public class UserDaoTestTwo {
          
          
    
        @Test
        public void TeacherTest(){
          
          
            SqlSession sqlSession = MybatisUtils.getSqlSession();
            BlogMapper blogMapper = sqlSession.getMapper(BlogMapper.class);
            HashMap<String, Object> map = new HashMap();
    
            List<Integer> list = new ArrayList();
            list.add(1);
            list.add(2);
    
            map.put("ids",list);
    
            List<Blog> blogs = blogMapper.getBlogByForeach(map);
            for (Blog blog : blogs) {
          
          
                System.out.println(blog);
            }
    
            sqlSession.close();
        }
    }
    
  3. 动态SQL就是在拼接SQL语句,我们只要保证SQL的正确性,按照SQL的格式,去排列组合就可以了

  4. 建议先把sql完整写出来然后再用动态SQL拼接。




15. 缓存

简介

  1. MyBatis包含一个非常强大的查询缓存特性,它可以非常方便地定制和配置缓存。缓存可以极大的提升查询效率。
  2. MyBatis系统中状认定义了两级缓存:一级缓存和二级缓存
    • 默认情况下,只有一级缓存开启。(SqlSession级别的缓存,也称为本地缓存,在close之前有效)
    • 二级缓存需要手动开启和配置,他是基于namespace级别的缓存。
    • 为了提高扩展性,MyBatis定义了缓存接口Cache。我们可以通过实现Cache接口来自定义二级缓存


15.1 一级缓存

简介

一级缓存也叫本地缓存:

  • 与数据库同一次会话期间查询到的数据会放在本地缓存中。
  • 以后如果需要获取相同的数据,直接从缓存中拿,没必须再去查询数据库;

测试

public class UserDaoTestTwo {
    
    

    @Test
    public void TeacherTest(){
    
    
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        BlogMapper blogMapper = sqlSession.getMapper(BlogMapper.class);
        Blog blog = blogMapper.getBlogByTitle("Java");
        Blog blog2 = blogMapper.getBlogByTitle("Java");
        System.out.println(blog2);

        sqlSession.close();
    }
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NOyfxXuR-1619756230266)(E:\学习笔记\图片\image-20210430102431637.png)]

缓存失效的原因

  1. 查询不同的东西

  2. 增删改操作,可能会改变原来的数据,所以必定会刷新缓存!(先查询1,再修改2,再次查询1,会进行三次SQL语句操作,而不是2次,缓存是针对整体数据库而言,而不是单个数据)

  3. 查询不同的Mapper.xml

  4. 手动清理缓存!

    public class UserDaoTestTwo {
          
          
    
        @Test
        public void TeacherTest(){
          
          
            SqlSession sqlSession = MybatisUtils.getSqlSession();
            BlogMapper blogMapper = sqlSession.getMapper(BlogMapper.class);
            Blog blog = blogMapper.getBlogByTitle("Java");
            sqlSession.clearCache();	//手动清理缓存
            Blog blog2 = blogMapper.getBlogByTitle("Java");
            System.out.println(blog2);
    
            sqlSession.close();
        }
    }
    

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ole35bVG-1619756230267)(E:\学习笔记\图片\image-20210430102811417.png)]



15.2 二级缓存

  1. 二级缓存也叫全局缓存,一级缓存作用域太低了,所以诞生了二级缓存。

  2. 基于namespace级别的缓存,一个名称空间,对应一个二级缓存;

  3. 工作机制:

    • 一个会话查询一条数据,这个数据就会被放在当前会话的一级缓存中;
    • 如果当前会话关闭了,这个会话对应的一级缓存就没了;但是我们想要的是,会话关闭了,一级缓存中的数据被保存到二级缓存中;
    • 新的会话查询信息,就可以从二级缓存中获取内容;
    • 不同的mapper查出的数据会放在自己对应的缓存(map)中;
  4. 开启步骤:

    • 开启全局缓存(默认是开启的,为了代码规范和可读性,我们需要显式开启让别人看到):

      [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-B0hhM9Fi-1619756230268)(E:\学习笔记\图片\image-20210430103818775.png)]

      <?xml version="1.0" encoding="UTF8"?>
      <!DOCTYPE configuration
              PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
              "http://mybatis.org/dtd/mybatis-3-config.dtd">
      <!--核心配置文件-->
      <configuration>
          <settings>
              <!--显式开启全局缓存-->
              <setting name="cacheEnabled" value="true"/>
          </settings>
      </configuration>
      
    • 在要使用二级缓存的mapper中开启:

      <mapper namespace="com.kuang.dao.BlogMapper">
          <!-- 在当前mapper.xml中使用二级缓存 -->
          <cache/>
          <select id="getBlogByTitle" resultType="Blog" parameterType="string">
              select * from mybatis.blog where title = #{biaoti}
          </select>
      </mapper>
      

      也可以自定义缓存参数:

      <mapper namespace="com.kuang.dao.BlogMapper">
          <!-- 在当前mapper.xml中使用二级缓存 -->
          <cache eviction="FIFO"
                 flushInterval="60000"
                 size="512"
                 readOnly="true"/>
          <select id="getBlogByTitle" resultType="Blog" parameterType="string">
              select * from mybatis.blog where title = #{biaoti}
          </select>
      </mapper>
      
    • 测试:

      <!--命名空间绑定一个对应的Dao接口-->
      <mapper namespace="com.kuang.dao.BlogMapper">
      
          <!-- 在当前mapper.xml中使用二级缓存 -->
          <cache/>
      
          <select id="getBlogByTitle" resultType="Blog" parameterType="string">
              select * from mybatis.blog where title = #{
              
              biaoti}
          </select>
      </mapper>
      
      public class UserDaoTestTwo {
              
              
      
          @Test
          public void TeacherTest(){
              
              
              SqlSession sqlSession = MybatisUtils.getSqlSession();
              SqlSession sqlSession2 = MybatisUtils.getSqlSession();
              BlogMapper blogMapper = sqlSession.getMapper(BlogMapper.class);
              BlogMapper blogMapper2 = sqlSession2.getMapper(BlogMapper.class);
              Blog blog = blogMapper.getBlogByTitle("Java");
              sqlSession.close();
              Blog blog2 = blogMapper2.getBlogByTitle("Java");
              System.out.println(blog2);
      
              sqlSession2.close();
          }
      }
      

      [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Q0S0acGY-1619756230269)(E:\学习笔记\图片\image-20210430105359056.png)]

      public class Blog implements Serializable {
              
              
          private String id;
          private String title;
          private String author;
          private Date createTime;
          private int views;
      }
      

      [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xTqEBcvP-1619756230269)(E:\学习笔记\图片\image-20210430105713265.png)]

小结

  1. 只要开启了二级缓存,在同一个Mapper下就有效。
  2. 所有的数据都会先放在一级缓存中,只有当会话提交,或者关闭的时候,才会提交到二级缓冲中!



15.3 自定义缓存ehcache

介绍

Ehcache是一种广泛使用的开源ava分布式缓存。主要面向通用缓存。

使用

  1. 导包

      <dependencies>
            <dependency>
                <groupId>org.mybatis.caches</groupId>
                <artifactId>mybatis-ehcache</artifactId>
                <version>1.1.0</version>
            </dependency>
        </dependencies>
    </project>
    
  2. ehcache.xml

    <?xml version="1.0" encoding="UTF-8" ?>
    <ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
            xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd"
            updateCheck="false">
    
            <diskStore path="./tmpdir/Tmp_EhCache"/>
    
            <defaultCache
                    eternal="false"
                    maxElementsInMemory="10000"
                    overflowToDisk="false"
                    diskPersistent="false"
                    timeToIdleSeconds="1800"
                    timeToLiveSeconds="259200"
                    memoryStoreEvictionPolicy="LRU"/>
    
            <cache
                    name="cloud_user"
                    eternal="false"
                    maxElementsInMemory="5000"
                    overflowToDisk="false"
                    diskPersistent="false"
                    timeToIdleSeconds="1800"
                    timeToLiveSeconds="1800"
                    memoryStoreEvictionPolicy="LRU"/>
        
    </ehcache>
    
  3. 测试,成功

    public class UserDaoTestTwo {
          
          
    
        @Test
        public void TeacherTest(){
          
          
            SqlSession sqlSession = MybatisUtils.getSqlSession();
            SqlSession sqlSession2 = MybatisUtils.getSqlSession();
            BlogMapper blogMapper = sqlSession.getMapper(BlogMapper.class);
            BlogMapper blogMapper2 = sqlSession2.getMapper(BlogMapper.class);
            Blog blog = blogMapper.getBlogByTitle("Java");
            sqlSession.close();
            Blog blog2 = blogMapper2.getBlogByTitle("Java");
            System.out.println(blog2);
    
            sqlSession2.close();
        }
    }
    

猜你喜欢

转载自blog.csdn.net/lyyrhf/article/details/116300914