ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

Mybatis学习笔记

2022-04-06 08:32:30  阅读:173  来源: 互联网

标签:xml mapper 缓存 笔记 学习 sqlSession user Mybatis log4j


一、什么是MyBatis

特点

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

历史发展

  • 本名“iBatis”,iBATIS一词来源于“internet”和“abatis”的组合
  • 2010年由apache基金会迁移到谷歌,改名为MyBatis
  • 2013年11月由谷歌迁移到GitHub

持久化

  • 什么是持久层

    即数据持久化,也就是指数据瞬时状态和持久状态的过程

    也指数据对象持久化,防止对象数据丢失

MyBatis优点

  • 解除sql与程序代码的耦合
  • 提供映射标签
  • 提供对象关系映射标签
  • 提供xml标签,支持编写动态sql

二、程序结构

  • 3个jar包

    • mysql-connector-java.jar
    • junit.jar
    • mybatis.jar
  • 核心配置文件

    <configuration>
        <!--配置文件-->
        <environments default="one">
            <!--多个环境-->
            <!--默认加载指定的环境ID-->
            <environment id="one">
                <transactionManager type="JDBC"/>
                <!--事务管理类型-->
                <dataSource type="POOLED">
                <!--数据资源类型为池子-->
                    <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
                    <property name="url" value="jdbc:mysql//:localhost:3306/数据库"/>
                    <property name="username" value="root"/>
                    <property name="password" value="root"/>
                </dataSource>
            </environment>
        </environments>
        <mappers>
        	<mapper namespace="com.dragon.dao.Mapper.xml"/>
        </mappers>
    </configuration>
    

    注意:Mapper.xml注册

  • 数据库连接

    public class MybatisUtils{
        private SqlSessionFactory sqlSessionFactory;
        static{
            String resource = "mybatis-config.xml";
            InputStream inputStream = MybatisUtils.class.getClassLoader().getResourceAsStream(resource);
            //InputStream inputStream = Resources.getResourceAsStream(resource);
            sqSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        }
        //返出session对应mapper.xml
        public static SqlSession getSession(){
            return SqlSessionFactory.openSession();
        }
    }
    
    
  • SQL语句实现

    <?xml version="1.0" encoding="UTF-8" ?>
    <!DOCTYPE mapper
            PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
            "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    <!--绑定对应接口-->
    <!--这里可以对应Impl实现接口功能-->
    
    <mapper namespace="com.dragon.dao.对应接口">
        <select id="方法名" resultType="结果类型">
        	select * from table;
        </select>
    </mapper>
    
  • 测试

    public class Test{
        SqlSession sqlSession = MybatisUtils.getSession();
        接口名 mapper = sqlSession.getMapper(接口名.class);
        返回类型 对象名 = mapper.方法名();
        //关闭
        sqlSession.close();
    }
    
  • 资源过滤

三、搭建入门Mybatis程序

1.程序部署

  • MySQL5.7/MySQL8.0以上

  • 创建数据库和数据表

    CREATE TABLE `users` (
      `id` int NOT NULL AUTO_INCREMENT,
      `name` varchar(20) CHARACTER SET utf8 COLLATE utf8 NOT NULL,
      `pwd` int NOT NULL,
      PRIMARY KEY (`id`)
    ) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8 COLLATE=utf8
    

    注意写引擎(ENGINE)和字符集(CHARSET

    注意配置Maven的setting.xml和仓库

  • 创建一个MavenModel

    • 将其src删除,以当前Model为父工程,创建子工程,防止多次配置pom.xml

    • 在父工程pom.xml配置以下内容

      <?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.dragon</groupId>
          <artifactId>mybatisStudy</artifactId>
          <packaging>pom</packaging>
          <version>1.0-SNAPSHOT</version>
          <modules>
              <module>mybatis-01</module>
          </modules>
          <!--导入依赖-->
          <!--mysql驱动-->
          <dependencies>
              <dependency>
                  <groupId>mysql</groupId>
                  <artifactId>mysql-connector-java</artifactId>
                  <version>8.0.19</version>
              </dependency>
              <!--mybatis驱动-->
              <dependency>
                  <groupId>org.mybatis</groupId>
                  <artifactId>mybatis</artifactId>
                  <version>3.5.9</version>
              </dependency>
              <!--junit-->
              <dependency>
                  <groupId>junit</groupId>
                  <artifactId>junit</artifactId>
                  <version>4.12</version>
              </dependency>
          </dependencies>
          <properties>
              <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
          </properties>
              <!--资源过滤-->
          <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>
      

      注意:一定要配置资源过滤,否则项目执行不成功

    • 该项目中包含三个包

      • pojo实体类

      • dao数据处理层

        1.接口

        2.userMapper.xml

      • utils工具包

        1.MybatisUtils.class

  • 链接数据库在IDEA中

  • 创建mybatis配置文件“mybatis-config.xml

    • 位于src/main/resources/

    • 配置内容如下

      <?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.cj.jdbc.Driver"/>
                      <property name="url" value="jdbc:mysql://localhost:3306/mybatis?serverTimezone=UTC&amp;useSSL=false&amp;useUnicode=true&amp;characterEncoding=UTF-8"/>
                      <property name="username" value="root"/>
                      <property name="password" value="root"/>
                  </dataSource>
              </environment>
          </environments>
          <mappers>
              <mapper resource="com/dragon/dao/userMapper.xml"/>
          </mappers>
      </configuration>
      

      注意:此处一定要配置mapper,找到userMapper.xml文件,否则执行不成功(userMapper.xml是实现接口的方法)

  • userMapper.xml内容如下

    <?xml version="1.0" encoding="UTF-8" ?>
    <!DOCTYPE mapper
            PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
            "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    <!--namespace绑定一个对应的dao接口-->
    
    <mapper namespace="com.dragon.dao.UserDao">
        <select id="getUserList" resultType="com.dragon.pojo.User">
            select * from mybatis.users
        </select>
    </mapper>
    

    此处getUserList为接口中的方法名

  • MybatisUtils.class

    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 java.io.IOException;
    import java.io.InputStream;
    
    public class MybatisUtils {
        private static SqlSessionFactory sqlSessionFactory;
        static {
            //使用Mybatis第一步,获取sqlSessionFactory对象
            String resource = "mybatis-config.xml";
            InputStream inputStream = null;
            try {
                inputStream = Resources.getResourceAsStream(resource);
            } catch (IOException e) {
                e.printStackTrace();
            }
            sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        }
        public static SqlSession getSqlSession(){
            return sqlSessionFactory.openSession();
        }
    }
    
  • Test测试类

    import org.apache.ibatis.session.SqlSession;
    import org.junit.Test;
    
    import java.util.List;
    
    public class MyBatisTest {
        @Test
        public void test(){
            //获取sqlSession对象
            SqlSession sqlSession = MybatisUtils.getSqlSession();
            UserDao mapper = sqlSession.getMapper(UserDao.class);
            List<User> userList = mapper.getUserList();
    
            for (User user : userList) {
                System.out.println(user);
            }
            sqlSession.close();
        }
    }
    
    

2.解决问题

2.1

  • mybatis-config连接驱动中,如果是mysql8版本以下的jar包/依赖,那么可以如下写

    <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"/>
    
  • 相反,在mysql8以及8以上版本

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

2.2

  • 配置pom.xml一定要配置资源过滤,否则会出现找不到userMapper.xml文件。

     <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>
    
  • 在mybatis-config.xml文件中配置内容中一定要注册mapper,否则会出现“org.apache.ibatis.binding.BindingException: Type interface com.dragon.dao.UserDao is not known to the MapperRegistry.”这种错误。

    <mappers>
    <mapper resource="com/dragon/dao/userMapper.xml"/>
     <!--或者  <package name="com.dragon.dao"/> -->
    </mappers>
    

四、CRUD

1.选择、查询语句

  • id:就是对应的namespace方法名
  • resultType:sql语句执行的返回值类型
  • parameterType:参数类型
  1. 编写接口

      List<User> getUserList();
    
  2. 配置mapper中SQL语句

    <mapper namespace="com.dragon.dao.UserMapper">
        <select id="getUserList" resultType="com.dragon.pojo.User">
            select * from mybatis.users
        </select>
    </mapper>
    
  3. 测试类

     @Test
        public void test(){
            //获取sqlSession对象
            SqlSession sqlSession = MybatisUtils.getSqlSession();
            UserMapper mapper = sqlSession.getMapper(UserMapper.class);
            users = mapper.getUserList();
    
            for (User user : users) {
                System.out.println(user);
            }
            sqlSession.close();
        }
    

2.增加数据

  1. 编写接口

     int insertUser(User user);
    
  2. 配置Mapper中SQL语句

     <insert id="insertUser" parameterType="com.dragon.pojo.User">
            insert into mybatis.users values (#{id},#{name},#{pwd});
     </insert>
    
  3. 测试类

     @Test
        public void testInsert(){
            SqlSession sqlSession = MybatisUtils.getSqlSession();
            UserMapper mapper = sqlSession.getMapper(UserMapper.class);
            int user0 = mapper.insertUser(new User(6, "小红", 888));
            if (user0>0){
                System.out.println("插入成功");
            }
            //提交事务
            sqlSession.commit();
            sqlSession.close();
        }
    

3.删除数据

  1. 编写接口

    void deleteUserById(int id);
    
  2. 配置Mapper中SQL语句

    <delete id="deleteUserById" parameterType="int">
        delete from mybatis.users where id = #{id}
    </delete>
    
  3. 测试类

        @Test
        public void testDeleteById(){
            SqlSession sqlSession = MybatisUtils.getSqlSession();
            UserMapper mapper = sqlSession.getMapper(UserMapper.class);
            mapper.deleteUserById(6);
            users = mapper.getUserList();
            for (User user : users) {
                System.out.println(user);
            }
            sqlSession.commit();
            sqlSession.close();
        }
    

4.更改数据

  1. 编写接口

    void queryUser(int pwd);
    
  2. 配置Mapper中SQL语句

    <update id="queryUser" parameterType="int">
        update mybatis.users set pwd=#{pwd} where id = 3
    </update>
    
  3. 测试类

    @Test
        public void testQueryById(){
            SqlSession sqlSession = MybatisUtils.getSqlSession();
            UserMapper mapper = sqlSession.getMapper(UserMapper.class);
            mapper.queryUser(0000);
            users = mapper.getUserList();
            for (User user : users) {
                System.out.println(user);
            }
            sqlSession.close();
        }
    

5.注意

  • 在增、删、改三种情况下必须提交事务!

    sqlSession.commit();
    
  • 关闭sqlSession

    sqlSession.close();
    
  • 接口所有的普通参数,尽量都写上@Param参数,尤其是多个参数时,必须写上!

  • 有时候根据业务的需求,可以考虑使用map传递参数!

    当一个实体类属性过多时,可以使用Map来解决命名传参

    //java---controller层
    Map<> list = new HashMap<String,Object>;
    list.put("a",值);
    
    <!--xml---sql语句-->
    <insert id="方法名" paramType="map">
        insert into table values(#{a});
    </insert>
    
    • Map传递参数,直接在SQL中取出即可
    • 对象传递参数,直接在SQL中取对象的属性即可
    • 只有一个基本类型参数的情况下,可以直接在sql中取到
    • 多个参数用Map或者注解
  • 为了规范操作,在SQL的配置文件中,我们尽量将Parameter参数和resultType都写上!

6.SQL模糊查询

  • 在sql.xml中直接%%,容易被SQL注入,不知道为啥
  • 在controller中用%%

五、配置

1.结构

MyBatis 的配置文件包含了会深深影响 MyBatis 行为的设置和属性信息。 配置文档的顶层结构如下:

2.属性(properties)

这些属性可以在外部进行配置,并可以进行动态替换。你既可以在典型的 Java 属性文件中配置这些属性,也可以在 properties 元素的子元素中设置。例如:

<properties resource="org/mybatis/example/config.properties">
  <property name="username" value="dev_user"/>
  <property name="password" value="F2Fa3!33TYyg"/>
</properties>

设置好的属性可以在整个配置文件中用来替换需要动态配置的属性值。比如:

<dataSource type="POOLED">
  <property name="driver" value="${driver}"/>
  <property name="url" value="${url}"/>
  <property name="username" value="${username}"/>
  <property name="password" value="${password}"/>
</dataSource>

3.属性别名(typeAliases)

  • 为实体类设置别名,意在降低冗余的全限定类名书写

    • DIY自定义
    <typeAliases>
    	<typeAlias alias="User" type="com.dragon.pojo.User"/>
    </typeAliases>
    
    • 扫描包
    <typeAliases>
    	<package name="com.dragon.pojo"/>
    </typeAliases>
    <!--扫描完之后,就可以直接使用类名-->
    
    • 注解
    @Alias("user")
    public class User{
        
    }
    

4.映射器(mappers)

  • 注册mapper-SQL语句

  • 三种方式

    • 相对路径

      <mappers>
      	<mapper resource="com.dragon.dao.UserMapper.xml" />
      </mappers>
      
    • 使用完全限定资源定位符(URL)

      <mappers>
        <mapper url="file:///var/mappers/AuthorMapper.xml"/>
        <mapper url="file:///var/mappers/BlogMapper.xml"/>
        <mapper url="file:///var/mappers/PostMapper.xml"/>
      </mappers>
      
    • 使用映射器接口实现类的完全限定类名

      <mappers>
        <mapper class="org.mybatis.builder.AuthorMapper"/>
        <mapper class="org.mybatis.builder.BlogMapper"/>
        <mapper class="org.mybatis.builder.PostMapper"/>
      </mappers>
      
    • 将包内的映射器接口实现全部注册为映射器

      <mappers>
        <package name="c"/>
      </mappers>
      

六、生命周期及作用域

  • SqlSession建造流程

    image-20220331154229005
  • 两种方式结束

    1. Sqlmapper获取类,类获取方法
    2. SqlSession直接调用sql操作(不建议使用)
  • 生命周期、作用域是至关重要的,因为错误的使用会导致非常严重的并发问题

  • SqlSessionFactoryBuilder

    • 一旦创建了SqlSessionFactory,就不需要建造者了
    • 局部变量
  • SqlSessionFactory:

    • 可以理解为:数据库连接池
    • 一旦被创建,在运行期间一直存在,没有任何理由丢弃或重新创建另外一个实例(当然也可以创建,但是会出现高并发,导致程序崩溃
    • 最简单就是使用单列模式/静态单列模式
  • SqlSession

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

    • 每个Mapper代表一个业务

七、ResultMap结果映射集

1.解决属性名和字段名不一致问题

  • 起别名

SQL中的字段与bean中的字段不一致

修改sql语句

as 别名

  • resultMap的使用

    当类中的属性和数据库的字段不一致时

    <resultMap id="对应下面的resultMapd" type="类名">
    	<result property="pwd" column="password"/>
    </resultMap>
    <mapper namespace="com.dragon.dao.UserMapper">
        <select resultMap="map" id="方法名">
        	select id,username,username from table
        </select>
    </mapper>
    

八、日志工厂

  • 在配置文件出错时,我们基本无法排查,所以有了日志工厂,以此来监听内部步骤

  • mybatis-config.xml文件中设置以下内容

    <settings>
    	<setting name="logImpl" value="STDOUT_LOGGING"/>
        <!--注意该句位置,以及书写对错-->
    </settings>
    
  • 官方文档:

logImpl 指定 MyBatis 所用日志的具体实现,未指定时将自动查找。 SLF4J | LOG4J(deprecated since 3.5.9) | LOG4J2 | JDK_LOGGING | COMMONS_LOGGING | STDOUT_LOGGING | NO_LOGGING

1.Log4j

  • 什么是Log4j

    • Apache的一个开源项目,通过使用Log4j,可以控制日志信息输送的目的地是控制台、文件、GUI组件
    • 也可以控制每一条日志的输出格式
    • 通过配置文件来灵活地进行配置,而不需要修改代码
  • 先导入log4j的包

    <!-- https://mvnrepository.com/artifact/log4j/log4j -->
    <dependency>
        <groupId>log4j</groupId>
        <artifactId>log4j</artifactId>
        <version>1.2.17</version>
    </dependency>
    
  • 配置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/dragon.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
    
  • 配置log4j为日志实现

    <settings>
    	<setting name="logImpl" value=""/>
    </settings>
    
  • 简单使用

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

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

      static Logger logger = Logger.getLogger(当前类名.class);
      
    3. 日志级别

      logger.info("info进入testlog4j");
      logger.debug("debug进入testlog4j");
      logger.error("error进入testlog4j");
      

九、分页

9.1LIMIT分页

  • 作用:减少数据处理量

  • SELECT * FROM user LIMIT startIndex,pageSize;
    #或者-----起始位置,每页数量
    SELECT * FROM user LIMIT pageSize;
    
  • 使用MyBatis分页

    1. 接口

      List<User> getUserByLimit(Map<String,Integer> map);
      
    2. SQL

      <select id="getUserByLimit" paramterType="map" resultMap="UserMap">
      	select * from mybatis.user limit #{startIndex},#{pageSize}
      </select>
      
    3. HashMap<> map = new HashMap<String,Integer>();
      map.put("startIndex",1);
      map.put("pageSize",2);
      

9.2RowBounds分页

十、注解开发MyBatis

  • 开发中基本不用
  • 简单,不用配置xml

十一、剖面解析原理

MyBatis剖面解析

十二、复杂查询

1.多对一

  • ResultMap结果映射

    • association:对象
    • collection*:集合
  • 两个实体类

    • Teacher.java
    • Student.java

    其中多个学生对应一个老师

    在Student类中创建Teacher的姓名

  • <select id="getStudent" resultMap="StudentTeacher">
    	select * from student
    </select>
    <resultMap id="StudentTeacher" type="Student">
        <result property="id" column="id"/>
        <result property="name" column="name"/>
        <!--对应Teacher类-->
        <association property="teacher" column="tid" javaType="Teacher" select="getTeacher"/>
    </resultMap>
    <select id="getTeacher" resultType="Teacher">
    	select * from teacher where id =#{id}
    </select>
    
  • 注意:提前映射Mapper文件,设置typeAlias

十三、动态SQL

  • 动态 SQL 是 MyBatis 的强大特性之一。

  • 动态关键词

    • if

      <select id="findActiveBlogLike"
           resultType="Blog">
        SELECT * FROM BLOG WHERE state = ‘ACTIVE’ 
        <if test="title != null">
          AND title like #{title}
        </if>
        <if test="author != null and author.name != null">
          AND author_name like #{author.name}
        </if>
      </select>
      
    • choose (when, otherwise)

      有时候,我们不想使用所有的条件,而只是想从多个条件中选择一个使用。针对这种情况,MyBatis 提供了 choose 元素,它有点像 Java 中的 switch 语句。

      还是上面的例子,但是策略变为:传入了 “title” 就按 “title” 查找,传入了 “author” 就按 “author” 查找的情形。若两者都没有传入,就返回标记为 featured 的 BLOG(这可能是管理员认为,与其返回大量的无意义随机 Blog,还不如返回一些由管理员精选的 Blog)。

      <select id="findActiveBlogLike"
           resultType="Blog">
        SELECT * FROM BLOG WHERE state = ‘ACTIVE’
        <choose>
          <when test="title != null">
            AND title like #{title}
          </when>
          <when test="author != null and author.name != null">
            AND author_name like #{author.name}
          </when>
          <otherwise>
            AND featured = 1
          </otherwise>
        </choose>
      </select>
      
    • trim (where, set)

      <select id="findActiveBlogLike"
           resultType="Blog">
        SELECT * FROM BLOG
        <where>
          <if test="state != null">
               state = #{state}
          </if>
          <if test="title != null">
              AND title like #{title}
          </if>
          <if test="author != null and author.name != null">
              AND author_name like #{author.name}
          </if>
        </where>
      </select>
      
    • foreach

      动态 SQL 的另一个常见使用场景是对集合进行遍历(尤其是在构建 IN 条件语句的时候)。比如:

      <select id="selectPostIn" resultType="domain.blog.Post">
        SELECT *
        FROM POST P
        <where>
          <foreach item="item" index="index" collection="list"
              open="ID in (" separator="," close=")" nullable="true">
                #{item}
          </foreach>
        </where>
      </select>
      

十四、缓存

简介

1、什么是缓存 [ Cache ]?

  • 存在内存中的临时数据。
  • 将用户经常查询的数据放在缓存(内存)中,用户去查询数据就不用从磁盘上(关系型数据库数据文件)查询,从缓存中查询,从而提高查询效率,解决了高并发系统的性能问题。

2、为什么使用缓存?

  • 减少和数据库的交互次数,减少系统开销,提高系统效率。

3、什么样的数据能使用缓存?

  • 经常查询并且不经常改变的数据。

Mybatis缓存

  • MyBatis包含一个非常强大的查询缓存特性,它可以非常方便地定制和配置缓存。缓存可以极大的提升查询效率。

  • MyBatis系统中默认定义了两级缓存:一级缓存二级缓存

    • 默认情况下,只有一级缓存开启。(SqlSession级别的缓存,也称为本地缓存)
    • 二级缓存需要手动开启和配置,他是基于namespace级别的缓存。
    • 为了提高扩展性,MyBatis定义了缓存接口Cache。我们可以通过实现Cache接口来自定义二级缓存

1.一级缓存

  • 一级缓存又叫“本地缓存”:SqlSession
    • 与数据库同一次会话期间查询到数据会放在本地缓存中
    • 以后如果需要获取相同数据,直接存缓存中拿,没必要去查询数据库
  • 缓存时间即事务开启到事务结束

一级缓存失效的四种情况

一级缓存是SqlSession级别的缓存,是一直开启的,我们关闭不了它;

一级缓存失效情况:没有使用到当前的一级缓存,效果就是,还需要再向数据库中发起一次查询请求!

1、sqlSession不同

@Test
public void testQueryUserById(){
   SqlSession session = MybatisUtils.getSession();
   SqlSession session2 = MybatisUtils.getSession();
   UserMapper mapper = session.getMapper(UserMapper.class);
   UserMapper mapper2 = session2.getMapper(UserMapper.class);

   User user = mapper.queryUserById(1);
   System.out.println(user);
   User user2 = mapper2.queryUserById(1);
   System.out.println(user2);
   System.out.println(user==user2);

   session.close();
   session2.close();
}

观察结果:发现发送了两条SQL语句!

结论:每个sqlSession中的缓存相互独立

2、sqlSession相同,查询条件不同

@Test
public void testQueryUserById(){
   SqlSession session = MybatisUtils.getSession();
   UserMapper mapper = session.getMapper(UserMapper.class);
   UserMapper mapper2 = session.getMapper(UserMapper.class);

   User user = mapper.queryUserById(1);
   System.out.println(user);
   User user2 = mapper2.queryUserById(2);
   System.out.println(user2);
   System.out.println(user==user2);

   session.close();
}

观察结果:发现发送了两条SQL语句!很正常的理解

结论:当前缓存中,不存在这个数据

3、sqlSession相同,两次查询之间执行了增删改操作!

增加方法

//修改用户
int updateUser(Map map);

编写SQL

<update id="updateUser" parameterType="map">
  update user set name = #{name} where id = #{id}
</update>

测试

@Test
public void testQueryUserById(){
   SqlSession session = MybatisUtils.getSession();
   UserMapper mapper = session.getMapper(UserMapper.class);

   User user = mapper.queryUserById(1);
   System.out.println(user);

   HashMap map = new HashMap();
   map.put("name","kuangshen");
   map.put("id",4);
   mapper.updateUser(map);

   User user2 = mapper.queryUserById(1);
   System.out.println(user2);

   System.out.println(user==user2);

   session.close();
}

观察结果:查询在中间执行了增删改操作后,重新执行了

结论:因为增删改操作可能会对当前数据产生影响

4、sqlSession相同,手动清除一级缓存

@Test
public void testQueryUserById(){
   SqlSession session = MybatisUtils.getSession();
   UserMapper mapper = session.getMapper(UserMapper.class);

   User user = mapper.queryUserById(1);
   System.out.println(user);

   session.clearCache();//手动清除缓存

   User user2 = mapper.queryUserById(1);
   System.out.println(user2);

   System.out.println(user==user2);

   session.close();
}

一级缓存就是一个map

2、二级缓存

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

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

  • 工作机制

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

使用步骤

1、开启全局缓存 【mybatis-config.xml】

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

2、去每个mapper.xml中配置使用二级缓存,这个配置非常简单;【xxxMapper.xml】

<cache/>

官方示例=====>查看官方文档
<cache
 eviction="FIFO"
 flushInterval="60000"
 size="512"
 readOnly="true"/>
这个更高级的配置创建了一个 FIFO 缓存,每隔 60 秒刷新,最多可以存储结果对象或列表的 512 个引用,而且返回的对象被认为是只读的,因此对它们进行修改可能会在不同线程中的调用者产生冲突。

3、代码测试

  • 所有的实体类先实现序列化接口
  • 测试代码
@Test
public void testQueryUserById(){
   SqlSession session = MybatisUtils.getSession();
   SqlSession session2 = MybatisUtils.getSession();

   UserMapper mapper = session.getMapper(UserMapper.class);
   UserMapper mapper2 = session2.getMapper(UserMapper.class);

   User user = mapper.queryUserById(1);
   System.out.println(user);
   session.close();

   User user2 = mapper2.queryUserById(1);
   System.out.println(user2);
   System.out.println(user==user2);

   session2.close();
}

结论

  • 只要开启了二级缓存,我们在同一个Mapper中的查询,可以在二级缓存中拿到数据
  • 查出的数据都会被默认先放在一级缓存中
  • 只有会话提交或者关闭以后,一级缓存中的数据才会转到二级缓存中

3.缓存原理

mybatis缓存原理

当一级缓存的session关闭时,利用我们在mapper中设置的<cache/>来进行二级缓存

标签:xml,mapper,缓存,笔记,学习,sqlSession,user,Mybatis,log4j
来源: https://www.cnblogs.com/Z-Dragon/p/16104889.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有