栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

Mybatis基础入门

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Mybatis基础入门

Mybatis

导航
  • Mybatis
    • 一、初识mybatis
      • 1.1 概述
      • 1.2 第一个mybatis程序
      • 1.3 第一个mybatis程序的坑
    • 二、mybatis的CRUD
      • 2.1 增
      • 2.2 删
      • 2.3 改
      • 2.4 查
      • 2.6 传参方式:Map传参
      • 2.7 两种方式模糊查询
      • 2.8 注意事项
    • 三、mybatis配置
      • 3.1 properties(属性)
      • 3.2 environments(环境配置)
        • 3.2.1 transactionManager
        • 3.2.2 dataSource
      • 3.3 typeAliases(类型别名)
      • 3.4 mappers(映射器)
    • 四、生命周期和作用域
      • SqlSessionFactoryBuider:
      • SqlSessionFactory
      • SqlSession
    • 五、resultMap
    • 六、日志
      • log4j
    • 七、分页
      • 7.1 Limit分页
      • 7.2 RowBounds分页
      • 7.3 插件分页
    • 八、使用注解开
      • 8.1 注解使用方法以及示例
      • 8.2 使用注解完成CRUD
    • 九、Lombok
    • 十、复杂的查询
      • 10.1 多对一
        • 查询嵌套
        • 结果嵌套
      • 10.2 一对多
        • 结果嵌套
        • 查询嵌套
    • 十一、动态SQL
      • 11.1 if
      • 11.2 where
      • 11.3 choose、when、otherwise
      • 11.4 set
      • 11.5 trim
      • 11.6 foreach
      • 11.7 SQL代码片段
    • 十二、缓存
      • 测试环境搭建
      • 12.1 一级缓存
      • 12.2 二级缓存

我的环境:

mysql:8.0.28

jdk:1.8

mybatis:3.5.9

junit

一、初识mybatis 1.1 概述

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

1.2 第一个mybatis程序

总的来说,用mybatis就可以不用写繁琐的JDBC代码。

来写一个mybatis程序就明白了

  1. 准备数据库:

    DROp DATABASE IF EXISTS `mybatis`;
    CREATE DATABASE `mybatis`;
    
    CREATE TABLE `user` (
      `id` int unsigned NOT NULL AUTO_INCREMENT,
      `name` varchar(20) NOT NULL,
      `pwd` varchar(20) NOT NULL,
      PRIMARY KEY (`id`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8
    
    insert into `user`(`name`,`pwd`) values ('root','123456'),('admin','123456');
    
  2. 导入依赖

    
        junit
        junit
        4.13.2
        test
    
    
        org.mybatis
        mybatis
        3.5.9
    
    
        mysql
        mysql-connector-java
        8.0.28
    
    
  3. 编写实体类

    package vip.yangsf.pojo;
    
    public class User {
        private int id;
        private String name;
        private String pwd;
    
        public User() {
        }
    
        public User(int id, String name, String pwd) {
            this.id = id;
            this.name = name;
            this.pwd = pwd;
        }
    
        public int getId() {
            return id;
        }
    
        public void setId(int id) {
            this.id = id;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public String getPwd() {
            return pwd;
        }
    
        public void setPwd(String pwd) {
            this.pwd = pwd;
        }
    
        @Override
        public String toString() {
            return "User{" +
                    "id=" + id +
                    ", name='" + name + ''' +
                    ", pwd='" + pwd + ''' +
                    '}';
        }
    }
    
  4. 编写持久层接口

    package vip.yangsf.dao;
    
    import vip.yangsf.pojo.User;
    
    import java.util.List;
    
    public interface UserMapper {
        List getUserList();
    }
    
    
  5. 配置映射文件 UserMapper.xml

    
    
    
    
        
        
            select * from mybatis.user
        
        
        
            
            insert into mybatis.user (id, name, pwd) values (#{id}, #{name}, #{pwd});
        
    
    
  6. 测试:

    class UserDaoTest {
        @Test
        public void test() {
            SqlSession session = FirstUtil.getSqlSession();
            UserMapper mapper = session.getMapper(UserMapper.class);
    		mapper.addUser(new User(3,"yaf","123456"));
            // 提交事务,不然添加无效
            session.commit();
            // 释放资源
            session.close();
        }
    }
    
2.2 删
  1. 和增一样的道理,先在UserMapper接口中添加删除方法:

    package vip.yangsf.dao;
    
    import vip.yangsf.pojo.User;
    
    import java.util.List;
    
    public interface UserMapper {
        // 查询全部User
        List getUserList();
        // 添加User
        int addUser(User user);
        // 通过id删除User
        int deleteUser(int id);
    }
    
  2. 修改UserMapper.xml:

    
    
    
    
        
        
            select * from mybatis.user
        
        
        
            
            insert into mybatis.user (id, name, pwd) values (#{id}, #{name}, #{pwd});
        
        
        
            delete from mybatis.user where id = #{id};
        
        
        
            update mybatis.user set name = #{name}, pwd = #{pwd} where id=#{id};
        
    
    
  3. 测试:

    class UserDaoTest {
        @Test
        public void test() {
            SqlSession session = FirstUtil.getSqlSession();
            UserMapper mapper = session.getMapper(UserMapper.class);
    		mapper.updateUser(new User(2, "ysf", "123456"));
            // 提交事务,不然添加无效
            session.commit();
            // 释放资源
            session.close();
        }
    }
    
2.4 查

查和增删改不一样的是,查不需要提交事务,因为没有修改表。

其余都是套路:

  1. 添加查询方法

    package vip.yangsf.dao;
    
    import vip.yangsf.pojo.User;
    
    import java.util.List;
    
    public interface UserMapper {
        // 查询全部User
        List getUserList();
        // 添加User
        int addUser(User user);
        // 通过id删除User
        int deleteUser(int id);
        // 修改User
        int updateUser(User user);
        // 通过id查询某条记录
        User getUserById(int id);
    }
    
  2. 添加映射

    
    
    
    
        
        
            select *
            from mybatis.user where id = #{id};
        
    
    
  3. 测试

    class UserDaoTest {
        @Test
        public void test() {
            SqlSession session = FirstUtil.getSqlSession();
            UserMapper mapper = session.getMapper(UserMapper.class);
            User user = mapper.getUserById(1);
            System.out.println(user);
        }
    }
    
2.6 传参方式:Map传参

假如说要插入一个User,我们刚刚是通过UserMapper.xml来映射实体类,比如:

insert into mybatis.user (id, name, pwd) values (#{id}, #{name}, #{pwd});

我们就需要传个User进去,这些变量名还得与User中的成员变量名,数据库中的字段名一样。

我们可以传Map进去,看代码就明白了:

  1. UserMapper接口中添加方法:

    package vip.yangsf.dao;
    
    import vip.yangsf.pojo.User;
    
    import java.util.List;
    
    public interface UserMapper {
        // 查询全部User
        List getUserList();
        // 添加User
        int addUser(User user);
        // 通过id删除User
        int deleteUser(int id);
        // 修改User
        int updateUser(User user);
        // 通过id查询某条记录
        User getUserById(int id);
        // Map传参
        int addUser1(Map map);
    }
    
  2. 配置文件中添加一个映射:

    
    
        
        insert into mybatis.user (name, pwd) values (#{username}, #{password});
    
    
  3. 测试:

    class MapTest {
        @Test
        public void test() {
            SqlSession sqlSession = FirstUtil.getSqlSession();
            UserMapper mapper = sqlSession.getMapper(UserMapper.class);
            Map map = new HashMap<>();
            // key名要和配置文件中对应
            map.put("username", "map");
            map.put("password", "123456");
            // 传map过去就行
            mapper.addUser1(map);
            // 提交事务
            sqlSession.commit();
            // 释放资源
            sqlSession.close();
        }
    }
    
2.7 两种方式模糊查询

本质上都一样,一种是通过java传参时手动输入通配符,一种是把通配符写死了。

先添加两条记录待会儿查找:

  1. 接口中添加方法:

    List getUserList1(String value);
    
    List getUserList2(String value);
    
  2. 添加映射语句:

    
        select * from mybatis.user where name like "%"#{value}"%";
    
    
  3. 测试

    // 查询所有name中带李的记录
    class LikeUserTest {
        @Test
        public void test() {
            SqlSession sqlSession = FirstUtil.getSqlSession();
            UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    
            System.out.println("方式一:");
            List list = mapper.getUserList1("%李%");
            list.forEach(System.out::println);
    
            System.out.println("方式二:");
            List list1 = mapper.getUserList2("李");
            list1.forEach(System.out::println);
    
            sqlSession.close();
        }
    }
    

    输出:

2.8 注意事项

增删改一定要记得提交事务,不然改了白改。

三、mybatis配置

在前面的学习中,我们学会了一点配置,比如:




    
        
            
            
                
                
                
                
            
        
    

    
        
    

接下里我们稍微深入一点,学习这里面的一些标签。

这段xml文档是从官网扒下来的,长这样子:




  
    
      
      
        
        
        
        
      
    
  
  
    
  

可以发现,里面有几个像是变量的东西,比如:driver、url这些。可以猜想,这些值是可以通过外界传进来的。

3.1 properties(属性)

和properties有关系的是这几行代码。

 
 
 
 

可以这样:




    
    
        
        
        
        
    
    
    
        
            
            
                
                
                
                
            
        
    
    
        
    

还可以引入外部文件:

db.properties:

driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/mybatis?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8
username=root
pwd=123456

mybatis-config.xml:





    

    
        
            
            
                
                
                
                
            
        
    
    
        
    

3.2 environments(环境配置)

通过标签名environments结尾的s,就可以猜想:是不是可以有多个环境?

事实上也是肯定的。

我们可以在environments中添加多个environment,但是每个SqlSessionFactory只能对应一个environment,我们在选择environment时可以通过environments的default属性的值来选择对应的environment,或是通过创建SqlSessionFactory时填写build方法的参数来选择。

3.2.1 transactionManager

事务管理器

在 MyBatis 中有两种类型的事务管理器(也就是 type=“[JDBC|MANAGED]”):

MANAGEND不太懂,一般只用JDBC,后面的学习了Spring就不需要配置事务管理器了。

3.2.2 dataSource

数据源,默认是POOLED,还有UNPOOLED UNPOOLED不能使用池化技术,POOLED就像是数据库连接池,可以为它设置一些属性如poolMaximumActiveConnections – 在任意时间可存在的活动(正在使用)连接数量,默认值:10,等等一些配置数据库连接池的一些属性。

3.3 typeAliases(类型别名)

可以为Java类型设置一个缩写名字,仅用于xml配置文件中,比如之前是这样写的:


    select * from mybatis.user;

当实体类较多时,可以直接给定一个包路径,mybatis会自动扫描这个包下面的所有JavaBean并给他们取个别名,别名就是类名:


    

如果有特别要求,可以在类上面加上@Alias("")来取类名:

@Alias("user")
public class User {
	……
}

mybatis为一些常见的 Java 类型内建的类型别名。它们都是不区分大小写的。可以去https://mybatis.net.cn/configuration.html#typeAliases查看。

3.4 mappers(映射器)

我们配置mybatis是为了执行sql,让mybatis找到要映射的文件,有四种方式:

  1. 使用相对路径

    
        
    
    
  2. 使用url

    
        
    
    
  3. 使用对应的接口名

    
        
    
    
  4. 使用包名,会将包中所有mapper注册

    
        
    
    

注意事项:

映射文件尽量与映射器接口名一样,不然后两种无法识别!

四、生命周期和作用域

错误的控制生命周期和作用域会导致并发问题。

SqlSessionFactoryBuider:

这个类可以被实例化、使用和丢弃,一旦创建了 SqlSessionFactory,就不再需要它了。 因此 SqlSessionFactoryBuilder 实例的最佳作用域是方法作用域(也就是局部方法变量)。 你可以重用 SqlSessionFactoryBuilder 来创建多个 SqlSessionFactory 实例,但最好还是不要一直保留着它,以保证所有的 XML 解析资源可以被释放给更重要的事情。

SqlSessionFactory

SqlSessionFactory 一旦被创建就应该在应用的运行期间一直存在,没有任何理由丢弃它或重新创建另一个实例。 使用 SqlSessionFactory 的最佳实践是在应用运行期间不要重复创建多次,多次重建 SqlSessionFactory 被视为一种代码“坏习惯”。因此 SqlSessionFactory 的最佳作用域是应用作用域。 有很多方法可以做到,最简单的就是使用单例模式或者静态单例模式。

SqlSession

每个线程都应该有它自己的 SqlSession 实例。SqlSession 的实例不是线程安全的,因此是不能被共享的,所以它的最佳的作用域是请求或方法作用域。 绝对不能将 SqlSession 实例的引用放在一个类的静态域,甚至一个类的实例变量也不行。 也绝不能将 SqlSession 实例的引用放在任何类型的托管作用域中,比如 Servlet 框架中的 HttpSession。 如果你现在正在使用一种 Web 框架,考虑将 SqlSession 放在一个和 HTTP 请求相似的作用域中。 换句话说,每次收到 HTTP 请求,就可以打开一个 SqlSession,返回一个响应后,就关闭它。 这个关闭操作很重要,为了确保每次都能执行关闭操作,你应该把这个关闭操作放到 finally 块中。 下面的示例就是一个确保 SqlSession 关闭的标准模式:

try (SqlSession session = sqlSessionFactory.openSession()) {
  // 你的应用逻辑代码
}
五、resultMap

现在有这么一张表:

CREATE TABLE `user_test` (
    `id` int(11) NOT NULL AUTO_INCREMENT,
    `name` varchar(20) NOT NULL,
    `password` varchar(20) NOT NULL,
    PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 

insert into `user`(`name`,`password`) values ('root','123456'),('admin','123456');

有这么一个JavaBean:

package vip.yangsf.pojo;

public class User1 {
        private int id;
        private String name;
        private String pwd;

        public User1() {
        }

        public User1(int id, String name, String pwd) {
            this.id = id;
            this.name = name;
            this.pwd = pwd;
        }

        public int getId() {
            return id;
        }

        public void setId(int id) {
            this.id = id;
        }

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public String getPwd() {
            return pwd;
        }

        public void setPwd(String pwd) {
            this.pwd = pwd;
        }

        @Override
        public String toString() {
            return "User{" +
                    "id=" + id +
                    ", name='" + name + ''' +
                    ", pwd='" + pwd + ''' +
                    '}';
        }
}

然后我们查询所有的User1:

pwd查不到,是因为数据库中字段为password,JavaBean中是pwd,我们可以用结果集映射来解决这种数据库字段与JavaBean属性名不一致问题。

修改映射器文件:




    
        
    
    
        select * from mybatis.user limit #{startIndex}, #{pageSize};
    

  • 测试

    @Test
    public void test() {
        SqlSession sqlSession = FirstUtil.getSqlSession();
        LimitUser mapper = sqlSession.getMapper(LimitUser.class);
    
        Map map = new HashMap<>();
        map.put("startIndex", 3);
        map.put("pageSize", 2);
        List user = mapper.getLimitUser(map);
        user.forEach(System.out::println);
    }
    
  • 7.2 RowBounds分页

    java提供了一个类来帮助我们分页,就不用写分页的sql语句了(但查询还是要写)

    1. 写接口

      package vip.yangsf.dao;
      
      import vip.yangsf.pojo.User;
      
      import java.util.List;
      import java.util.Map;
      
      public interface LimitUser {
          List getLimitUser(Map map);
          List getLimitUserByRowBounds();
      }
      
    2. 写sql

      
      
      
          
              select * from mybatis.user
          
      
      
    3. 测试

      @Test
      public void rowBounds() {
          SqlSession sqlSession = FirstUtil.getSqlSession();
      
          List users = sqlSession.selectList("vip.yangsf.dao.LimitUser.getLimitUserByRowBounds", null, new RowBounds(3, 2));
      
          users.forEach(System.out::println);
      }
      

      效果和上面是一样的。

    7.3 插件分页

    还可以用mybatis插件进行分页,比如PageHelper

    八、使用注解开 8.1 注解使用方法以及示例

    步骤都是一样的,sql不用再写在xml里面

    1. 写接口

      package vip.yangsf.mapper;
      
      import vip.yangsf.pojo.User;
      
      import java.util.List;
      
      public interface UserMapper {
          List getUserList();
      
      }
      
    2. 写sql

      直接添加注解就完事:

      package vip.yangsf.mapper;
      
      import org.apache.ibatis.annotations.Select;
      import vip.yangsf.pojo.User;
      
      import java.util.List;
      
      public interface UserMapper {
          @Select("select * from mybatis.user")
          List getUserList();
      }
      
    3. 测试

      package vip.yangsf;
      
      import org.apache.ibatis.session.SqlSession;
      import org.junit.Test;
      import vip.yangsf.mapper.UserMapper;
      import vip.yangsf.pojo.User;
      import vip.yangsf.utils.MyUtils;
      
      import java.util.List;
      
      public class Test01 {
          @Test
          public void test() {
              SqlSession sqlSession = MyUtils.getSqlSession();
              UserMapper mapper = sqlSession.getMapper(UserMapper.class);
              List userList = mapper.getUserList();
              userList.forEach(System.out::println);
          }
      }
      
    8.2 使用注解完成CRUD

    都是三步走!

    1. 写接口

      package vip.yangsf.mapper;
      
      import org.apache.ibatis.annotations.*;
      import vip.yangsf.pojo.User;
      
      import java.util.List;
      
      public interface UserMapper {
          // 查询
          List getUserList();
      	// 根据id查询
          User getUserById(int id);
      	// 增
          int addUser(User user);
      	// 增,利用id自增,只需要填写姓名和密码
          int addUser1(String name, String password);
      	// 改
          int updateUser(User user);
      	// 删
          int deleteUser(int id);
      }
      
    2. 写sql

      package vip.yangsf.mapper;
      
      import org.apache.ibatis.annotations.*;
      import vip.yangsf.pojo.User;
      
      import java.util.List;
      
      public interface UserMapper {
          // 查询
          @Select("select * from mybatis.user")
          List getUserList();
      	// 根据id查询
          @Select("select * from mybatis.user where id = #{id}")
          User getUserById(int id);
      	// 增
          @Insert("insert into mybatis.user(id, name, pwd) values(#{id}, #{name}, #{pwd})")
          int addUser(User user);
      	// 增,利用id自增,只需要填写姓名和密码
          @Insert("insert into mybatis.user(name, pwd) values(#{name}, #{password})")
          int addUser1(@Param("name") String name, @Param("password") String password);
      	// 改
          @Update("update mybatis.user set name = #{name}, pwd = #{pwd} where id = #{id}")
          int updateUser(User user);
      	// 删
          @Delete("delete from mybatis.user where id = #{id}")
          int deleteUser(int id);
      }
      
    3. 写测试

      package vip.yangsf;
      
      import org.apache.ibatis.session.SqlSession;
      import org.junit.Test;
      import vip.yangsf.mapper.UserMapper;
      import vip.yangsf.pojo.User;
      import vip.yangsf.utils.MyUtils;
      
      import java.util.List;
      
      public class Test01 {
          @Test
          public void test() {
              SqlSession sqlSession = MyUtils.getSqlSession();
              UserMapper mapper = sqlSession.getMapper(UserMapper.class);
      		// 查询全部
              List userList = mapper.getUserList();
              userList.forEach(System.out::println);
      		// 根据id查询
              User user = mapper.getUserById(3);
              System.out.println(user);
      		// 增
              mapper.addUser(new User(6, "ysf", "123456"));
      		// 增
              mapper.addUser1("ysf", "123456");
      		// 改
              mapper.updateUser(new User(6, "yangsf", "123456"));
      		// 删
              mapper.deleteUser(6);
          }
      }
      
    九、Lombok

    Lombok项目是一个Java库,它会自动插入编辑器和构建工具中,Lombok提供了一组有用的注释,用来消除Java类中的大量样板代码。仅五个字符(@Data)就可以替换数百行代码从而产生干净,简洁且易于维护的Java类。

    说起来复杂,但其实做起来很简单,导完包之后,就可以用它的各种注解来简化我们的代码了。

    1. 导包

      
          org.projectlombok
          lombok
          1.18.24
      
      
    2. 在实体类上加注解,例如@Data

      package vip.yangsf.pojo;
      
      import lombok.Data;
      
      @Data
      public class User {
          private int id;
          private String name;
          private String pwd;
      }
      
    3. 看结构

    发现多了很多方法 get/set, toString…………方法都有了,但是有参构造不见了,可以添加@AllArgsConstructor,添加这个注解后无参构造不见了,我们再加上@NoArgsConstructor。非常完美。

    当然,不只有@Data注解,还有很多注解

    例如

    @Setter :注解在类或字段,注解在类时为所有字段生成setter方法,注解在字段上时只为该字段生成setter方法。

    @Getter :使用方法同上,区别在于生成的是getter方法。

    @ToString :注解在类,添加toString方法。

    @EqualsAndHashCode: 注解在类,生成hashCode和equals方法。

    @NoArgsConstructor: 注解在类,生成无参的构造方法。

    @RequiredArgsConstructor: 注解在类,为类中需要特殊处理的字段生成构造方法,比如final和被@NonNull注解的字段。

    @AllArgsConstructor: 注解在类,生成包含类中所有字段的构造方法。

    @Data: 注解在类,生成setter/getter、equals、canEqual、hashCode、toString方法,如为final属性,则不会为该属性生成setter方法。

    @Slf4j: 注解在类,生成log变量,严格意义来说是常量。

    ----百度百科

    十、复杂的查询

    真tm打脑壳。

    10.1 多对一

    先来看看多对一的情况。

    现在有两张表:

    CREATE TABLE `teacher` (
      `id` INT(10) NOT NULL,
      `name` VARCHAr(30) DEFAULT NULL,
      PRIMARY KEY (`id`)
    ) ENGINE=INNODB DEFAULT CHARSET=utf8;
    
    INSERT 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`),
    ) ENGINE=INNODB DEFAULT CHARSET=utf8;
    
    INSERT 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');
    

    我们需要的是,查询所有学生以及对应的老师信息。

    sql这样写:

    select s.id `学生id`, s.name `学生姓名`, t.id `老师id`, t.name `老师姓名` from student s inner join teacher t on s.tid = t.id
    

    那再mybatis中应该怎么写呢?有两种方法,先看第一种

    查询嵌套

    这就复杂起来了,这不是sql简单复制就可以实现的,还是按照步骤来。

    1. 先写实体类

      Student的实体类,tid代表的是老师的信息,所以是Teacher类型,待会儿去老师表里查出来:

      package vip.yangsf.pojo;
      
      import lombok.Data;
      
      @Data
      public class Student {
          private int id;
          private String name;
          
          private Teacher tid;
      }
      

      Teacher的实体类:

      package vip.yangsf.pojo;
      
      
      import lombok.Data;
      
      @Data
      public class Teacher {
          private int id;
          private String name;
      }
      
    2. 写接口:

      package vip.yangsf.mapper;
      
      import vip.yangsf.pojo.Student;
      
      import java.util.List;
      
      public interface StudentMapper {
          List getList();
      }
      
    3. 写sql,这里就比较复杂了,我们先看一下答案:

      
      
      
          
              select * from mybatis.student
          
          
          
              
          
      
          
              select s.*, t.id as ttid, t.name as tname from mybatis.student s, mybatis.teacher t where s.tid = t.id;
          
          
          
              
              
              
                  
                  
              
          
          
      
      

      不用嵌套sql,我们直接将查出来的字段名和java类的成员变量做映射。

      测试还是一样的代码:

      package vip.yangsf;
      
      import org.apache.ibatis.session.SqlSession;
      import vip.yangsf.mapper.StudentMapper;
      import vip.yangsf.pojo.Student;
      import vip.yangsf.utils.MyUtils;
      
      import java.util.List;
      
      public class test02 {	
          public static void main(String[] args) {
              SqlSession sqlSession = MyUtils.getSqlSession();
              StudentMapper studentMapper = sqlSession.getMapper(StudentMapper.class);
      
              List list1 = studentMapper.getList();
              list1.forEach(System.out::println);
          }
      }
      

      结果:

      10.2 一对多

      一个老师对应多个学生,同样的也有两种方法,步骤大同小异。

      结果嵌套
      1. 实体类

        teacher:

        import lombok.Data;
        
        import java.util.List;
        
        @Data
        public class Teacher {
            private int id;
            private String name;
        
            // 一对多
            private List students;
        }
        

        student:

        import lombok.Data;
        
        @Data
        public class Student {
            private int id;
            private String name;
            private int tid;
        }
        
      2. 写接口

        TeacherMapper

        import vip.yangsf.pojo.Teacher;
        
        import java.util.List;
        
        public interface TeacherMapper {
            List getList();
        }
        
      3. 写sql

        TeacherMapper.xml:

        
        
        
            
            
            select * from mybatis.teacher
        
        
        
            
            
        
        
        
            select * from mybatis.blog
            
                where title like "%"#{title}"%"
            
            
                and author like "%"#{author}"%"
            
        
        

        如果传入的title不为空,那么就拼接这条sql语句,如果author不为空,那么拼接sql语句。

        测试:

        import org.apache.ibatis.session.SqlSession;
        import org.junit.Test;
        import vip.yangsf.mapper.BlogMapper;
        import vip.yangsf.utils.MyUtils;
        
        import java.util.HashMap;
        import java.util.Map;
        
        public class Test01 {
            @Test
            public void test01() {
                SqlSession sqlSession = MyUtils.getSqlSession();
                BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
        
                Map map = new HashMap<>();
                map.put("title", "3");
                map.put("author", "0");
                System.out.println(mapper.searchBlogs(map));
                
                sqlSession.close();
            }
        }
        

        传入一个map。

        11.2 where

        上面程序有一个问题:如果不传入title,只传入author,那么sql就会错误。

        如和选择一个语句执行呢?

        当然,可以这样

        
            select * from mybatis.blog
            
                
                    and title like "%"#{title}"%"
                
                
                    and author like "%"#{author}"%"
                
            
        
        

        测试:

        import java.util.HashMap;
        import java.util.Map;
        
        public class Test01 {
            @Test
            public void test01() {
                SqlSession sqlSession = MyUtils.getSqlSession();
                BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
        
                Map map = new HashMap<>();
                map.put("author", "0");
                
                System.out.println(mapper.searchBlogs(map));
                sqlSession.close();
            }
        }
        

        当然也是成功的。

        执行过程:

        where标签下的,会选择性拼接,拼接很灵性,如果第一个条件开头是and或者or,那么where就会将and或者or替换掉,后面满足条件就拼接就完事了。

        11.3 choose、when、otherwise

        用了where标签过后,sql已经很动态了。

        现在还有一种情况:有时候只需要满足一个条件,如果一个条件都不满足,那么就得返回全部数据,这不合理。

        于是,choose、when、otherwise来了。类似于switch-case

        
          SELECT *
          FROM POST P
          WHERe ID in
          
                #{item}
          
        
        

        先拼接open字符串,然后将list集合里面的所有item拼接进来,通过分隔符逗号分隔,然后结尾为close字符串也就是扩号。index就是每个item的下标。

        实例:

        写个接口方法,准备查询id 为 1~3 的Blog。记得先去把表中找三个记录,把它们的uuid改成1,2,3中的一个。

        List getSomeBlogs(Map map);
        

        不知道传什么,就传map,万能。

        sql:

        
                select * from mybatis.user
            
        
            
        
            
                insert into mybatis.user(name, pwd) VALUES (#{name}, #{pwd})
            
        
        
        12.1 一级缓存

        一级缓存又被称为 SqlSession 级别的缓存。

        同一个 SqlSession 对象, 在参数和 SQL 完全一样的情况先, 只执行一次 SQL 语句(如果缓存没有过期)。

        一级缓存是默认开启的且关不掉。

        看代码更容易理解:

            @Test
            public void test() {
                SqlSession sqlSession = MyUtil.getSqlSession();
                UserMapper1 mapper = sqlSession.getMapper(UserMapper1.class);
        
                User user1 = mapper.queryUserById(1);
                System.out.println(user1);
                
                System.out.println("==================================");
                
                User user2 = mapper.queryUserById(1);
                System.out.println(user2);
        
                System.out.println(user1 == user2);
                sqlSession.close();
            }
        

        查看日志会发现:

        只有一次会话,而且user1和user2是同一个东西(输出了true)。

        除了查询以外的其他操作会刷新缓存:

            @Test
            public void test() {
                SqlSession sqlSession = MyUtil.getSqlSession();
                UserMapper1 mapper = sqlSession.getMapper(UserMapper1.class);
        
                User user1 = mapper.queryUserById(1);
                System.out.println(user1);
                
                System.out.println("==================================");
                
                Map map = new HashMap();
                map.put("name", "qqq");
                map.put("pwd", "877602782");
                mapper.addUser(map);
                
                System.out.println("==================================");
        
                User user2 = mapper.queryUserById(1);
                System.out.println(user2);
        
                System.out.println(user1 == user2);
                sqlSession.close();
            }
        
        

        发生了三次会话并且 user1和user2不是同一个东西(输出了false):

        也可以手动清除缓存:

            @Test
            public void test() {
                SqlSession sqlSession = MyUtil.getSqlSession();
                UserMapper1 mapper = sqlSession.getMapper(UserMapper1.class);
        
                User user1 = mapper.queryUserById(1);
                System.out.println(user1);
                
                System.out.println("==================================");
                // 清除缓存
                sqlSession.clearCache();
                
                User user2 = mapper.queryUserById(1);
                System.out.println(user2);
        
                System.out.println(user1 == user2);
                sqlSession.close();
            }
        

        两次会话,并且两个user不是同一个(输出了false):

        12.2 二级缓存

        二级缓存存在于 SqlSessionFactory 生命周期中。二级缓存是mapper级别的缓存,多个SqlSession去操作同一个Mapper的sql语句,多个SqlSession可以共用二级缓存,二级缓存是跨SqlSession的。UserMapper有一个二级缓存区域(按namespace分),其它mapper也有自己的二级缓存区域(按namespace分)。每一个namespace的mapper都有一个二级缓存区域,两个mapper的namespace如果相同,这两个mapper执行sql查询到数据将存在相同的二级缓存区域中。

        看代码更好理解:

            @Test
            public void test02() {
                SqlSession sqlSession = MyUtil.getSqlSession();
                UserMapper1 mapper = sqlSession.getMapper(UserMapper1.class);
        
                SqlSession sqlSession2 = MyUtil.getSqlSession();
                UserMapper1 mapper2 = sqlSession2.getMapper(UserMapper1.class);
        
                User user1 = mapper.queryUserById(1);
                System.out.println(user1);
        
                sqlSession.close();
                System.out.println("==================================");
        
                User user2 = mapper2.queryUserById(1);
                System.out.println(user2);
        
                System.out.println(user1 == user2);
        
                sqlSession2.close();
            }
        }
        

        如果不开二级缓存,结果就是这样:

        开启二级缓存:

        1. mybatis-config.xml里面添加配置:
        
        

        默认就是开启的。

        1. 在要使用二级缓存的Mapper中开启:

          
          
        2. 测试:

          同样的代码,看结果:

          第二次查询直接就在缓存中取了,并且两个user相同(输出true)

        当然 也可以不用设置参数:

        上面的第二步变为:

        
        

        并且要给实体类实现序列化接口

        @Data
        @AllArgsConstructor
        @NoArgsConstructor
        public class User implements Serializable {
            private int id;
            private String name;
            private String pwd;
        }
        

        测试:

        第二次查询是从缓存中查,但是输出为false

        只需要将缓存设置为只读:

        
        

        完美。

    转载请注明:文章转载自 www.mshxw.com
    本文地址:https://www.mshxw.com/it/868027.html
    我们一直用心在做
    关于我们 文章归档 网站地图 联系我们

    版权所有 (c)2021-2022 MSHXW.COM

    ICP备案号:晋ICP备2021003244-6号