Mabatis的学习笔记

MyBatis

什么是MyBatis?

  • Mybatis是一款优秀的持久层框架,用于简化JDBC开发

  • 它支持自定义 SQL、存储过程以及高级映射。

  • MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。

  • MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。

  • 官网: https://mybatis.org/mybatis-3/zh/index.html

  • MyBatis原来是apache的一个开源项目,叫做ibatis,2010年这个项目由apache迁移到了google code,并且改名为MyBatis,2013年11月官方代码迁移到GitHub

持久化

持久化时将程序数据在持久状态瞬时状态切换的机制。(持久化是mybatis最重要的特性)

  • 即把数据(如内存中的对象)保存到可永久存放的存储设备中(如磁盘)。持久化的主要应用时将内存中的对象存储在数据库中,或者存储在磁盘中、XML文件中

  • JDBC就是一种持久化机制。文件IO也是一种持久化机制。

  • 内存:断电即失

为什么需要持久化服务?

  • 内存断点后数据会丢失,但有些业务不允许这种情况的存在

  • 比起硬盘,内存过于昂贵,如果有够量的内存,则不需要持久化服务,但是正是因为内存太贵,储存有限,因此需要持久化来缓存

持久层

  • 持久层,顾名思义是完成持久化工作的代码块,也就是Date Access Object(Dao层)

  • 大多数情况下特别是企业级应用,数据持久化往往也就意味着将内存中的数据保存到磁盘上加以固化,而持久化的实现过程则大多通过各种关系数据库来完成。

  • 不过这里有一个字需要特别强调,也就是所谓的“层”。对于应用系统而言,数据持久功能大多是必不可少的组成部分。也就是说,我们的系统中,已经天然的具备了“持久层”概念?也许是,但也许实际情况并非如此。之所以要独立出一个“持久层”的概念,而不是“持久模块”,“持久单元”,也就意味着,我们的系统架构中,应该有一个相对独立的逻辑层面,专注于数据持久化逻辑的实现

  • 与系统其他部分相对而言,这个层面应该具有一个较为清晰和严格的逻辑边界。(也就是该层就是为了操作数据库而设计的

Mybatis的作用

  • Mybatis就是帮程序员将数据存取到数据库里面

  • 传统的JDBC操作,有很多重复代码块,比如:数据取出时的封装 数据库的建立连接等等… , 通过框架可以减少重复代码,提高开发效率 .

  • MyBatis 是一个半自动化的ORM框架 (Object Relationship Mapping) -->对象关系映射

  • 所有的事情,不用Mybatis依旧可以做到,只是用了它,会更加方便更加简单,开发更快速。

Mybatis的优点

  • 简单易学:本身就很小且简单。没有任何第三方依赖,最简单安装只要两个jar文件+配置几个sql映射文件就可以了,易于学习,易于使用,通过文档和源代码,可以比较完全的掌握它的设计思路和实现。

  • 灵活:mybatis不会对应用程序或者数据库的现有设计强加任何影响。sql写在xml里,便于统一管理和优化。通过sql语句可以满足操作数据库的所有需求。

  • 解除sql与程序代码的耦合:通过提供DAO层,将业务逻辑和数据访问逻辑分离,使系统的设计更清晰,更易维护,更易单元测试。sql和代码的分离,提高了可维护性。

  • 提供xml标签,支持编写动态sql。

  • 现在主流使用方法

第一个Mybatis程序

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

搭建环境

搭建数据库

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 char set =utf8;
insert into user(id, name, pwd) VALUES
(1,'java',1223456),
(2,'dewie',31251263),
(3,'cnwuiv',243678623)

新建项目

1.新建一个普通的maven项目

2.删除src目录

3.导入maven依赖

 <!--导入依赖-->
    <dependencies>
        <!-- MySQL驱动 -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.33</version> <!-- MySQL 8.x版本支持JDK 11 -->
        </dependency>

        <!-- MyBatis驱动 -->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.5.13</version> <!-- MyBatis 3.x版本支持JDK 11 -->
        </dependency>

        <!-- JUnit测试框架 -->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.13.2</version> <!-- JUnit 4.x版本支持JDK 11 -->
            <scope>test</scope>
        </dependency>
    </dependencies>

4.创建模块

  • 编写Mybatis的核心配置文件

  • <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="211314"/>
                   </dataSource>
            </environment>
        </environments>
    
    </configuration>
  • 编写Mybatis的工具类

    package 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 java.io.IOException;
    import java.io.InputStream;
    
    public class MybatisUtils {
        private static SqlSessionFactory sqlSessionFactory;
        static {
            String resource = "mybatis-config.xml";
            try {
                //使用mybatis第一获取SQLSessionFactory对象
                InputStream inputStream = Resources.getResourceAsStream(resource);
                SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
    //既然有了 SqlSessionFactory,顾名思义,我们可以从中获得 SqlSession 的实例。
    // SqlSession 提供了在数据库执行 SQL 命令所需的所有方法。你可以通过 SqlSession 实例来直接执行已映射的 SQL 语句。
    public static SqlSession getSqlSession(){
        SqlSession sqlSession = sqlSessionFactory.openSession();
        return sqlSession;
    }
    
    }
    

    编写代码

    • 实体类

      package 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 + '\'' +
                      '}';
          }
      }
      

    • DAO接口

      public interface UserDao {
          List<User> getUserList();
      }
      

    • 接口实现类由原来的UserDaoImpl转换成一个Mapper配置文件

<?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">
<mapper namespace="dao.UserDao">
    <select id="getUserList" resultType="pojo.User">
       select * from Mybatis.user
    </select>
</mapper>

2.4测试

注意点:

org.apache.ibatis.binding.BindingException: Type interface dao.UserDao is not known to the MapperRegistry.

MapperRegistry是什么?

每一个Mapper.xml文件都需要在Mybatis核心配置文件中注册!

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

  • junit测试

    package dao;
    
    import org.apache.ibatis.session.SqlSession;
    import org.junit.Test;
    import pojo.User;
    import utils.MybatisUtils;
    
    import java.util.List;
    
    public class UserDaoTest {
        @Test
        public void test(){
            //获得SQL session对象
                SqlSession sqlSession = MybatisUtils.getSqlSession();
                //方式一:getMapper
                try {
                UserDao userDao = sqlSession.getMapper(UserDao.class);
                List<User> userList = userDao.getUserList();
                for (User user : userList) {
                    System.out.println(user);
                }
            } catch (Exception e) {
                throw new RuntimeException(e);
            } finally {
                //关闭sqlsession
                sqlSession.close();
            }
        }
    }
    

    可能会遇到的问题:

    1.配置文件没有注册

    2.绑定接口错误

    3.方法名不对

    4.返回类型不对

    5.Maven导出资源问题

SqlSessionFactoryBuilder

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

SqlSessionFactory

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

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

CURD

insert delete update select

  • id:就是对应的namespace中的方法名;

  • namespace中的包名要和Dao/Mapper接口的包名保持一致

  • resultType:sql语句的返回值! Class,

  • parameterType:参数类型,方法的参数

主要步骤:

1.编写接口

public interface UserMapper {
    //查询
    List<User> selectUser();
    //根据id查询
    User selectUserById(int id);
    //增加用户
    int addUser(User user);
    //修改用户
    int updateUser(User user);
    //删除用户
    int deleteUser(int id);
}

2.编写对应的mapper中的SQL语句

<mapper namespace="com.lyc.mapper.UserMapper">
    <select id="selectUser" resultType="com.lyc.pojo.User">
        select * from mybatis.user
    </select>
    <select id="selectUserById" resultType="com.lyc.pojo.User" parameterType="int">
        select * from mybatis.user where id = #{id}
    </select>
    <insert id="addUser" parameterType="com.lyc.pojo.User">
        insert into mybatis.user(id,name,pwd) values (#{id},#{name},#{pwd})
    </insert>
    <update id="updateUser" parameterType="com.lyc.pojo.User">
        update mybatis.user
        set name = #{name},pwd = #{pwd}
        where id = #{id};
    </update>
    <delete id="deleteUser" parameterType="com.lyc.pojo.User">
        delete
        from mybatis.user
        where id = #{id};
    </delete>
</mapper>

3.测试

public void selectAllUser() throws Exception {
    SqlSession sqlSession = new MybatisUtils().getsqlSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    mapper.selectUser().forEach(System.out::println);
}
@Test
public void selectUserById() throws Exception {
    SqlSession sqlSession = new MybatisUtils().getsqlSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    User user = mapper.selectUserById(1);
    System.out.println(user);
}
@Test
public void addUser() throws Exception {
    SqlSession sqlSession = new MybatisUtils().getsqlSession();
    User user = new User(4, "lyc", "1275683");
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    int i = mapper.addUser(user);
    //提交事务
    sqlSession.commit();
    sqlSession.close();
}
@Test
public void updateUser() throws Exception {
    SqlSession sqlSession = new MybatisUtils().getsqlSession();
    User user = new User(4, "lyc", "1786683");
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    int i = mapper.updateUser(user);
    //提交事务
    sqlSession.commit();
    sqlSession.close();
}
@Test
public void deleteUser() throws Exception {
    SqlSession sqlSession = new MybatisUtils().getsqlSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
   int i = mapper.deleteUser(4);
    //提交事务
    sqlSession.commit();
    sqlSession.close();
}

注:增删改需要提交事务!!!

     sqlSession.commit();

Map

假设我们的实体类字段或者参数过多,我们应当考虑使用Map

 <insert id="AddUserByMap" parameterType="map">
        insert into mybatis.user(id,name,pwd) values (#{userId},#{userName},#{userPwd})
    </insert>
 public void AddUserByMap() throws Exception {
        SqlSession sqlSession = new MybatisUtils().getsqlSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        Map<String, Object> map = new HashMap<>();
        map.put("userId",5);
        map.put("userName","张角");
        map.put("userPwd","岁在甲子,天下大吉");
        //提交事务
        sqlSession.commit();
        sqlSession.close();
    }

Map传递参数,直接在SQL中取出对应的key即可 【parameterType="map"】

对象传递参数,直接在SQL中取对象的属性即可 【parameterType="com.lyc.pojo.User"】

只有一个基本类型参数的情况下,可以直接在SQL中取到

多个参数用Map或注解

模糊查询

1.Java代码执行时,传递通配符% %

public void selectUserLike() throws Exception {
    SqlSession sqlSession = new MybatisUtils().getsqlSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    List<User> userList = mapper.selectUserLike("%张%");
    userList.forEach(System.out::println);
    sqlSession.close();
}

2.在SQL中使用通配符!

select *
from mybatis.user
where name like concat('%',#{value},'%');

配置解析

1.核心配置文件

  • mybatis-config.xml

  • Mybatis的配置文件包含了会深深影响Mybatis行为的设置与属性信息

  • configuration(配置)
    properties(属性)
    settings(设置)
    typeAliases(类型别名)
    typeHandlers(类型处理器)
    objectFactory(对象工厂)
    plugins(插件)
    environments(环境配置)
    environment(环境变量)
    transactionManager(事务管理器)
    dataSource(数据源)
    databaseIdProvider(数据库厂商标识)
    mappers(映射器)

2.environments(环境配置)

Mybatis可以配置成多种环境,但是每个sqlSessionFactory实例只能选择一种环境,如下,有环境development和test,默认是development环境

<environments default="test">
    <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"/>
            <property name="username" value="root"/>
            <property name="password" value="211314"/>
        </dataSource>
    </environment>
    <environment id="test">
        <transactionManager type="JDBC"/>
        <dataSource type="POOLED">
        <property name="driver" value="com.mysql.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://localhost:3306/mybatis"/>
        <property name="username" value="root"/>
        <property name="password" value="211314"/>
        </dataSource>
    </environment>
</environments>
transactionManager(事务管理器)

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

  • JDBC – 这个配置直接使用了 JDBC 的提交和回滚设施,它依赖从数据源获得的连接来管理事务作用域。

  • MANAGED – 这个配置几乎没做什么。它从不提交或回滚一个连接,而是让容器来管理事务的整个生命周期(比如 JEE 应用服务器的上下文)。 默认情况下它会关闭连接。然而一些容器并不希望连接被关闭,因此需要将 closeConnection 属性设置为 false 来阻止默认的关闭行为。例如:

    <transactionManager type="MANAGED">
      <property name="closeConnection" value="false"/>
    </transactionManager>
dataSource(数据源)

作用:连接数据库 dbcp c3p0 druid

dataSource 元素使用标准的 JDBC 数据源接口来配置 JDBC 连接对象的资源。

  • 大多数 MyBatis 应用程序会按示例中的例子来配置数据源。虽然数据源配置是可选的,但如果要启用延迟加载特性,就必须配置数据源。

有三种内建的数据源类型(也就是 type="[UNPOOLED|POOLED|JNDI]")

mybatis默认的事务管理器就是JDBC,连接池就是POOLED

3.properties(属性)

可以通过properties属性来实现引用配置文件 这些属性可以在外部进行配置,并可以进行动态替换。你既可以在典型的 Java 属性文件中配置这些属性,也可以在 properties 元素的子元素中设置(即db.properties)

注:在xml中,所有的标签都可以规定其顺序

编写一个配置文件

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

在核心配置文件映入

<!--   映入外部配置文件-->
    <properties resource="db.properties">
        <property name="username" value="root"/>
        <property name="password" value="211314"/>
    </properties>
  • 可以直接引入外部文件

  • 可以在其中增加一些属性配置

  • 如果两个文件有同一个字段,优先使用外部配置文件

注:外部配置文件的优先级比在xml文件中写的配置优先级更高

4.类型别名(typeAliases)

  • 类型别名可为 Java 类型设置一个缩写名字。 它仅用于 XML 配置,

  • 存在的意义就是降低冗余的全限定类名书写。

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

也可以指定一个包名,MyBatis 会在包名下面搜索需要的 Java Bean,每一个在包 domain.blog 中的 Java Bean,在没有注解的情况下,会使用 Bean 的首字母小写的非限定类名来作为它的别名。 比如 domain.blog.Author 的别名为 author

 <typeAliases>
        <package name="com.lyc.pojo"/>
    </typeAliases>

也可以使用注解@Alias("user")

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

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

如果实体类十分多,可以用第二种

5.设置(settings)

是 MyBatis 中极为重要的调整设置,它们会改变 MyBatis 的运行时行为。 下表描述了设置中各项设置的含义、默认值等。

设置名描述有效值默认值
cacheEnabled全局性地开启或关闭所有映射器配置文件中已配置的任何缓存。true | falsetrue
lazyLoadingEnabled延迟加载的全局开关。当开启时,所有关联对象都会延迟加载。 特定关联关系中可通过设置 fetchType 属性来覆盖该项的开关状态。true | falsefalse
aggressiveLazyLoading开启时,任一方法的调用都会加载该对象的所有延迟加载属性。 否则,每个延迟加载属性会按需加载(参考 lazyLoadTriggerMethods)。true | falsefalse (在 3.4.1 及之前的版本中默认为 true)
multipleResultSetsEnabled是否允许单个语句返回多结果集(需要数据库驱动支持)。true | falsetrue
useColumnLabel使用列标签代替列名。实际表现依赖于数据库驱动,具体可参考数据库驱动的相关文档,或通过对比测试来观察。true | falsetrue
useGeneratedKeys允许 JDBC 支持自动生成主键,需要数据库驱动支持。如果设置为 true,将强制使用自动生成主键。尽管一些数据库驱动不支持此特性,但仍可正常工作(如 Derby)。true | falseFalse
autoMappingBehavior指定 MyBatis 应如何自动映射列到字段或属性。 NONE 表示关闭自动映射;PARTIAL 只会自动映射没有定义嵌套结果映射的字段。 FULL 会自动映射任何复杂的结果集(无论是否嵌套)。NONE, PARTIAL, FULLPARTIAL
autoMappingUnknownColumnBehavior指定发现自动映射目标未知列(或未知属性类型)的行为。NONE: 不做任何反应WARNING: 输出警告日志('org.apache.ibatis.session.AutoMappingUnknownColumnBehavior' 的日志等级必须设置为 WARNFAILING: 映射失败 (抛出 SqlSessionException)NONE, WARNING, FAILINGNONE
defaultExecutorType配置默认的执行器。SIMPLE 就是普通的执行器;REUSE 执行器会重用预处理语句(PreparedStatement); BATCH 执行器不仅重用语句还会执行批量更新。SIMPLE REUSE BATCHSIMPLE
defaultStatementTimeout设置超时时间,它决定数据库驱动等待数据库响应的秒数。任意正整数未设置 (null)
defaultFetchSize为驱动的结果集获取数量(fetchSize)设置一个建议值。此参数只可以在查询设置中被覆盖。任意正整数未设置 (null)
defaultResultSetType指定语句默认的滚动策略。(新增于 3.5.2)FORWARD_ONLY | SCROLL_SENSITIVE | SCROLL_INSENSITIVE | DEFAULT(等同于未设置)未设置 (null)
safeRowBoundsEnabled是否允许在嵌套语句中使用分页(RowBounds)。如果允许使用则设置为 false。true | falseFalse
safeResultHandlerEnabled是否允许在嵌套语句中使用结果处理器(ResultHandler)。如果允许使用则设置为 false。true | falseTrue
mapUnderscoreToCamelCase是否开启驼峰命名自动映射,即从经典数据库列名 A_COLUMN 映射到经典 Java 属性名 aColumn。true | falseFalse
localCacheScopeMyBatis 利用本地缓存机制(Local Cache)防止循环引用和加速重复的嵌套查询。 默认值为 SESSION,会缓存一个会话中执行的所有查询。 若设置值为 STATEMENT,本地缓存将仅用于执行语句,对相同 SqlSession 的不同查询将不会进行缓存。SESSION | STATEMENTSESSION
jdbcTypeForNull当没有为参数指定特定的 JDBC 类型时,空值的默认 JDBC 类型。 某些数据库驱动需要指定列的 JDBC 类型,多数情况直接用一般类型即可,比如 NULL、VARCHAR 或 OTHER。JdbcType 常量,常用值:NULL、VARCHAR 或 OTHER。OTHER
lazyLoadTriggerMethods指定对象的哪些方法触发一次延迟加载。用逗号分隔的方法列表。equals,clone,hashCode,toString
defaultScriptingLanguage指定动态 SQL 生成使用的默认脚本语言。一个类型别名或全限定类名。org.apache.ibatis.scripting.xmltags.XMLLanguageDriver
defaultEnumTypeHandler指定 Enum 使用的默认 TypeHandler 。(新增于 3.4.5)一个类型别名或全限定类名。org.apache.ibatis.type.EnumTypeHandler
callSettersOnNulls指定当结果集中值为 null 的时候是否调用映射对象的 setter(map 对象时为 put)方法,这在依赖于 Map.keySet() 或 null 值进行初始化时比较有用。注意基本类型(int、boolean 等)是不能设置成 null 的。true | falsefalse
returnInstanceForEmptyRow当返回行的所有列都是空时,MyBatis默认返回 null。 当开启这个设置时,MyBatis会返回一个空实例。 请注意,它也适用于嵌套的结果集(如集合或关联)。(新增于 3.4.2)true | falsefalse
logPrefix指定 MyBatis 增加到日志名称的前缀。任何字符串未设置
logImpl指定 MyBatis 所用日志的具体实现,未指定时将自动查找。SLF4J | LOG4J | LOG4J2 | JDK_LOGGING | COMMONS_LOGGING | STDOUT_LOGGING | NO_LOGGING未设置
proxyFactory指定 Mybatis 创建可延迟加载对象所用到的代理工具。CGLIB | JAVASSISTJAVASSIST (MyBatis 3.3 以上)
vfsImpl指定 VFS 的实现自定义 VFS 的实现的类全限定名,以逗号分隔。未设置
useActualParamName允许使用方法签名中的名称作为语句参数名称。 为了使用该特性,你的项目必须采用 Java 8 编译,并且加上 -parameters 选项。(新增于 3.4.1)true | falsetrue
configurationFactory指定一个提供 Configuration 实例的类。 这个被返回的 Configuration 实例用来加载被反序列化对象的延迟加载属性值。 这个类必须包含一个签名为static Configuration getConfiguration() 的方法。(新增于 3.2.3)一个类型别名或完全限定类名。未设置

较为重点:日志:logImpl

6.其他设置

typeHandlers(类型处理器) objectFactory(对象工厂) plugins(插件) 1.mybatis-generator-core 2.mybatis-plus 3.通用mapper

7.映射器(mappers)

MapperRegistry:绑定注册我们的Mapper文件;(没有绑定会报这个错误)

org.apache.ibatis.binding.BindingException: Type interface com.lyc.mapper.UserMapper is not known to the MapperRegistry.

方式一:

//每一个mapper.xml都需要在Mybatis核心配置文件中注册
<mappers>
    <mapper resource="com/lyc/mapper/UserMapper.xml"/>
</mappers>

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

    <mappers>
<!--        <mapper resource="com/lyc/mapper/UserMapper.xml"/>-->
        <mapper class="com.lyc.mapper.UserMapper"/>
    </mappers>

注意点:

  • 接口和它的Mapper配置文件必须同名

  • 接口和它的配置文件必须在同一个包下

方式三:使用扫描进行绑定注册

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

注意点和第二种一样

8.生命周期和作用域

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

SqlSessionFactoryBuilder

  • 一旦创建了sqlSessionFactory,就不需要他了,它的作用域就是局部变量

SqlSessionFactory

  • 可以想象成数据库连接池,用完还可以循环利用

  • SqlSessionFactory一旦被创建就应该在应用的运行期间一直存在,没有任何理由丢弃它或重新创建另外一个实例

  • 最简单的就是用单例模式或静态单例模式

SQLSession

  • 连接到连接池的一个请求

  • SqlSession 的实例不是线程安全的,因此是不能被共享的,所以它的最佳的作用域是请求或方法作用域。

  • 用完之后要赶紧关闭,否则被占用资源

  • 最合适的

    try (SqlSession session = sqlSessionFactory.openSession()) {
      // 你的应用逻辑代码
    }
    finally{
    session.close();
    }

ResultMap结果集映射

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

数据库中的字段和mybatis实体类的属性名不一致的情况

解决方法:

  • 起别名

<select id="selectUser" resultType="com.lyc.pojo.User">
    select id, name, pwd as password from mybatis.user
</select>

resultMap(结果集映射)

映射

    <resultMap id="UserMap" type="com.lyc.pojo.User">
<!--        colum数据库中的字段,property实体类的属性-->
        <id property="id" column="id"/>
        <result property="name" column="name"/>
        <result property="password" column="pwd"/>
    </resultMap>
    <select id="selectUser" resultMap="UserMap">
        select * from mybatis.user
    </select>

resultMap 元素是 MyBatis 中最重要最强大的元素,ResultMap 的设计思想是,对简单的语句做到零配置,对于复杂一点的语句,只需要描述语句之间的关系就行了。

这就是 ResultMap 的优秀之处——你完全可以不用显式地配置它们。 虽然上面的例子不用显式配置 ResultMap

如果这个世界总是这么简单就好了

日志

日志工厂

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

曾经:sout debug

现在:日志工厂

  • SLF4J

  • LOG4J

  • LOG4J2

  • JDK_LOGGING

  • COMMONS_LOGGING

  • STDOUT_LOGGING 【掌握】

  • NO_LOGGING

在mybatis中具体使用哪个日志实现,在设置中设定

STDOUT_LOGGING标准日志输出

<settings>
    <setting name="logImpl" value="stdout_logging"/>
</settings>

分页

思考:为什么要分页?

  • 减少数据的处理量

使用Limit分页

语法:select * from user limit startIndex,pageSize
select * from mybatis.user limit 6;#[0,n]

使用Mybatis实现分页,核心SQL

1.接口

List<User> selectUserByLimit(Map<String,Integer> map);

2.Mapper.xml

<select id="selectUserByLimit" parameterType="map" resultType="user" resultMap="UserMap">
    select * from mybatis.user limit #{startIndex},#{pageSize}
</select>

3.测试

public void selectUserByLimit() throws Exception {
    SqlSession sqlSession = new MybatisUtils().getsqlSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    Map<String, Integer> map = new HashMap<>();
    map.put("startIndex",0);
    map.put("pageSize",2);
    List<User> users = mapper.selectUserByLimit(map);
    for (User user : users) {
        System.out.println(user);
    }
    sqlSession.close();
}

注:基本类型的别名是前面加个_

别名映射的类型
_bytebyte
_longlong
_shortshort
_intint
_integerint
_doubledouble
_floatfloat
_booleanboolean
stringString
byteByte
longLong
shortShort
intInteger
integerInteger
doubleDouble
floatFloat
booleanBoolean
dateDate
decimalBigDecimal
bigdecimalBigDecimal
objectObject
mapMap
hashmapHashMap
listList
arraylistArrayList
collectionCollection
iteratorIterator

使用注解开发

面向接口编程

在真正的开发中,很多时候我们会选择面向接口编程

根本原因:解耦,可拓展,提高复用,分层开发中,上层不用管具体的实现,大家都遵守共同的标准,是的开发变得容易,规范性更好

在一个面向对象的系统中,系统的各种功能是有许许多多不同的对象协作完成的,在这种情况下,各个对象内部是如何实现自己的,对系统设计人员讲就不那么重要了

而各个对象之间的协作关系则称为系统设计的关键,小到不同类的通信,大到各模块的交互,在系统设计之初都是要着重考虑的,这是系统设计的主要工作内容,面向接口编程就是按照这种思想来编程

关于接口的理解

  • 接口从更深层次的理解,应是定义(规范、约束)与实现(名实分离的原则)的分离

  • 接口的本身反映了系统的抽象理解

  • 接口应为两类

    • 第一类是对一个个体的抽象,它可对应一个抽象体(abstract class)

    • 第二类是对一个个某一方面的抽象,即形成一个抽象面(interface)

一个个体可能有多个抽象面,抽象体与抽象面是有区别的

三个面向区别

  • 面向对象是指我们考虑问题时,以对象为单位,考虑他的属性与方法

  • 面向过程是指,我们考虑问题时,以一个具体的流程(事务过程)为单位,考虑他的实现

  • 接口设计和非接口设计是针对复用技术而言的,与面向对象(过程)不是一个问题,更多的体现就是对系统整体的架构

使用注解开发

1.注解在接口上实现

public interface UserMapper {
    @Select("select * from mybatis.user")
    List<User> getUsers();
}

2.需要在核心配置文件绑定接口

<mappers>
    <mapper class="com.lyc.mapper.UserMapper"/>
</mappers>

3.测试

本质:反射机制实现

底层:动态代理

注解增删改查

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

 sqlSessionFactory.openSession(true);

编写接口,增加注解

public interface UserMapper {
    @Select("select * from mybatis.user")
    List<User> getUsers();
    //方法存在多个参数,所有的参数前面必须加上@Param("id")优先级最高,如果括号里的参数为错,会报错
    @Select("select * from mybatis.user where id = #{id}")
    User getUserById(@Param("id") int id);
   @Insert("insert into mybatis.user(id,name,pwd) values (#{id},#{name},#{password})")
    int addUser(User user);
   //修改
    @Update("update mybatis.user set name = #{name},pwd = #{password} where id = #{id}")
    int updateUser(User user);
    @Delete("delete from mybatis.user where id = #{id}")
    int deleteUser(@Param("id") int id);
}

测试

  public void test4() throws IOException {
        SqlSession sqlSession = new MybatisUtils().getsqlSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        int i = mapper.deleteUser(7);
        sqlSession.close();
    }
}

注:必须要将接口注册到核心配置文件中,没有注入会爆org.apache.ibatis.binding.BindingException: Type interface com.lyc.mapper.UserMapper is not known to the MapperRegistry.

关于@param()注解

  • 基本类型的参数或String类型,需要加上

  • 引用类型不用加

  • 如果只有一个基本类型的话,可以忽略,但是建议大家都加上

  • 我们在SQL中引用的就是我们这里的@Param()中设定的属性名

    SQL注入:就是别人通过拼接的方式修改SQL语句,十分危险

#{} ${} 区别

1.1 #符号解析:

上面说了#可以实现预编译,那你知道预编译过程中mybatis都做了啥吗,其实主要表现在数据类型检查安全检查两部分:

数据类型检查表现在:若检测到为数值类型,就不加引号,即?;若检测到位字符串类型,就加上引号,即'?'。

安全检查表现在:若变量的值带有引号,会对引号进行转义处理,这样可以防止sql注入

1.2 #符号的应用场景:

*需要在sql映射文件中动态拼接sql时的开发场景*,比如传入多个变量进行条件查询、传入一个POJO进行数据插入等。

2.1 $符号解析:

$符号主要用于传入的参数是sql片段的场景下,会直接进行字符串替换,完成了sql的拼接。比如我们不在sql映射文件中利用mybatis的动态sql来拼接sql,而是在java代码中去动态的拼接sql,然后将拼接的sql片段作为变量传入sql映射文件。上面也说了$符号只是进行字符串的替换,如果我们传入一个sql片段的话,相当于直接进行了sql拼接,就不用在sql映射文件中利用mybatis提供的动态sql标签来拼接sql了。甚至可以这样:

<select id="getUserPage" resultType="com.ymxx.oa.bean.User">



        ${sql片段}



</select>

对于这样外部传入的sql,就不能使用#{},上面也说了,#{}会进行预编译,检测到该sql片段是个字符串,就会加上引号,即'sql片段',这样就是字符串了而不是sql,执行会报错。

可以说,${}让以前在java代码中动态拼接sql的麻烦时代,在mybatis中成为了可能,mybatis想的真周到啊,嘿嘿。

依据$适用于传入sql片段的情况,我们可以将数据表字段名、表名、数据库名等传入,来进行sql拼接,如:

2.2 $符号的应用场景:

比如:select {columns} from {tableName},假设columns变量的值是userName,age 。tableName变量的值是user,那么该sql语句就会被解析成:select userName,age from user

比如有这样一个需求:对于后台管理系统的table列,想让用户选择展示哪些列,不展示哪些列,或者根据权限来分配不同的用户可以看到哪些列?

这个需求正好用到了符号,我们可以将每个用户对于一个table能看到哪些列,存储到数据表中,对该数据表进行crud,每次展示列表的时候,先根据用户ID和table类型,到数据表中查询该用户对该table能看到的列名(字段名)列表,然后将该字段名列表作为sql片段传入mybatis进行查询,此时就必须得用到符号,而不能用#符号了。

2.3 $符号的sql注入问题:

用${}时要特别注意sql注入的风险,如果该sql片段是根据用户的输入拼接的,要注意检查sql注入的问题,防止数据的泄露与丢失!

反面案例:

假如这个sql:select * from {tableName} where name = {name}

如果tableName的值为 user; delete user; --,该sql最终解析成select * from user; delete user; -- where name = xxx,结果查询了整张表,然后把user表给删了...

多对一处理(association)

  • 多个学生对一个老师

  • 对于学生这边,关联:多个学生,关联一个老师[多对一]

  • 对于老师而言,集合,一个老师有多个学生【一对多】

测试环境搭建

1.新建实体类

2.建立Mapper接口,建立Mapper.xml

3.在核心配置文件中绑定注册我们的Mapper映射文件

4.测试

按照查询嵌套处理

<mapper namespace="com.lyc.mapper.StudentMapper">
<!--    思路:
1.查询所有的学生信息
2.根据查询出来的学生的tid查询到老师信息
 -->
    <select id="getStudent" resultMap="student-teacher">
        select *
        from mybatis.student;
    </select>
    <resultMap id="student-teacher" type="com.lyc.pojo.Student">
        <result property="id" column="id"/>
        <result property="name" column="name"/>
<!--        复杂的属性,我们需要单独处理
            对象:association
            集合:collection-->
        <association property="teacher" column="tid" javaType="com.lyc.pojo.Teacher" select="getTeacher"/>
    </resultMap>
    <select id="getTeacher" resultType="com.lyc.pojo.Teacher">
        select * from mybatis.teacher where id = #{id}
    </select>
</mapper>

按照结果嵌套查询

<!--    按照结果嵌套处理-->
    <select id="getStudent2" resultMap="student-teacher2">
        select student.id,student.name,teacher.name as teacherName
        from mybatis.student,mybatis.teacher
        where student.tid = teacher.id
    </select>
    <resultMap id="student-teacher2" type="com.lyc.pojo.Student">
        <result property="id" column="id"/>
        <result property="name" column="name"/>
        <association property="teacher" javaType="com.lyc.pojo.Teacher">
            <result property="name" column="teacherName"/>
        </association>
    </resultMap>

注:单表查询时,当执行到tid时去调用映射,映射又去子查询,所以#{}里面自动填充了cloum的值,所以#{}的值可以随便添,记住,是单表查询时

回顾MySQL多对一查询方式:

  • 子查询

  • 联表查询

一对多处理(collection)

环境搭建

实体类:

public class Student {
    private int id;
    private String name;
    //学生要关联一个老师
   private int tid;
}
public class Teacher {
    private int id;
    private String name;
    //一个老师有多个老师
    private List<Student> students;
}

按照查询嵌套处理

   <select id="selectTeacher2" resultMap="teacher-student2">
    select * from mybatis.teacher where id = #{tid};
    </select>
    <resultMap id="teacher-student2" type="com.lyc.pojo.Teacher">
        <collection property="students" javaType="ArrayList" ofType="com.lyc.pojo.Student" select="getStudent" column="id"/>
    </resultMap>
    <select id="getStudent" resultType="com.lyc.pojo.Student">
        select * from mybatis.student where tid = #{tid}
    </select>
</mapper>

按照结果嵌套查询

<select id="selectTeacher" resultMap="teacher-student">
    select s.id sid,s.name sname,t.id tid,t.name tname
    from mybatis.teacher t
        join mybatis.student s on
            t.id = s.tid and t.id = #{tid};
</select>

<resultMap id="teacher-student" type="com.lyc.pojo.Teacher">
    <result property="id" column="tid"/>
    <result property="name" column="tname"/>
    <!--        复杂的属性,我们需要单独处理
        对象:association
        集合:collection
        javaType:
        指定属性的类型,集合中的泛型信息,我们使用ofType获取-->
    <collection property="students" ofType="com.lyc.pojo.Student">
        <result property="id" column="sid"/>
        <result property="name" column="sname"/>
        <result property="tid" column="tid"/>
    </collection>
</resultMap>

小结

  1. 关联 association

  2. 集合:collection

  3. javaType & ofype

    1. javaType用来指定实体类中属性的类型

    2. ofType 用来指定映射到List或者集合中的实体类,反省中的约束类型

注:

  • 保证SQL的可读性,精良保证通俗易懂

  • 注意一对多和多对一中 属性名和字段名的问题

  • 如果问题不好排查,可以使用日志,建议使用log4j

动态SQL

什么是动态SQL?

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

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

if
choose (when, otherwise)
trim (where, set)
foreach

搭建环境

create table blog(
    id varchar(50) not null comment '博客id',
    title 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;

注:UUID通用唯一标识符

IF

 <select id="queryBlogIF" resultType="com.lyc.pojo.Blog" parameterType="map">
 select * from mybatis.blog where 1=1
 <if test="author != null and author != ''">
     and author = #{author}
 </if>
<if test="title != null and title != ''">
    and title = #{title}
</if>
 </select>

choose、when、otherwise

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

他的<when>标签就相当于case,他的<otherwise>标签就相当于所有条件都不符合最后才用的SQL语句,他的<when>标签是按顺序执行的,只要第一个执行,其他全部作废,只适合单元素查询

<select id="queryBlogChoose" resultType="com.lyc.pojo.Blog" parameterType="map">
    select * from mybatis.blog
    <where>
        <choose>
            <when test="author != null">
                author = #{author}
            </when>
            <when test="title != null">
               title = #{title}
            </when>
            <otherwise>
                 views = #{views}
            </otherwise>
        </choose>
    </where>
</select>

trim、where、set

where 元素只会在子元素返回任何内容的情况下才插入 “WHERE” 子句。而且,若子句的开头为 “AND” 或 “OR”,where 元素也会将它们去除。

<select id="queryBlogIF" resultType="com.lyc.pojo.Blog" parameterType="map">
select * from mybatis.blog
    <where>
        <if test="author != null and author != ''">
        and author = #{author}
    </if>
        <if test="title != null and title != ''">
        and title = #{title}
        </if>
    </where>
</select>

set 元素会动态地在行首插入 SET 关键字,并会删掉额外的逗号(这些逗号是在使用条件语句给列赋值时引入的)

<update id="updateBlog">
    update mybatis.blog
    <set>
        <if test="title != null">
            title = #{title},
        </if>
        <if test="author != null">
            author = #{author}
        </if>
    </set>
    where id = #{id}
</update>

所谓的动态SQL本质还是SQL语句,只是我们可以在SQL层面执行一个逻辑代码

主要使用if where set比较多

foreach(遍历)

动态 SQL 的另一个常见使用场景是对集合进行遍历(尤其是在构建 IN 条件语句的时候),允许你指定一个集合,声明可以在元素体内使用的集合项(item)和索引(index)变量。它也允许你指定开头与结尾的字符串以及集合项迭代之间的分隔符。这个元素也不会错误地添加多余的分隔符

select * from user where 1=1 and (id = 2 or id = 1 or id = 3)

<foreach item="item" index="index" collection="list"
      open="(" separator="," close=")">
        #{item}
  </foreach>

案例:

<!--  我们现在传递一个万能的map,这map中可以存在一个集合  -->
<!--    open:开头 close:结尾 separator:分隔符 是为了拼接SQL语句-->
    <select id="queryBlogForeach" resultType="com.lyc.pojo.Blog" parameterType="map">
# select * from mybatis.blog where true and (id = 1 or id = 2 or id = 3);
        select * from mybatis.blog
        <where>
            <foreach collection="ids" item="id" open=" (" close=")" separator="or">
                id = #{id}
            </foreach>
        </where>
    </select>

SQL片段

有的时候,我们可能会将一些公共的部分抽取出来,方便复用!

1.使用SQL标签抽取公共的部分、

<sql id="if-title-author">
    <if test="title != null">
        title = #{title},
    </if>
    <if test="author != null">
        author = #{author}
    </if>
</sql>

2.在需要使用的地方使用include标签引出即可

<update id="updateBlog">
    update mybatis.blog
    <set>
        <include refid="if-title-author"/>
    </set>
    where id = #{id}
</update>

注意事项:最好基于单表来定义SQL片段

在公共部分中不要存在where标签

动态SQL就是在拼接SQL语句,我只要保证SQL的正确性,按照SQL的格式,去排列组合就可以了

建议:

  • 现在Mysql中写出完整的SQL,在对应的去修改成为我们的动态SQL实现通用即可

缓存

什么是缓存?

  • 存在内存中的临时数据

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

  • 经常查询且不经常改变的数据适合使用缓存

为什么使用缓存?

缓存(即cache)的作用是为了减去数据库的压力,提高数据库的性能。缓存实现的原理是从数据库中查询出来的对象在使用完后不销毁,而是存储在内存(缓存)中,当再次需要获取该对象时,直接从内存中获取,不再向数据库执行select语句,减少对数据库的查询次数,提高了数据库的性能。缓存是使用Map集合存储数据。

可用的清除策略有:

  • LRU – 最近最少使用:移除最长时间不被使用的对象。

  • FIFO – 先进先出:按对象进入缓存的顺序来移除它们。

Mybatis缓存

MyBatis有一级缓存和二级缓存之分。

一级缓存的作用域是同一个SqlSession,在同一个SqlSession中两次执行相同的sql语句,第一次执行完毕会将数据库查询的数据写到缓存(内存),第二次会从缓存中获取数据而不进行数据库查询,大大提高了查询效率。当一个SqlSession结束后该SqlSession中的一级缓存也就不存在了。MyBtais默认启动以及缓存。

二级缓存是多个SqlSession共享的,其作用域是mapper的同一个namespace,不同的sqlSession两次执行相同namespace下的sql语句且向sql中传递的参数也相同时,第一次执行完毕会将数据库中查询到的数据写到缓存(内存),第二次会直接从缓存中获取,从而提高了查询效率。MyBatis默认不开启二级缓存,需要在MyBtais全局配置文件中进行setting配置开启二级缓存。

为了提高扩展性,mybatis定义了缓存接口Cache。我们可以通过实现Cache接口来定义二级缓存

  • 映射语句文件中的所有 select 语句的结果将会被缓存。

  • 映射语句文件中的所有 insert、update 和 delete 语句会刷新缓存。

  • 缓存会使用最近最少使用算法(LRU, Least Recently Used)算法来清除不需要的缓存。

  • 缓存不会定时进行刷新(也就是说,没有刷新间隔)。

一级缓存

MyBatis默认开启一级缓存,一级缓存只是相对于同一个SqlSession而言。所以在参数个sql完全一致的情况下,我们使用同一个SqlSession对象调用一个Mapper方法,往往只执行一次sql,使用SqlSession第一次查询后,MyBatis会将其放在缓存中,之后再查询时若没有缓存失效或超时,SqlSession都会取出当前缓存的数据,不会再发送sql到数据库。

测试步骤:

1.开启日志!

2.测试在一个Session中查询两次相同的记录

3.查看日志输出

缓存失效的情况:

1.查询不同的实体

2.增删改操作。可能会改变原来的数据,所以必定刷新缓存

3.查询不同的mapper

4.手动清理缓存

  SqlSession sqlSession = MybatisUtils.getsqlSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        User user = mapper.queryUserById(1);
        System.out.println(user);
        sqlSession.clearCache();//手动清理缓存
        System.out.println("=========");
        User user2 = mapper.queryUserById(1);
        System.out.println(user2);
        System.out.println(user == user2);

小结:一级缓存默认开启,作用域为同一个Sqlsession,只在一次SQLsession中有效,也就是拿到连接和关闭连接之间

一级缓存就是一个map

二级缓存

二级缓存的作用域是SqlSessionFactory级别,整个应用程序只有一个。二级缓存区域是根据mapper的namespace划分的,相同的namespace的mapper查询的数据缓存在同一个区域,如果使用mapper代理方法,每一个mapper的namespace都不同,此时可以理解为二级缓存区域是根据mapper进行划分的。

每次查询都会先从缓存区域查找,如果找不到则从数据库进行查询,并将查询到的数据写入缓存。MyBtais内部缓存使用HashMap,key为hashCode+sqlid+sql语句,value为从查询出来映射生成的java对象。SqlSession执行任何一个update(修改)、delete(删除)、insert(新增)操作commit提交后都会清空缓存区域,防止脏读。

工作机制

  • 一个会话查询一条数据,这个数据就会被放在当前会话的一级缓存中

  • 如果当前会话关闭了,这个会话的一级缓存就没了,但是我们想要的是,会话关闭了,一级缓存的数据会被保存在二级缓存中

  • 新的会话查询信息,就可以从二级缓存中获取内容

  • 不同的mapper查出的数据会放在自己对应的缓存中

步骤:

1.开启全局缓存

<!--        开启全局缓存-->
        <setting name="cacheEnabled" value="true"/>

2.在使用二级缓存的Mapper中开启

<cache/>

也可以自定义一些参数

<cache
        eviction="FIFO"
        flushInterval="60000"
        size="512"
        readOnly="true"/>
        //创建了一个 FIFO 缓存,每隔 60 秒刷新,最多可以存储结果对象或列表的 512 个引用,而且返回的对象被认为是只读的

3.测试:

1.问题:我们要将实体类序列化!否则就会报错

org.apache.ibatis.cache.CacheException:

解决方式:在实体类实现接口: Serializable

public class User implements Serializable 

小结:

  • 只要开启了二级缓存,在同一个Mapper下有效

  • 所有的数据都会先放在一级缓存中

  • 只有当会话提交,或关闭的时候·,才会提交到二级缓存中

扩充:在正式业务中,如何保证多个数据库数据一致?使用主从复制

缓存只是读,写还是数据库,为读写分离,提高数据库性能

自定义缓存-ehcache

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

要在程序中使用ehcache,先要导包

<dependencies>
    <dependency>
        <groupId>org.mybatis.caches</groupId>
        <artifactId>mybatis-ehcache</artifactId>
        <version>1.2.3</version>
    </dependency>
</dependencies>

完结撒花!!!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值