Mybatis学习及搭建

Mybatis

前言:

经过几天的学习mybatis,在内容方面还是容易忘记一些的,故边学习边总结记录,方便以后查看分享。

一、简介

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

简单来说:就是帮助我们处理jdbc,即优化数据库连接的简化

二、搭建第一个简单的Mybatis

思路:搭建环境–>导入Mybatis–>编写代码–>测试!

步骤如下

  1. 搭建数据库
  2. 创建一个普通的maven项目

    这里注意一个事项如图:

    https://gitee.com/he-ze__-an/markdown_image/raw/master/mybatis_images/image-20221004150712239.png

  3. 建立工程(父子),导入mysql驱动,mybatis和junit相应的依赖
    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
    
    <!--    父工程-->
        <groupId>com.heze</groupId>
        <artifactId>Mybatis-Study</artifactId>
        <packaging>pom</packaging>
        <version>1.0-SNAPSHOT</version>
    
        <modules>
            <module>mybatis-01</module>
            <module>mybatis-02</module>
            <module>mybatis-03</module>
            <module>mybatis-04</module>
        </modules>
    
        <dependencies>
    <!--        mysql驱动-->
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
                <version>8.0.30</version>
            </dependency>
    <!--        mybatis-->
            <!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
            <dependency>
                <groupId>org.mybatis</groupId>
                <artifactId>mybatis</artifactId>
                <version>3.5.11</version>
            </dependency>
    
            <!--        junit-->
            <dependency>
                <groupId>junit</groupId>
                <artifactId>junit</artifactId>
                <version>4.12</version>
    <!--            <scope>test</scope>-->
                <scope>*</scope>
            </dependency>
    
    <!--        log4j-->
            <dependency>
                <groupId>log4j</groupId>
                <artifactId>log4j</artifactId>
                <version>1.2.12</version>
            </dependency>
    
    <!--        lombok-->
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
                <version>1.18.4</version>
                <scope>provided</scope>
            </dependency>
    
    
        </dependencies>
    
    
    <!--    在build中配置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>
    
    </project>
    

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zBfA0tzk-1665152069751)(C:\Users\何泽\AppData\Roaming\Typora\typora-user-images\image-20221004151038036.png)]

  4. 配置mybatis核心xml文件,建议建立在resource文件下面

    db.properties

    driver=com.mysql.cj.jdbc.Driver
    url=jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
    username = root
    password=123456
    

    mybatis-config.xml

    <?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>
    
    <!--    引入外部配置文件-->
        <properties resource="db.properties"></properties>
    
    <!--    设置日志-->
        <settings>
    <!--        <setting name="logImpl" value="STDOUT_LOGGING"/>-->
            <setting name="logImpl" value="LOG4J"/>
        </settings>
    
    <!--    可以给实体类起别名-->
        <typeAliases>
    <!--        <typeAlias type="com.heze.pojo.User" alias="User"/>-->
            <package name="com.heze.pojo"/>
        </typeAliases>
    
        <environments default="development">
            <environment id="development">
                <transactionManager type="JDBC"/>
                <dataSource type="POOLED">
                    <property name="driver" value="${driver}"/>
                    <property name="url" value="${url}"/>
                    <property name="username" value="${username}"/>
                    <property name="password" value="${password}"/>
                </dataSource>
            </environment>
        </environments>
    
        <mappers>
    <!--        <mapper resource="com/heze/dao/UserMapper.xml"/>-->
    <!--        绑定接口-->
            <mapper class="com.heze.dao.UserMapper"/>
        </mappers>
    </configuration>
    

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jrQMWsRs-1665152069751)(C:\Users\何泽\AppData\Roaming\Typora\typora-user-images\image-20221004151728070.png)]

  5. 从xml中构建SqlSessionFactory,网上可以找模板

    MybatisUtils.java

    package com.heze.utils;
    
    import org.apache.ibatis.io.Resources;
    import org.apache.ibatis.session.SqlSession;
    import org.apache.ibatis.session.SqlSessionFactory;
    import org.apache.ibatis.session.SqlSessionFactoryBuilder;
    
    import javax.annotation.Resource;
    import java.io.IOException;
    import java.io.InputStream;
    
    /**
     * @author 何泽
     * @version 1.0
     */
    
    //sqlSessionFactory --> sqlSession
    public class MybatisUtils {
    
        private static SqlSessionFactory sqlSessionFactory;
    
        //静态代码块,初始即编译
        static {
                try {
                    //获取sqlSessionFactory,第一步:获取sqlSessionFactory对象
                    String resource = "mybatis-config.xml";
                    InputStream inputStream = Resources.getResourceAsStream(resource);
                    sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
                } catch (IOException e) {
                    e.printStackTrace();
                }
        }
    
        //既然有了SqlSessionFactory,顾名思义,我们就可以从中获取SqlSession的实例了
        //SqlSession 完全包含了面向数据库执行的sql命令所需要的所有方法
        public static SqlSession getSqlSession(){
            return sqlSessionFactory.openSession(true);
        }
    
    
    }
    
    

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hnQOQYAE-1665152069752)(C:\Users\何泽\AppData\Roaming\Typora\typora-user-images\image-20221004152112312.png)]

  6. 编写实体类,alt+Ins快捷键编写构造等方法,后面使用lombok注解也可

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lpUrdiLz-1665152069752)(C:\Users\何泽\AppData\Roaming\Typora\typora-user-images\image-20221004152312043.png)]

  7. 编写Dao层接口,利用xml配置文件的方式代替原先的接口实现类Impl,实现简化

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ytsATMfF-1665152069753)(C:\Users\何泽\AppData\Roaming\Typora\typora-user-images\image-20221004152837504.png)]

  8. Test测试,利用@Test注解测试,在test包下创建一个类,用来测试我们写的代码

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IMPyMfD2-1665152069753)(C:\Users\何泽\AppData\Roaming\Typora\typora-user-images\image-20221004153208772.png)]

    在最终执行结果时,我出现如下错误:

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Etrj5nKW-1665152069754)(C:\Users\何泽\AppData\Roaming\Typora\typora-user-images\image-20221004154126830.png)]

    在我的mybatis-config.xml文件中,少了如下代码

    <?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">
    

    - [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Hge90nxW-1665152069754)(C:\Users\何泽\AppData\Roaming\Typora\typora-user-images\image-20221004153922628.png)]

    应写成:

    <mappers>
            <mapper resource="com/heze/dao/UserMapper.xml"/>
        </mappers>
    
    • 路径没问题,但是xml配置文件找不到

      [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cLPdEVJB-1665152069754)(C:\Users\何泽\AppData\Roaming\Typora\typora-user-images\image-20221004154527286.png)]

      可以在所有pom.xml文件下添加如下代码

      <!--    在build中配置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-cTnDE9XO-1665152069755)(C:\Users\何泽\AppData\Roaming\Typora\typora-user-images\image-20221004154824430.png)]

      到这里,我的第一个Mybatis程序就完成了,主要还是了解过程以及思想,还有就是解决问题的耐心和能力

三、 CRUD(核心)
  1. namespace

    namespace中的包名要和Dao/mapper接口的包名一致!

  2. select、insert、update、delete
    • resultType:Sql语句执行的返回值!
    • resultType:Sql语句执行的返回值!
    • parameterType:参数类型!
    1. 编写接口

      public interface UserMapper {
          //查询全部用户
          List<User> getUserList();
      
          //根据ID查询用户
          User getUserById(int id);
      
          //insert一个用户
          int addUser(User user);
      
          //修改用户
          int updateUser(User user);
      
          //删除用户
          int delUserById(int id);
      }
      
    2. 编写对应的mapper中的sql语句

      <mapper namespace="com.heze.dao.UserMapper">
      <!--    select查询语句-->
          <select id="getUserList" resultType="com.heze.pojo.User">
              select * from mybatis.user
          </select>
      
          <select id="getUserById" parameterType="int" resultType="com.heze.pojo.User">
              select * from mybatis.user where id = #{id}
          </select>
      
      <!--    对象中的属性,可以直接取出来-->
          <insert id="addUser" parameterType="com.heze.pojo.User" >
              insert into mybatis.user (id, name, pwd) values (#{id}, #{name}, #{pwd});
          </insert>
      
          <update id="updateUser" parameterType="com.heze.pojo.User">
              update mybatis.user set name = #{name},pwd = #{pwd} where id = #{id};
          </update>
      
          <delete id="delUserById" parameterType="com.heze.pojo.User">
              delete from mybatis.user where id = #{id};
          </delete>
      </mapper>
      
    3. 测试 注意点:增删改需要提交事务

      public class UserDaoTest {
          @Test
          public void test(){
              //第一步:获得SqlSession对象
              SqlSession sqlSession = MybatisUtils.getSqlSession();
              //方式一:getMapper
              UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
              List<User> userList = userMapper.getUserList();
      
              for (User user : userList){
                  System.out.println(user);
              }
      
              //关闭SqlSession
              sqlSession.close();
          }
      
          @Test
          public void getUserById(){
              SqlSession sqlSession = MybatisUtils.getSqlSession();
      
              UserMapper mapper = sqlSession.getMapper(UserMapper.class);
              User user = mapper.getUserById(1);
              System.out.println(user);
      
              sqlSession.close();
          }
      
          //增删改需要提交事务
          @Test
          public void insertUser(){
              SqlSession sqlSession = MybatisUtils.getSqlSession();
              UserMapper mapper = sqlSession.getMapper(UserMapper.class);
              int res = mapper.addUser(new User(4, "哈哈", "123321"));
              if (res > 0){
                  System.out.println("插入成功");
              }
              sqlSession.commit();
              sqlSession.close();
          }
      
          @Test
          public void updateUser(){
              SqlSession sqlSession = MybatisUtils.getSqlSession();
              UserMapper mapper = sqlSession.getMapper(UserMapper.class);
              int res = mapper.updateUser(new User(4, "微微", "520520"));
              if (res > 0){
                  System.out.println("修改成功!");
              }
              sqlSession.commit();
              sqlSession.close();
          }
      
          @Test
          public void delUserById(){
              SqlSession sqlSession = MybatisUtils.getSqlSession();
              UserMapper mapper = sqlSession.getMapper(UserMapper.class);
              int i = mapper.delUserById(1);
              if (i > 0){
                  System.out.println("删除成功");
              }
              sqlSession.commit();
              sqlSession.close();
          }
      }
      
      
四、排查错误
  1. 解析错误,从报错代码从下往上看
  2. 输出乱码,调节xml文件中字符集为UTF-8
  3. 输出地址,实体类构造方法中需要构造toString方法
  4. 不能找到相应的xml,路径格式是否写规范,mappers下面的resource需要使用/来划分文件路径
  5. utf-8中第几个字节的出错问题,是否添加了xml头文件格式
五、模糊查询
  1. java代码执行的时候,传递通配符% %(建议)
    List<User> userList = mapper.getUserLike("%李%");
    
  2. 在sql拼接中使用通配符(不建议)
    select * from mybatis.user where name like "%"#{value}"%"
    
六、配置解析
  1. Mybatis可以配置成适应多种环境

    不过要记住:尽管可以配置多个环境,但每个SqlSessionFactory实例只能选择一种环境

  2. Mybatis默认事务管理器就是JDBC,连接池:POOLED
  3. 属性(properties):我们可以通过properties属性来实现引用配置文件

    这些属性都是可外部配置且可动态替换的,既可以在典型的java属性文件中配置,亦可通过properties元素的子元素来传递。【db.properties】

    编写一个配置文件db.properties

    driver=com.mysql.jdbc.Driver
    url=jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai"
    username = root
    password=123456
    

    引入外部文件,可以增加一些属性配置,但优先使用外部配置文件

    <!--    引入外部配置文件-->
        <properties resource="db.properties"></properties>
    
  4. 别名typeAliases
    • 类型别名是为java类型设置一个短的名字。

    • 存在的意义仅用于来减少类完全限定名的冗余。

      <!--    可以给实体类起别名-->
          <typeAliases>
              <typeAlias type="com.heze.pojo.User" alias="User"/>
          </typeAliases>
      

      也可指定一个包名,Mybatis会在包名下面搜索需要的javabean,比如:

      扫描实体类的包,它的默认别名就为这个类的 类名,首字母小写(大写也可)

      <!--    可以给实体类起别名-->
          <typeAliases>
              <package name="com.heze.pojo"/>
          </typeAliases>
      

      第三种就是通过注解起别名,比如:

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

      在实体类比较少的时候,使用第一种方式。

      如果实体类十分多,建议使用第二次方式。

      第一种和第三种可以DIY(自定义)别名,第二种则不行。

  5. 映射器(mappers)

    MapperRegistry:注册绑定我们的Mapper文件;

    方式一:[推荐使用]

    <mappers>
            <mapper resource="com/heze/dao/UserMapper.xml"/>
        </mappers>
    

    方式二:使用class文件绑定注册

    <mappers>
            <mapper class="com.heze.dao.UserMapper"/>
        </mappers>
    

    方式三:使用package包绑定注册

    <mappers>
            <package name="com.heze.dao"/>
        </mappers>
    

    方式二和方式三注意点:

    • 接口和他的Mapper配置文件必须同名!
    • 接口和他的Mapper配置文件必须在同一个包下!
  6. 生命周期和作用域

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DF4dSlxL-1665152069755)(img/image-20221005172937417.png)]

    生命周期和作用域是至关重要的,因为错误的使用会导致非常严重的并发问题

    SqlSessionFactoryBuilder:

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

    SqlSessionFactory:

    • 数据库连接池
    • 一旦创建就应该在应用中一直存在
    • 因此SqlSessionFactory的最佳作用域是应用作用域
    • 最简单的就是使用单例模式或者静态单例模式

    SqlSession:

    • 连接到连接池的一个请求
    • SqlSession的实例不是线程安全的,因此是不能被共享的,所以他的最佳作用域是请求或方法作用域。
    • 用完之后需要赶紧关闭,否则资源被占用!

    这里的每一个Mapper,就代表一个具体的sql业务!

  7. 解决属性名和字段名不一致的问题
    • 表别名
    • resultMap
七、日志
  1. 日志工厂

    如果一个数据库操作,出现了异常,我们需要排错,日志就是最好的助手!

    曾经:sout,debug

    现在:日志工厂!

    • SLF4J
    • LOG4J 【掌握】
    • LOG4JJ2
    • JDK_LOGGING
    • COMMONS_LOGGING
    • STDOUT_LOGGING 【掌握】
    • NO_LOGGING

    在Mybatis中具体使用哪个日志,在设置中设定!

    STDOUT_LOGGING标准日志输出

    在mybatis核心文件中,配置我们的日志!

    <!--    设置标准日志工厂-->
        <settings>
            <setting name="logImpl" value="STDOUT_LOGGING"/>
        </settings>
    
    

    输出结果主要如下:

    原本输出:

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qYr2A86R-1665152069756)(img/image-20221005212317064.png)]

    添加日志后的输出:

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cNjb6sd1-1665152069756)(img/image-20221005212231090.png)]

  2. 配置Log4j
    • 导入依赖

      <!--        log4j-->
              <dependency>
                  <groupId>log4j</groupId>
                  <artifactId>log4j</artifactId>
                  <version>1.2.12</version>
              </dependency>
      
    • 在resource中添加log4j属性配置

      #将等级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/heze.log
      log4j.appender.file.MaxFilesize=10mb
      log4j.appender.file.Threshold=DEBUG
      log4j.appender.file.layout=org.apache.log4j.PatternLayout
      1og4j.appender.file.layout.ConversionPattern=[%p][%d{yy-MM-dd}][%c]%m%n
      
      #日志输出级别
      1og4j.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
      
    • 配置log4j设置为日志实现

      <!--    设置日志-->
          <settings>
              <setting name="logImpl" value="LOG4J"/>
          </settings>
      
      
    • Log4j的使用!

      [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oVbbgg8M-1665152069757)(img/image-20221005215240805.png)]

      简单使用:

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

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

        static Logger logger = Logger.getLogger(UserDaoTest.class);
        
      3. 日志级别

        logger.info("info:进入了testLog4j");
                logger.debug("debug:进入了testLog4j");
                logger.error("error:进入了testLog4j");
        

      显示效果:

      [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oKcdVxbu-1665152069757)(img/image-20221005221643091.png)]

八、分页
  1. 使用limit实现分页,下标从0开始,每页大小为2
    select * from user limit 0, 2;
    

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-scYcOTYd-1665152069758)(img/image-20221006093745748.png)]

  2. 使用Mybatis实现分页,核心SQL
    • 接口

      //分页
      List<User> getUserByLimit(Map<String, Integer> map);
      
    • Mapper.xml

      <!--    分页-->
          <select id="getUserByLimit" parameterType="map" resultType="User">
              select * from mybatis.user limit #{startIndex},#{pageSize}
          </select>
      
    • 测试

      @Test
          public void getUserByLimit(){
              SqlSession sqlSession = MybatisUtils.getSqlSession();
              UserMapper mapper = sqlSession.getMapper(UserMapper.class);
              HashMap<String, Integer> map = new HashMap<>();
              map.put("startIndex", 0);
              map.put("pageSize", 2);
      
              List<User> userList = mapper.getUserByLimit(map);
              for (User user : userList) {
                  System.out.println(user);
              }
              sqlSession.close();
      
          }
      

      [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yGlOhonh-1665152069758)(img/image-20221006095625961.png)]

九、使用注解开发
  1. 面向接口开发

    我们学习的时候大都是面向对象开发,但是在开发过程中,大多数都是面向接口开发

  2. 注解在接口上实现
    @Select("select * from user")
    List<User> getUsers();
    
  3. 需要在核心配置文件中绑定接口!
    <mappers>
    <!--        绑定接口-->
            <mapper class="com.heze.dao.UserMapper"/>
    </mappers>
    
  4. 测试

    本质:反射机制实现

    底层:动态代理!

  5. CRUD
    • 我们可以在工具类创建的时候实现自动提交事务!

       public static SqlSession getSqlSession(){
              return sqlSessionFactory.openSession(true);
          }
      
    • 编写接口,增加注释

      @Select("select * from user where id = #{id}")
      User getUserById(@Param("id") int id);
      

      注意:我们必须要将注册绑定到我们的核心配置文件中!

  6. 关于@Param()注解
    • 基本类型的参数或者String类型需要加上
    • 引用类型不需要加
    • 如果只有一个基本类型的话,可以忽略,但是建议大家都加上!
    • 我们在Sql中引用的就是我们这里@Param()中设定的属性
  7. #{}与${}
    • 尽量使用#{}替代${}
    • 因为${}无法阻止sql注入产生的问题
十、Lombok
  1. 在idea中安装lombok插件

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-V16ki4Ka-1665152069758)(img/image-20221006132546900.png)]

  2. 在项目中导入lombok的jar包
    <!--        lombok-->
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>1.18.4</version>
        <scope>provided</scope>
    </dependency>
    
  3. 在实体类加上注解
    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    public class User {
        private int id;
        private String name;
        private String pwd;
    }
    

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4M8t55FZ-1665152069759)(img/image-20221006133011580.png)]

@Getter and @Setter
@FieldNameConstants
@ToString
@EqualsAndHashCode
@AllArgsConstructor, @RequiredArgsConstructor and @NoArgsConstructor
@Log, @Log4j, @Log4j2, @Slf4j, @XSlf4j, @CommonsLog, @JBossLog, @Flogger, @CustomLog
@Data【大多使用】
@Builder
@SuperBuilder
@Singular
@Delegate
@Value
@Accessors
@Wither
@With
@SneakyThrows

说明:

@Data:无参构造,get,set,tostring,hashcode,equals
@AllArgsConstructor	有参构造
@NoArgsConstructor	无参构造
十一、多对一处理
  1. 比如:多个学生有同一名老师

    实体类:
    @Data
    public class Student {
        private int id;
        private String name;
    
        //学生需要关联一个老师!
        private Teacher teacher;
    }
    
    @Data
    public class Teacher {
        private int id;
        private String name;
    }
    
  2. 多对一:关联(association) 一对多:集合(collection)

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BC66OFv4-1665152069759)(img/image-20221006170904764.png)]

  3. 按照查询嵌套处理(子查询)
    <mapper namespace="com.heze.dao.StudentMapper">
    
        <select id="getStudent" resultMap="StudentTeacher">
            select * from student
        </select>
        <resultMap id="StudentTeacher" type="Student">
            <result property="id" column="id"/>
            <result property="name" column="name"/>
    <!--        复杂的属性,我们需要单独处理  对象:association  集合:collection-->
            <association property="teacher" column="tid" javaType="Teacher" select="getTeacher"/>
        </resultMap>
        <select id="getTeacher" resultType="Teacher">
           select * from teacher where id = #{tid}<!--这个地方tid可以随便写,但是建议写tid-->
        </select>
    
    </mapper>
    
  4. 按照结果嵌套处理(连表查询)
    <!--    按照结果嵌套处理-->
        <select id="getStudent2" resultMap="StudentTeacher2">
            select s.id sid, s.name sname, t.name tname
            from student s, teacher t
            where s.tid = t.id
        </select>
        <resultMap id="StudentTeacher2" type="Student">
            <result property="id" column="sid"/>
            <result property="name" column="sname"/>
            <association property="teacher" javaType="Teacher">
                <result property="name" column="tname"/>
            </association>
        </resultMap>
    
十二、一对多处理
  1. 比如:一个老师拥有多名学生

    实体类:
    @Data
    public class Student {
        private int id;
        private String name;
    
        private int tid;
    }
    
    @Data
    public class Teacher {
        private int id;
        private String name;
    
        //一个老师拥有多个学生
        private List<Student> students;
    }
    
  2. 按照结果查询处理
    <!--    关联查询-->
    <select id="getTeacher" resultMap="TeacherStudent">
        select s.id sid, s.name sname, 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"/>
        <!--        association:javaType:指定属性的类型
                    collection:ofType:指定属性的类型-->
        <collection property="students" ofType="Student">
            <result property="id" column="sid"/>
            <result property="name" column="sname"/>
            <result property="tid" column="tid"/>
        </collection>
    </resultMap>
    
  3. 按照查询嵌套处理
    <!--    子查询-->
    <select id="getTeacher2" resultMap="TeacherStudent2">
        select * from mybatis.teacher where id = #{tid};
    </select>
    <resultMap id="TeacherStudent2" type="Teacher">
        <collection property="students" column="id" javaType="ArrayList" ofType="Student" select="getStudentByTeacherId"/>
    </resultMap>
    <select id="getStudentByTeacherId" resultType="Student">
        select * from mybatis.student where tid = #{tid}
    </select>
    
  4. 结果:

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RTgTAfba-1665152069759)(img/image-20221007102857442.png)]

  5. javaTpye & ofType
    • javaType:用来指定实体类中属性的类型
    • ofType:用来指定映射到List或者集合中的pojo类型,泛型中的约束类型
  6. 注意点
    • 保证SQL的可读性,尽量保证通俗易懂
    • 注意一对多和多对一中属性名和字段的问题
    • 如果问题不好排查错误,可以使用日志,建议使用Log4j
十三、动态SQL
  1. 什么是动态SQL

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

    简单来讲,就是拼接sql语句

    建议:先在Mysql中写出完整语句,再对应的去修改成为我们的动态SQL实现通用即可!

    使用动态 SQL 并非一件易事,但借助可用于任何 SQL 映射语句中的强大的动态 SQL 语言,MyBatis 显著地提升了这一特性的易用性。

    如果你之前用过 JSTL 或任何基于类 XML 语言的文本处理器,你对动态 SQL 元素可能会感觉似曾相识。在 MyBatis 之前的版本中,需要花时间了解大量的元素。借助功能强大的基于 OGNL 的表达式,MyBatis 3 替换了之前的大部分元素,大大精简了元素种类,现在要学习的元素种类比原来的一半还要少。

    • if
    • choose (when, otherwise)
    • trim (where, set)
    • foreach
  2. 搭建环境
    CREATE TABLE `blog`(
    	`id` varchar(50) NOT NULL COMMENT'博客id',
    	`tit1e` varchar(100)NOT NULL COMMENT'博客标题',
    	`author` varchar(30) NOT NULL COMMENT'博客作者',
    	`create_time` datetime NOT NULL COMMENT '创建时间',
    	`views` int(30)NOT NULL COMMENT'浏览量'
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8
    

    创建一个基础工程

    1. 导包
    2. 编写配置文件
    3. 编写实体类
    4. 编写实体类对应的Mapper接口和Mapper.xml文件
  3. IF,条件选择
    <select id="queryBlogIF" parameterType="map" resultType="blog">
        select * from mybatis.blog where 1 = 1
        <if test="title != null">
            and title = #{title}
        </if>
        <if test="author != null">
            and author = #{author}
        </if>
    </select>
    
    @Test
        public void queryBlogIF(){
            SqlSession sqlSession = MybatisUtils.getSqlSession();
            BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
            HashMap hashMap = new HashMap();
            hashMap.put("title", "世界如此简单");
            List<Blog> blogs = mapper.queryBlogIF(hashMap);
            for (Blog blog : blogs) {
                System.out.println(blog);
            }
            sqlSession.close();
        }
    

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RPr0IG93-1665152069760)(img/image-20221007134129454.png)]

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pO8r55mX-1665152069760)(img/image-20221007134059519.png)]

  4. choose(when,otherwise)相当于swich case,选择一个
    <select id="queryBlogChoose" parameterType="map" resultType="blog">
        select * from mybatis.blog
        <where>
            <choose>
                <when test="title != null">
                    title = #{title}
                </when>
                <when test="author != null">
                    and author = #{author}
                </when>
                <otherwise>
                    and views = #{views}
                </otherwise>
            </choose>
        </where>
    </select>
    
  5. set,set语句及逗号处理
    <update id="updateBlog" parameterType="map">
        update mybatis.blog
        <set>
            <if test="title != null">
                title = #{title},
            </if>
            <if test="author != null">
                author = #{author}
            </if>
        </set>
        where id = #{id}
    </update>
    
  6. sql片段

    sql将一部分sql语句抽离出来使用include进行复用

    <sql id="if-title-author">
        <if test="title != null">
            title = #{title}
        </if>
        <if test="author != null">
            and author = #{author}
        </if>
    </sql>
    
    <select id="queryBlogIF" parameterType="map" resultType="blog">
            select * from mybatis.blog
            <where>
            <include refid="if-title-author"></include>  <!--进行引用-->
            </where>
        </select>
    

    注意事项:

    • 最好基于单表来定义SQL片段
    • 不要存在where标签,尽量使用if片段
  7. Foreach

    动态sql中对集合进行遍历,通常是在构建IN条件语句的时候,比如:

    select * from mybatis.blog where 1 = 1 and (id=1 or id=2 or id=3)
    
    <select id="queryBlogForeach" parameterType="map" resultType="blog">
        select * from mybatis.blog
        <where>
            <foreach collection="ids" item="id" open="and (" close=")" separator="or">
                id = #{id}
            </foreach>
        </where>
    </select>
    
    @Test
        public void queryBlogForeach(){
            SqlSession sqlSession = MybatisUtils.getSqlSession();
            BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
            HashMap map = new HashMap();
            ArrayList<Integer> ids = new ArrayList<>();
            ids.add(1);
            ids.add(2);
            map.put("ids", ids);
            List<Blog> blogs = mapper.queryBlogForeach(map);
            for (Blog blog : blogs) {
                System.out.println(blog);
            }
            sqlSession.close();
        }
    

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Xq9CfLHc-1665152069761)(img/image-20221007150540472.png)]

十四、缓存(了解)
  1. 一级缓存(默认开启)

    是基于 PerpetualCache(MyBatis自带)的 HashMap 本地缓存,作用范围为 session 域内。当 session flush(刷新)或者 close(关闭)之后,该 session 中所有的 cache(缓存)就会被清空。

    在参数和 SQL 完全一样的情况下,我们使用同一个 SqlSession 对象调用同一个 mapper 的方法,往往只执行一次 SQL。因为使用 SqlSession 第一次查询后,MyBatis 会将其放在缓存中,再次查询时,如果没有刷新,并且缓存没有超时的情况下,SqlSession 会取出当前缓存的数据,而不会再次发送 SQL 到数据库。

    由于 SqlSession 是相互隔离的,所以如果你使用不同的 SqlSession 对象,即使调用相同的 Mapper、参数和方法,MyBatis 还是会再次发送 SQL 到数据库执行,返回结果。

    @Test
        public void getUserById(){
            SqlSession sqlSession = MybatisUtils.getSqlSession();
    
            UserMapper mapper = sqlSession.getMapper(UserMapper.class);
            User user = mapper.getUserById(1);
            System.out.println(user);
    
            System.out.println("===========================");
            User user2 = mapper.getUserById(1);
            System.out.println(user2);
    
            sqlSession.close();
        }
    

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GE5370mB-1665152069761)(img/image-20221007171700563.png)]

    由结果可知,只是查询同一个东西的话查询两次,只进行了一次查询数据库的操作。

    查询不同东西:

    User user2 = mapper.getUserById(2);
    

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-m1MaTrtH-1665152069761)(img/image-20221007172153907.png)]

    查询两个不同的就会执行两次操作,一次缓存与SqlSession同级,也就是与SqlSession周期一致,topool结束

    缓存失败的情况:

    1. 查询不同的东西
    2. 增删改操作,可能会改变原来的数据,所以必定会刷新缓存!
    3. 查询不同的Mapper.xml
    4. 手动清理缓存!

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6fBGypld-1665152069762)(img/image-20221007172741721.png)]

  2. 二级缓存

    二级缓存是全局缓存,作用域超出 session 范围之外,可以被所有 SqlSession 共享。

    一级缓存缓存的是 SQL 语句,二级缓存缓存的是结果对象。

    简单来说:我们可以开启二级缓存,将一级缓存的东西放在二级缓存里面

    工作机制:一级缓存消亡的时候,二级缓存可开启,相当于需要一级完成,二级才可拷贝

    二级缓存的配置

    1)MyBatis 的全局缓存配置需要在 mybatis-config.xml 的 settings 元素中设置,代码如下。

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

    2)在 mapper 文件(如 WebMapper.xml)中设置缓存,默认不开启缓存。需要注意的是,二级缓存的作用域是针对 mapper 的 namescape 而言,即只有再次在 namescape 内(net.biancheng.WebsiteMapper)的查询才能共享这个缓存,代码如下。

    <mapper namescape="net.biancheng.WebsiteMapper">    
        <!-- cache配置 -->    
        <cache        
               eviction="FIFO"        
               flushInterval="60000"        
               size="512"        
               readOnly="true" />
        ...
    </mapper>
    

    3)在 mapper 文件配置支持 cache 后,如果需要对个别查询进行调整,可以单独设置 cache,代码如下。

    <select id="getWebsiteList" resultType="net.biancheng.po.Website" usecache="true">    ...</select>
    
    测试:我们需要将实体类序列化!否则就要报错
    Caused by: java.io.NotSerializableException: com.heze.pojo.User
    
    小结:
    • 只要开启了二级缓存,在同一个Mapper下就有效
    • 所有的数据都会先放在一级缓存中
    • 只有当会话提交,或者关闭的时候,才会提交到二级缓存中!
    • 用户查询的时候:查看二级缓存–>一级缓存–>查询数据库

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yHg9RmA5-1665152069762)(img/image-20221007175231062.png)]
    总结:到目前为止,mybatis差不多学完了,这里将我练习的mybatis项目放在我的仓库,以便方便回顾和继续学习。

我的博客地址:
    [https://blog.csdn.net/qq_51326491?spm=1011.2415.3001.5343](https://blog.csdn.net/qq_51326491?spm=1011.2415.3001.5343)
我的仓库地址:
    [https://gitee.com/he-ze__-an/gitstudy](https://gitee.com/he-ze__-an/gitstudy)
  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值