MyBatis原理及搭建教程

1. MyBatis

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

持久化:持久化就是将程序的数据在持久状态和瞬时状态转化的过程。

持久层

Dao层、Service层、Controller层

1. 第一个MyBatis程序

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

2.1 导入Mybatis
<!--导入依赖-->
    <dependencies>
        <!--mysql驱动-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.41</version>
        </dependency>
        <!--mybatis-->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.5.2</version>
        </dependency>
        <!--junit-->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>
    </dependencies>

2.2 编写mybatis的核心配置文件

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<!--configuration核心配置文件-->
<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="123456"/>
            </dataSource>
        </environment>
    </environments>
</configuration>

2.3 编写mybatis工具类

//sqlSessionFactory --> sqlSession相当于jdbc里面的preStatement
public class MybatisUtils {
    //提升作用域
    private static SqlSessionFactory sqlSessionFactory;
    static{
        try {
            //使用mybatis工具类获取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 命令所需的所有方法。你可以通过 SqlSession 实例来直接执行已映射的 SQL 语句
    public static SqlSession getSqlSession(){
        return sqlSessionFactory.openSession();
    }
}
3 编写代码

实体类

package com.jia.pojo;
//实体类
public class User1 {
    private int id;
    private String name;
    private String pwd;

    public User1() {
    }

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

    public int getId() {
        return id;
    }

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

    public String getName() {
        return name;
    }

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

    public String getPwd() {
        return pwd;
    }

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

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


Dao/Mapper层

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

接口实现类

<?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接口-->
<mapper namespace="com.jia.dao.UserDao">
    <select id="getUserList" resultType="com.jia.pojo.User1">
        select * from db1.user1
    </select>
</mapper>
测试

注意

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

maven资源过滤问题,由于配置文件在resources中,在maven项目中,可能导致文件导出失败。

<!--在build中配置resources,来防止我们资源导出失败的问题-->
    <build>
        <resources>
            <resource>
                <directory>src/main/java</directory>
                <includes>
                    <include>**/*.properties</include>
                    <include>**/*.xml</include>
                </includes>
                <filtering>true</filtering>
            </resource>
            <resource>
                <directory>src/main/resources</directory>
                <includes>
                    <include>**/*.properties</include>
                    <include>**/*.xml</include>
                </includes>
                <filtering>true</filtering>
            </resource>
        </resources>
    </build>

Junit测试

public class UserDaoTest {
    @Test
    public void test(){
        //获得SqlSession对象
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        try{
            //方式一:getMapper
        /*UserDao mapper = sqlSession.getMapper(UserDao.class);
        List<User1> userList = mapper.getUserList();*/

            //方式二:不推荐使用
            List<User1> userList = sqlSession.selectList("com.jia.dao.UserDao.getUserList");
            for (User1 user1 : userList) {
                System.out.println(user1);
            }
        }catch (Exception e){
            System.out.println(e.getMessage());
        }finally {
            //关闭SqlSession
            sqlSession.close();
        }
    }
}
SqlSessionFactoryBuilder

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

SqlSessionFactory

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

SqlSession

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

2. MyBatis实现赠改查

namespace中的包名要和Mapper接口的包名一致

select:选择、查询语句:

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

resultType:Sql语句执行的返回值类型

parameterType:参数类型

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接口-->
<mapper namespace="com.jia.dao.UserMapper">
    <select id="getUserList" resultType="com.jia.pojo.User1">
        select * from db1.user1
    </select>
    <select id="getUserById" resultType="com.jia.pojo.User1" parameterType="int">
        select * from db1.user1 where id=#{id}
    </select>
    <select id="getUserById2" resultType="com.jia.pojo.User1" parameterType="map">
        select * from db1.user1 where id=#{id}
    </select>
    <!--对象中的属性可以直接取出来-->
    <insert id="insertUser" parameterType="com.jia.pojo.User1">
        insert into db1.user1(id,name,pwd) values (#{id},#{name},#{pwd})
    </insert>
    <update id="updataUser" parameterType="com.jia.pojo.User1" >
        update db1.user1 set name=#{name},pwd=#{pwd} where id=#{id}
    </update>
    <delete id="deleteUser" parameterType="com.jia.pojo.User1">
        delete from db1.user1 where id=#{id}
    </delete>

</mapper>

UserMapper.java

package com.jia.dao;

import com.jia.pojo.User1;

import java.util.List;
import java.util.Map;

public interface UserMapper {
    //获取全部用户
    List<User1> getUserList();
    //根据id查询用户
    User1 getUserById(int id);
    User1 getUserById2(Map<String,Object> map);
    //插入一个用户的数据
    int insertUser(User1 user1);
    //修改用户
    int updataUser(User1 user1);
    //删除用户
    int deleteUser(int id);
}

UserDaoTest

package com.jia.dao;

import com.jia.pojo.User1;
import com.jia.utils.MybatisUtils;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;

import java.util.HashMap;
import java.util.List;

public class UserDaoTest {
    @Test
    public void test(){
        //获得SqlSession对象
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        try{
            //方式一:getMapper
        /*UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        List<User1> userList = mapper.getUserList();*/

            //方式二:不推荐使用
            List<User1> userList = sqlSession.selectList("com.jia.dao.UserMapper.getUserList");
            for (User1 user1 : userList) {
                System.out.println(user1);
            }
        }catch (Exception e){
            System.out.println(e.getMessage());
        }finally {
            //关闭SqlSession
            sqlSession.close();
        }
    }
    @Test
    public void test2(){
        //获得SqlSession对象
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        try{
            //方式一:getMapper
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        User1 user = mapper.getUserById(2);
            System.out.println(user);
        }catch (Exception e){
            System.out.println(e.getMessage());
        }finally {
            //关闭SqlSession
            sqlSession.close();
        }
    }
    //万能的map
    @Test
    public void test6(){
        //获得SqlSession对象
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        try{
            //方式一:getMapper
            UserMapper mapper = sqlSession.getMapper(UserMapper.class);
            HashMap<String, Object> map = new HashMap<>();
            map.put("id",1);
            User1 user = mapper.getUserById2(map);
            System.out.println(user);
        }catch (Exception e){
            System.out.println(e.getMessage());
        }finally {
            //关闭SqlSession
            sqlSession.close();
        }
    }
    //增删改需要提交事务
    @Test
    public void test3(){
        //获得SqlSession对象
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        try{
            //方式一:getMapper
            UserMapper mapper = sqlSession.getMapper(UserMapper.class);
            int count=mapper.insertUser(new User1(3,"王五流","323232"));
            System.out.println(count);
            //提交事务
            sqlSession.commit();
        }catch (Exception e){
            System.out.println(e.getMessage());
        }finally {
            //关闭SqlSession
            sqlSession.close();
        }
    }
    //增删改需要提交事务
    @Test
    public void test4(){
        //获得SqlSession对象
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        try{
            //方式一:getMapper
            UserMapper mapper = sqlSession.getMapper(UserMapper.class);
            int count=mapper.updataUser(new User1(3,"刘辟","3888888"));
            System.out.println(count);
            //提交事务
            sqlSession.commit();
        }catch (Exception e){
            System.out.println(e.getMessage());
        }finally {
            //关闭SqlSession
            sqlSession.close();
        }
    }
    @Test
    public void test5(){
        //获得SqlSession对象
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        try{
            //方式一:getMapper
            UserMapper mapper = sqlSession.getMapper(UserMapper.class);
            int count=mapper.deleteUser(2);
            System.out.println(count);
            //提交事务
            sqlSession.commit();
        }catch (Exception e){
            System.out.println(e.getMessage());
        }finally {
            //关闭SqlSession
            sqlSession.close();
        }
    }


}

User1

//实体类
public class User1 {
    private int id;
    private String name;
    private String pwd;
//生成getter和setter方法

万能的Map

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

//Mapper.xml
<select id="getUserById2" resultType="com.jia.pojo.User1" parameterType="map">
        select * from db1.user1 where id=#{id}
    </select>
//Controller层
HashMap<String, Object> map = new HashMap<>();
            map.put("id",1);
            User1 user = mapper.getUserById2(map);

3. 配置解析

MyBatis 的配置文件包含了会深深影响 MyBatis 行为的设置和属性信息

  1. 环境配置(environments)

    MyBatis可以配置成适应多种环境

    尽管可以配置多个环境,但每个SqlSessionFactory实例只能选择一种环境

    MyBatis默认事务管理器就是JDBC,连接池LPOOLED

  2. 属性(properties)

    这些属性可以在外部进行配置,并可以进行动态替换。

    你既可以在典型的 Java 属性文件中配置这些属性,也可以在 properties 元素的子元素中设置

    编写一个配置文件(db.properties)

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

    在配置文件进行映入

    <!--configuration核心配置文件-->
    <configuration>
        <!--映入外部配置文件,存在顺序关系哦-->
        <properties resource="db.properties"/>
        可以使用这样的方式进行配置
        <property name="username" value="root" />
        <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>
        <!--每一个Mapper.xml都需要在Mybatis核心配置文件中注册-->
        <mappers>
            <mapper resource="com/jia/dao/UserMapper.xml" />
        </mappers>
    </configuration>
    
  3. 类型别名

    类型别名可为 Java 类型设置一个缩写名字。 它仅用于 XML 配置,意在降低冗余的全限定类名书写

    方式一:给实体类写别名的方式

    <typeAliases>
      <typeAlias alias="Author" type="domain.blog.Author"/>
    </typeAliases>
    

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

    ​ 扫描包的情况下,可以使用注解进行起别名

    //实体类
    @Alias("hello")
    public class User1 {
        private int id;
        private String name;
        private String pwd;
    
    <typeAliases>
      <package name="com.jia.pojo"/>
    </typeAliases>
    

4.设置(setting)

MyBatis 中极为重要的调整设置,它们会改变 MyBatis 的运行时行为。

设置名描述有效值默认值
cacheEnabled全局性地开启或关闭所有映射器配置文件中已配置的任何缓存。true | falsetrue
lazyLoadingEnabled延迟加载的全局开关。当开启时,所有关联对象都会延迟加载。 特定关联关系中可通过设置 fetchType 属性来覆盖该项的开关状态。true | falsefalse
logImpl指定 MyBatis 所用日志的具体实现,未指定时将自动查找。SLF4J | LOG4J | LOG4J2 | JDK_LOGGING | COMMONS_LOGGING | STDOUT_LOGGING | NO_LOGGING未设置
mapUnderscoreToCamelCase是否开启驼峰命名自动映射,即从经典数据库列名 A_COLUMN 映射到经典 Java 属性名 aColumn。true | falseFalse

5.其他配置

6.映射器(mappers)

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

方式一 推荐

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

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

<!--每一个Mapper.xml都需要在Mybatis核心配置文件中注册-->
<mappers>
    <mapper class="com.jia.dao.UserMapper" />
</mappers>

注意:

  1. 接口必须和他的Mapper配置文件必须同名
  2. 接口和它的Mapper配置文件必须在同一个包下

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

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

4. 作用域(Scope)和生命周期

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

SqlSessionFactoryBuilder
  • 一旦创建了SqlSessionFactory,就不再需要它了
  • 局部变量
SqlSessionFactory:
  • 可以理解为数据库连接池
  • SqlSessionFactory 一旦被创建就应该在应用的运行期间一直存在,没有任何理由丢弃它或重新创建另一个实例
  • SqlSessionFactory 的最佳作用域是应用作用域
  • 最简单的就是使用单例模式或者静态单例模式。
SqlSession
  • 可以理解为连接到连接池的一个请求
  • 每个线程都应该有它自己的 SqlSession 实例。SqlSession 的实例不是线程安全的,因此是不能被共享的,所以它的最佳的作用域是请求或方法作用域。
  • 用完之后需要赶紧关闭,否则资源被占用

5. ResultMap结果集映射(解决属性名和字段名不一致的问题)

java实体类和数据库中字段名不一致

//实体类
public class User1 {
    private int id;
    private String name;
    private String pwd;

数据库中的字段名

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AIJyIodn-1640586170002)(E:\Typroa笔记\MyBatis.assets\image-20211027225554748.png)]

查询的结果:

User1{id=1, name='张三', pwd='null'}
User1{id=3, name='刘辟', pwd='null'}

不一致返回的内容为null

mapper.xml运行原理:

<mapper namespace="com.jia.dao.UserMapper">
    <select id="getUserList" resultType="user1">
        select * from db1.user1
    //可以理解为  其中pwd和实体类中的password不对应
    //类型处理器 处理为:
    select id,name,pwd from db1.user1
    </select>
</mapper>

解决方法:

  1. 起别名(暴力)

    ​ select id,name,pwd as password from db1.user1 在mapoer.xml中直接起别名和实体类一致

  2. ResultMap

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dvVen7Ha-1640586170004)(E:\Typroa笔记\MyBatis.assets\image-20211027230722227.png)]

    注意:返回类型设置为resultMap

resultMap 元素是 MyBatis 中最重要最强大的元素。

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

ResultMap 的优秀之处——你完全可以不用显式地配置它们

实体类和数据库字段名不一致的地方只需要映射一下就行

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PAJlpJw8-1640586170005)(E:\Typroa笔记\MyBatis.assets\image-20211027231459480.png)]

6. 日志

6.1 日志工厂

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

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

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

注意顺序哦

The content of element type "configuration" must match "(properties?,settings?,typeAliases?,typeHandlers?,objectFactory?,objectWrapperFactory?,reflectorFactory?,plugins?,environments?,databaseIdProvider?,mappers?)".
<settings>
    <!--标准的日志工厂实现-->
    <!--<setting name="logImpl" value="STDOUT_LOGGING"/>-->
    <setting name="logImpl" value="LOG4J"/>
</settings>

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nCWXrBVD-1640586170006)(E:\Typroa笔记\MyBatis.assets\image-20211028214910586.png)]

6.2 Log4j

Log4j是Apache的一个开源项目,通过使用Log4j,我们可以控制日志信息输送的目的地是控制台、文件、GUI组件。

我们也可以控制每一条日志的输出格式;通过定义每一条日志信息的级别,我们能够更加细致地控制日志的生成过程。

可以通过一个配置文件来灵活地进行配置,而不需要修改应用的代码。

  1. 先导入log4j
<!-- https://mvnrepository.com/artifact/log4j/log4j -->
<dependency>
    <groupId>log4j</groupId>
    <artifactId>log4j</artifactId>
    <version>1.2.17</version>
</dependency>
   2. log4j.properties
#将等级为DEBUG的日志信息输出到console和file这两个目的地,console和file的定义在下面的代码
log4j.rootLogger=DEBUG,console,file

#控制台输出的相关设置
log4j.appender.console = org.apache.log4j.ConsoleAppender
log4j.appender.console.Target = System.out
log4j.appender.console.Threshold=DEBUG
log4j.appender.console.layout = org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern=[%c]-%m%n

#文件输出的相关设置
log4j.appender.file = org.apache.log4j.RollingFileAppender
log4j.appender.file.File=./log/kuang.log
log4j.appender.file.MaxFileSize=10mb
log4j.appender.file.Threshold=DEBUG
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=[%p][%d{yy-MM-dd}][%c]%m%n

#日志输出级别
log4j.logger.org.mybatis=DEBUG
log4j.logger.java.sql=DEBUG
log4j.logger.java.sql.Statement=DEBUG
log4j.logger.java.sql.ResultSet=DEBUG
log4j.logger.java.sql.PreparedStatement=DEBUG
  1. log4j的使用
<settings>
    <!--标准的日志工厂实现-->
    <!--<setting name="logImpl" value="STDOUT_LOGGING"/>-->
    <setting name="logImpl" value="LOG4J"/>
</settings>
  1. 直接运行

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jxQsns66-1640586170008)(E:\Typroa笔记\MyBatis.assets\image-20211028221539287.png)]

简单使用:

  1. 在要使用Log4j的类中,导入包:import org.apache.log4j.Logger
  2. 日志对象,参数为当前类的class
static Logger logger = Logger.getLogger(UserDaoTest.class);
  1. 日志级别
[com.jia.dao.UserDaoTest]-info...
[com.jia.dao.UserDaoTest]-debug.....
[com.jia.dao.UserDaoTest]-error....

7. 分页

思考: 为什么要分页?

  • 减少数据的处理量

使用Limint分页:

语法: SELECT * FROM user LIMIT startIndex,pageSize;
select * from user limit 3;  #[0,n]

使用Mybatis实现分页,核心SQL

  1. 接口

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

    <!--分页-->
    <select id="getUserByLimit" parameterType="map" resultType="user1">
        select * from db1.user1 limit #{startIndex},#{pageSize}
    </select>
    
  3. 测试

@Test
    public void getUserByLimit(){
        //获得SqlSession对象
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        try{
            UserMapper mapper = sqlSession.getMapper(UserMapper.class);
            HashMap<String, Integer> map = new HashMap<>();
            map.put("startIndex", 0);
            map.put("pageSize", 1);
            List<User1> userByLimit=mapper.getUserByLimit(map);
            for (User1 user1 : userByLimit) {
                System.out.println(user1);
            }
        }catch (Exception e){
            System.out.println(e.getMessage());
        }finally {
            //关闭SqlSession
            sqlSession.close();
        }
    }
7.2 RowBounds分页
  1. 接口

    //分页2 rowbounds
    List<User1> getUserByRowBounds();
    
  2. Mapper.xml

    <!--分页 RowBounds-->
        <select id="getUserByRowBounds"  resultType="user1">
            select * from db1.user1
        </select>
    
  3. 测试

     @Test
        public void getUserByRowBounds(){
            //获得SqlSession对象
            SqlSession sqlSession = MybatisUtils.getSqlSession();
            //RowBounds实现
            RowBounds rowBounds = new RowBounds(0, 1);
    
            try{
                List<User1> userList = sqlSession.selectList("com.jia.dao.UserMapper.getUserByRowBounds",null,rowBounds);
                for (User1 user1 : userList) {
                    System.out.println(user1);
                }
            }catch (Exception e){
                System.out.println(e.getMessage());
            }finally {
                //关闭SqlSession
                sqlSession.close();
            }
        }
    
7.3 Mybatis分页插件

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qyH84Aem-1640586170010)(E:\Typroa笔记\MyBatis.assets\image-20211031110723082.png)]

8. 使用注解开发

  1. 注解在接口上实现

    public interface UserMapper {
        @Select("select * from db1.user1")
        List<User1> getUsers();
    }
    
  2. 需要在核心配置文件中绑定接口

    <!--绑定接口-->
    <mappers>
        <mapper class="com.jia.dao.UserMapper"/>
    </mappers>
    
  3. 测试

本质 :反射机制实现

底层 :动态代理

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xkdSTWzM-1640586170012)(E:\Typroa笔记\MyBatis.assets\image-20211031114148179.png)]

8.1 Mybatis执行流程解析
  1. Resources获取加载全局配置文件
  2. 实例化SqlSessionFactoryBuilder构造器
  3. 解析配置文件流XML ConfigBuilder
  4. Configuration所有的配置信息
  5. SqlSessionFactory实例化
  6. transactional事务管理
  7. 创建executor执行器
  8. 创建sqlSession
  9. 实现CRUD
  10. 是否执行成功 否 返回第6步
  11. 提交事务
  12. 关闭
8.3 注解增删改查

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

/既然有了 SqlSessionFactory,顾名思义,我们可以从中获得 SqlSession 的实例。
    // SqlSession 提供了在数据库执行 SQL 命令所需的所有方法。你可以通过 SqlSession 实例来直接执行已映射的 SQL 语句
    public static SqlSession getSqlSession(){
        return sqlSessionFactory.openSession(true);
    }

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SvxQuJ7L-1640586170013)(E:\Typroa笔记\MyBatis.assets\image-20211031130440660.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-edyI0hyq-1640586170014)(E:\Typroa笔记\MyBatis.assets\image-20211031130512557.png)]

测试类:

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

关于@Param() 注解

  • 基本类型的参数或者String类型,需要加上
  • 引用类型不需要叫
  • 如果只有一个基本类型的化,可以忽略,但是建议加上
  • 我们在SQL中引用的就是我们这里的@Param()中设定的属性名

#{} ${}的区别

#{}和${}这两个语法是为了动态传递参数而存在的,是Mybatis实现动态SQL的基础,总体上他们的作用是一致的(为了动态传参),但是在编译过程、是否自动加单引号、安全性、使用场景等方面有很多不同,下面详细比较两者间的区别.

  1. #{} 占位符 :动态解析 -> 预编译 -> 执行
  2. ${} 拼接符 :动态解析 -> 编译 -> 执行

  1. ***#{}* 对应的变量*会*自动加上*单引号*

  2. ***${}* 对应的变量*不会*加上*单引号*


  3. ***#{}* *能*防止sql 注入

  4. ***${}* *不能*防止sql 注入

9.复杂环境的搭建

collection:一个复杂类型的集合
嵌套结果映射-集合本身可以是一个resultMap元素,或者从别处引用一个

association:一个复杂类型的关联:许多结果包装成这种类型

​ 嵌套结果映射:关联本身可以是一个resultMap元素,或者从别处引用一个

  1. 导入lombok(@Data 简化书写实体类getter等)
  2. 新建实体类Teacher,Student
  3. 建立Mapper接口
  4. 建立Mapper.xml
  5. 在核心配置文件中绑定注册我们的Mapper接口
  6. 测试查询能否能够成功

1导入lombok(@Data 简化书写实体类getter等)

父类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>org.example</groupId>
 <artifactId>com.smile.mybatis</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-anno</module>
     <module>mybatis-05-multiple</module>
 </modules>

 <!--导入依赖-->
 <dependencies>
     <!--mysql驱动-->
     <dependency>
         <groupId>mysql</groupId>
         <artifactId>mysql-connector-java</artifactId>
         <version>8.0.25</version>
     </dependency>
     <!--mybatis-->
     <dependency>
         <groupId>org.mybatis</groupId>
         <artifactId>mybatis</artifactId>
         <version>3.5.2</version>
     </dependency>
     <!--junit-->
     <dependency>
         <groupId>junit</groupId>
         <artifactId>junit</artifactId>
         <version>4.12</version>
         <scope>test</scope>
     </dependency>
     <!-- https://mvnrepository.com/artifact/log4j/log4j -->
     <dependency>
         <groupId>log4j</groupId>
         <artifactId>log4j</artifactId>
         <version>1.2.17</version>
     </dependency>

 </dependencies>

 <!--在build中配置resources,来防止我们资源导出失败的问题-->
 <build>
     <resources>
         <resource>
             <directory>src/main/java</directory>
             <includes>
                 <include>**/*.properties</include>
                 <include>**/*.xml</include>
             </includes>
             <filtering>false</filtering>
         </resource>
         <resource>
             <directory>src/main/resources</directory>
             <includes>
                 <include>**/*.properties</include>
                 <include>**/*.xml</include>
             </includes>
             <filtering>false</filtering>
         </resource>
     </resources>
 </build>
</project>

子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">
 <parent>
     <artifactId>com.smile.mybatis</artifactId>
     <groupId>org.example</groupId>
     <version>1.0-SNAPSHOT</version>
 </parent>
 <modelVersion>4.0.0</modelVersion>

 <artifactId>mybatis-05-multiple</artifactId>

 <dependencies>
     <dependency>
         <groupId>org.projectlombok</groupId>
         <artifactId>lombok</artifactId>
         <version>1.18.20</version>
     </dependency>
 </dependencies>
</project>

2新建实体类Teacher,Student

import lombok.Data;
/**
 * 多对一模型
 * lombok:相当于实体类中的getter和setter
 */
@Data //getter和setter
@AllArgsConstructor   //有参构造
@NoArgsConstructor   //无参构造
public class Student {
    private int id;
    private String name;
    //学生需要关联一个老师
    private Teacher teacher;
}
@Data //getter和setter
@AllArgsConstructor   //有参构造
@NoArgsConstructor   //无参构造
public class Teacher {
    private int id;
    private String name;
}

3建立Mapper接口

import com.jia.pojo.Teacher;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;

public interface TeacherMapper {
    @Select("select * from teacher where id=#{tid}")
    Teacher getTeacher(@Param("tid")int id);
}

4建立Mapper.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper  namespace="com.jia.dao.TeacherMapper">

</mapper>

5在核心配置文件中绑定注册我们的Mapper接口

<?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核心配置文件-->
<configuration>
    <!--映入外部配置文件,存在顺序关系哦-->
    <properties resource="db.properties"/>
    <settings>
        <!--标准的日志工厂实现-->
        <!--<setting name="logImpl" value="STDOUT_LOGGING"/>-->
        <setting name="logImpl" value="LOG4J"/>
    </settings>
    <!--可以给实体类写别名-->
    <typeAliases>
        <package name="com.jia.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/jia/dao/TeacherMapper.xml" />
        <mapper resource="com/jia/dao/StudentMapper.xml" />
    </mappers>
</configuration>

6测试查询能否能够成功

import org.junit.Test;
public class MyTest {
    @Test
    public void test1() {
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        TeacherMapper mapper = sqlSession.getMapper(TeacherMapper.class);
        Teacher teacher = mapper.getTeacher(1);
        System.out.println(teacher);
        sqlSession.close();
    }
    static Logger logger = Logger.getLogger(MyTest.class);
    @Test
    public void testLog4j(){
        logger.info("info...");
        logger.debug("debug.....");
        logger.error("error....");
    }
}

7工具类

package com.jia.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;

//sqlSessionFactory --> sqlSession相当于jdbc里面的preStatement
public class MybatisUtils {
    //提升作用域
    private static SqlSessionFactory sqlSessionFactory;
    static{
        try {
            //使用mybatis工具类获取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 命令所需的所有方法。你可以通过 SqlSession 实例来直接执行已映射的 SQL 语句
    public static SqlSession getSqlSession(){
        return sqlSessionFactory.openSession(true);
    }
}

9.1 多对一的处理
9.1.1 按照查询嵌套处理
<mapper  namespace="com.jia.dao.StudentMapper">
 <!--
 思路:查询所有的学生信息包括另一个表中的老师
 select student.id,student.name,teacher.name from teacher,student where teacher.id=student.id
 -->
 <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在Student中为对象,
         对象:association
         集合:collection
         javaType:对象的类型(实体类)
         select:嵌套查询
     -->
     <association property="teacher" column="tid" javaType="Teacher" select="getTeacher"/>
 </resultMap>
 <!--其实此处id=#{tid}会自动匹配,-->
 <select id="getTeacher" resultType="Teacher">
     select * from teacher where id=#{id}
 </select>
</mapper>
import lombok.Data;
/**
 * 多对一模型
 * lombok:相当于实体类中的getter和setter
 */
@Data //getter和setter
@AllArgsConstructor   //有参构造
@NoArgsConstructor   //无参构造
public class Student {
    private int id;
    private String name;
    //学生需要关联一个老师
    private Teacher teacher;
}
 @Test
    public void test3() {
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
        //teache参数对应student
        List<Student> student = mapper.getStudent();
        student.forEach(item-> System.out.println(item));
        sqlSession.close();
    }
9.1.2 按照结果嵌套处理
<!--按照结果嵌套处理:-->
    <select id="getStudent2" resultMap="StudentTeacher2">
        select student.id,
        student.name,
        teacher.name tname
        from teacher,student
        where teacher.id=student.id
    </select>
    <resultMap id="StudentTeacher2" type="Student">
        <result property="id" column="id"/>
        <result property="name" column="name"/>
        <association property="teacher" javaType="Teacher">
            <result property="name" column="tname"/>
        </association>
    </resultMap>

9.2 一对多的处理

  1. 环境搭建

    1. 实体类
    public class TeacherDouble {
        private int id;
        private String name;
             
        //一个老师拥有多个学生
        private List<Student> students;
    }
             
    @Data //getter和setter
    @AllArgsConstructor   //有参构造
    @NoArgsConstructor   //无参构造
    public class StudentSingle {
        private int id;
        private String name;
        private int tid;
    }
    
  2. 接口类

    public interface TeacherDoubleMapper {
        //获取老师
        List<TeacherDouble> getTeacher();
    
        //获取指定老师下的所有学生及老师的信息
        TeacherDouble getTeacher2(@Param("tid")int id);
        //TeacherDouble(id=1, name=贾老师, students=[StudentSingle(id=1, name=小蜜瓜, tid=1)])
        TeacherDouble getTeacher3(@Param("tid")int id);//TeacherDouble(id=0, name=贾老师, students=[StudentSingle(id=1, name=小蜜瓜, tid=1)])
    }
    
  3. Mapper.xml

    <?xml version="1.0" encoding="UTF-8" ?>
    <!DOCTYPE mapper
            PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
            "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    <mapper  namespace="com.jia.dao.TeacherDoubleMapper">
        <select id="getTeacher" resultType="com.jia.pojo.TeacherDouble">
            select * from teacher;
        </select>
        <!--按照结果查询处理-->
        <select id="getTeacher2" 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="TeacherDouble">
            <result property="id" column="tid"/>
            <result property="name" column="tname"/>
            <!--复杂的属性,我们需要单独处理,对象:association  集合使用:collection
            havaType="" 指定属性的类型
            集合中的泛型信息,我们使用ofType获取-->
            <collection property="students" ofType="StudentSingle">
                <result property="id" column="sid"/>
                <result property="name" column="sname"/>
                <result property="tid" column="tid"/>
            </collection>
        </resultMap>
        <!--按照查询嵌套处理-->
        <select id="getTeacher3" resultMap="TeacherStudent3">
            select * from teacher where id=#{tid}
        </select>
        <resultMap id="TeacherStudent3" type="TeacherDouble">
            <!--一样的可以省略不写
            <result property="id" column="id"/>-->
            <collection property="students" column="id" javaType="ArrayList"  ofType="StudentSinle" select="getStudentSingleById"/>
        </resultMap>
        <select id="getStudentSingleById" resultType="StudentSingle">
            select * from student where tid=#{tid}
        </select>
    </mapper>
    

    4数据库
    student:

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-S4MbJ1DD-1640586170015)(E:\Typroa笔记\MyBatis.assets\image-20211120173311345.png)]

    teacher:

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6Hqr9DKq-1640586170017)(E:\Typroa笔记\MyBatis.assets\image-20211120173251529.png)]

小结

  1. 关联 - association 多对一

  2. 集合-collection 一对多

  3. javaTyoe 和 ofType

    ​ javaType:用来指定实体类中属性的类型

    ​ ofType: 用来指定映射到List或者集合中的pojo类型,泛型中的约束类型

10.动态SQL

1.1 什么是动态SQL:

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

  • if
  • choose (when, otherwise)
  • trim (where, set)
  • foreach
1.1.1 if
<select id="queryBlogIf" parameterType="map" resultType="blog">
        select * from mybatis.db1 where 1=1
        <if test="title != null">
            and title = #{title}
        </if>
        <if test="content != null">
            and content = #{content}
        </if>
    </select>
 HashMap map = new HashMap();
        map.put("title","1"); 
        List<Bolg> bolgs = mapper.queryBlogIf(map);
        for (Blog blog:bolgs) {
            System.out.println(blog);
        }
1.1.2 choose、when、otherwise

不使用所有的条件,从多个条件中选择一个使用,类似于Java中的switch语句,如果没有条件,则所有都返回

<select id="findActiveBlogLike"
     resultType="Blog">
  SELECT * FROM BLOG WHERE state = ‘ACTIVE’
    <where>
  		<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>
      </where>
</select>
1.1.3 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>

where元素只会在子元素返回任何内容的情况下插入“where”子句,若子句开头为:AMD/OR,where元素会将它移除掉

自定义 trim 元素来定制 where 元素的功能

<trim prefix="WHERE" prefixOverrides="AND |OR ">
  ...
</trim>

prefixOverrides 属性会忽略通过管道符分隔的文本序列(注意此例中的空格是必要的)。上述例子会移除所有 prefixOverrides 属性中指定的内容,并且插入 prefix 属性中指定的内容。

动态set

<update id="updateAuthorIfNecessary">
  update Author
    <set>
      <if test="username != null">username=#{username},</if>
      <if test="password != null">password=#{password},</if>
      <if test="email != null">email=#{email},</if>
      <if test="bio != null">bio=#{bio}</if>
    </set>
  where id=#{id}
</update>

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

set 元素等价的自定义 trim 元素,覆盖了后缀值设置,并且自定义了前缀值

<trim prefix="SET" suffixOverrides=",">
  ...
</trim>
1.1.4 SQL片段

有点时候,我们可能会将一些功能的部分抽取出来,方便复用;

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

    <sql id="if-title-author">
            <if test="title !=null">
                title = #{title}
            </if>
            <if test="author != null">
                and author = #{author}
            </if>
        </sql>
    
  2. 在需要使用的地方使用include标签引用即可

    <select id="queryBlogIF" parameterType="map" resultType="blog">
            select * from blog
            <where>
                <include refid="if-title-author"></include>
            </where>
        </select>
    

    总体代码:

    <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 blog
            <where>
                <include refid="if-title-author"></include>
            </where>
        </select>
    

    注意事项:

    • 最好基于单表来定义SQL片段
    • SQL片段中不要存在where标签
1.1.5 Foreach

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

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

注意事项:foreach 元素的功能非常强大,它允许你指定一个集合,声明可以在元素体内使用的集合项(item)和索引(index)变量。它也允许你指定开头与结尾的字符串以及集合项迭代之间的分隔符。这个元素也不会错误地添加多余的分隔符,看它多智能!

提示 :你可以将任何可迭代对象(如 List、Set 等)、Map 对象或者数组对象作为集合参数传递给 foreach。当使用可迭代对象或者数组时,index 是当前迭代的序号,item 的值是本次迭代获取到的元素。当使用 Map 对象(或者 Map.Entry 对象的集合)时,index 是键,item 是值。

Service:

HashMap map = new HashMap();
    ArrayList<Integer> ids = new ArrayList<Integer>();
    ids.add(1);
    ids.add(2);
    ids.add(3);
    map.put("ids","ids");
    List<Blog> blogs = mapper.queryBlogForeach(map);
    for(Blog blog : blogs){
        System.out.println(blog);
    }

Mapper:

//查询第1-2-3号记录的博客  foreach测试
    List<Blog> queryBlogForeach(Map map);

mapper.xml:

<!--select * from mybatis.blog where 1=1 and (id=1 or id=2 or id=3)
    我们现在传递一个万能的map,这个map可以存在一个集合
    -->
    <select id="queryBlogForeach" resultType="blog" parameterType="map">
        select * from mybatis.blog
        <where>
            <foreach collection="ids" item="id" open = "and (" close=")" separator="or">
                id = #{id}
            </foreach>
        </where>
    </select>

动态SQL就是在拼接SQL语句,我们只要保证SQL的正确性,按照SQL的形式,排序组合就行

11.缓存

查询 : 连接数据库 ,耗资源!

​ 一次查询的结果,给他暂存在一个可以直接取到的地方! —>内存 : 缓存

我们再次查询相同数据的时候,直接走缓存,就不用走数据库了

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

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

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

经常查询并且步经常改变的数据

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

默认情况下只开启一级缓存,(SqlSession级别的缓存,也称为本地缓存)

SqlSession sqlSession = MybatisUtils.getSqlSession();
StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
//第一次取
Student student = mapper.queryStudentById(1);
System.out.println(student);
Student student1 = mapper.queryStudentById(1);
System.out.println(student1);
System.out.println(student==student1);    //true  相同情况下第二次在缓存中取

//这个区间是一级缓存

sqlSession.close();

缓存失效的情况:

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

  2. 查询不同的数据

  3. 查询不同的Mapper.xml

  4. 手动清理缓存:

    sqlSession.clearCache();//手动清理缓存 
    

小结:一级缓存默认是开启的,只在一次SqlSession中有效,也就是拿到连接到连接关闭连接这个区间段,无法关闭****

11.1.6 二级缓存
  • 二级缓存也叫全局缓存,一级缓存作用域太低了,所以出现了二级缓存
  • 基于namespace级别的缓存,一个名称空间,对应一个二级缓存
  • 工作机制:一个会话查询一条数据,这个数据就会被放在当前会话的一级缓存中,如果当前会话关闭了,一级缓存中的数据就会被保存到二级缓存中,新的会话查询信息就可以从二级缓存中读取,不同的mapper查出的数据会放到自己对应的缓存(map)中

步骤

  1. 开启全局缓存
<!--显示的开启二级缓存-->
<setting name="cacheEnabled" value="true"/>
  1. 在要使用二级缓存的Mapper中开启
<!--在当前mapper.xml中使用二级缓存-->
    <cache/>
<!--也可以自定义参数-->
<cache eviction="FIFO"
           flushInterval="60000"
           size="512"
           readOnly="true"
    />

测试代码:

public void getStudent2(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        SqlSession sqlSession2 = MybatisUtils.getSqlSession();
        StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
        StudentMapper mapper2 = sqlSession2.getMapper(StudentMapper.class);
        //第一次取
        Student student = mapper.queryStudentById(1);
        System.out.println(student);
        sqlSession.close();
        Student student2 = mapper2.queryStudentById(1);
        System.out.println(student2);
        //这个区间是一级缓存
        sqlSession2.close();
        System.out.println(student2==student);   //true
    }

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PKP4w4TP-1640586170018)(E:\Typroa笔记\MyBatis.assets\image-20211130110639753.png)]

只进行了一次查询,第二次查询保存到了二级缓存中,直接取

报错问题:,在实体类中序列化

Caused by: java.io.NotSerializableException: com.jia.pojo.Student
/**
 * 多对一模型
 * lombok:相当于实体类中的getter和setter
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Student implements Serializable {
    private int id;
    private String name;
    private String tid;
}

小结:只要开启了二级缓存,在同一个Mapper下有效、所有的数据都会先放在一级缓存中、只有当会话提交或者关闭的时候,才会提交到二级缓存中

11.2.1 缓存原理

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-27CkTRnd-1640586170019)(E:\Typroa笔记\MyBatis.assets\image-20211130112654905.png)]

11.2.2 自定义缓存-enchace

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

附录1 foreach与lambda表达式

        ArrayList<String> list = new ArrayList<>();
           list.add("b");
           list.add("a");
           list.add("d");
           list.forEach(item -> System.out.print(item));
      
           HashMap<String, String> map = new HashMap<>();
           map.put("1", "1");
           map.put("2", "2");
           map.put("3", "3");
           map.forEach((String key, String value) -> {
               System.out.println("key-->>" + key + "value--->>" + value);
           });

			List<Student> student = mapper.getStudent2();
            student.forEach(item-> {System.out.println(item);
                                System.out.println("ok");});

附录2 left join on

SELECT * FROM teacher LEFT JOIN student ON teacher.id=student.tid
SELECT * FROM student LEFT JOIN teacher ON teacher.id=student.tid
SELECT a.,b.,c.* FROM user a
LEFT JOIN relevance b ON b.userId=a.userId
LEFT JOIN signature c ON c.signId=b.signId
WHERE a.orgnizationid=‘320923199604013026’;

附录3 面试高频

  • Mysql引擎
  • InnoDB底层
  • 索引
  • 索引优化

附录4 UUID简化

import org.junit.jupiter.api.Test;
import java.util.UUID;
@SuppressWarnings("all")   //抑制警告
public class IDutils {
    public static String getId(){
        return UUID.randomUUID().toString().replace("-","");
    }
    @Test
    public void test1(){
        System.out.println(IDutils.getId());
        //1cb458e9bfaf49ee9268ae26fd9678fe
    }
}

ring name;

private String tid;

}

小结:只要开启了二级缓存,在同一个Mapper下有效、所有的数据都会先放在一级缓存中、只有当会话提交或者关闭的时候,才会提交到二级缓存中

11.2.1 缓存原理

[外链图片转存中…(img-27CkTRnd-1640586170019)]

11.2.2 自定义缓存-enchace

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

附录1 foreach与lambda表达式

        ArrayList<String> list = new ArrayList<>();
           list.add("b");
           list.add("a");
           list.add("d");
           list.forEach(item -> System.out.print(item));
      
           HashMap<String, String> map = new HashMap<>();
           map.put("1", "1");
           map.put("2", "2");
           map.put("3", "3");
           map.forEach((String key, String value) -> {
               System.out.println("key-->>" + key + "value--->>" + value);
           });

			List<Student> student = mapper.getStudent2();
            student.forEach(item-> {System.out.println(item);
                                System.out.println("ok");});

附录2 left join on

SELECT * FROM teacher LEFT JOIN student ON teacher.id=student.tid
SELECT * FROM student LEFT JOIN teacher ON teacher.id=student.tid
SELECT a.,b.,c.* FROM user a
LEFT JOIN relevance b ON b.userId=a.userId
LEFT JOIN signature c ON c.signId=b.signId
WHERE a.orgnizationid=‘320923199604013026’;

附录3 面试高频

  • Mysql引擎
  • InnoDB底层
  • 索引
  • 索引优化

附录4 UUID简化

import org.junit.jupiter.api.Test;
import java.util.UUID;
@SuppressWarnings("all")   //抑制警告
public class IDutils {
    public static String getId(){
        return UUID.randomUUID().toString().replace("-","");
    }
    @Test
    public void test1(){
        System.out.println(IDutils.getId());
        //1cb458e9bfaf49ee9268ae26fd9678fe
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
MyBatis是一个开源的持久层框架,它可以帮助开发人员简化数据库操作。如果你想学习MyBatis,下面是一个学习路线的建议: 1. 数据库基础:在学习MyBatis之前,建议先掌握数据库的基本概念和SQL语言。了解关系型数据库原理、表设计和常用的SQL语句会对学习MyBatis有很大帮助。 2. MyBatis入门:开始学习MyBatis之前,可以先了解一下MyBatis的基本概念和核心特性。阅读官方文档或者参考一些入门教程可以帮助你快速上手。 3. 配置文件:学习如何配置MyBatis的核心配置文件,包括数据源、映射文件、类型处理器等。了解不同配置项的作用和常用配置方式。 4. 映射文件:深入学习MyBatis的映射文件,了解如何使用SQL语句进行数据库操作,并学习动态SQL的使用技巧。掌握映射文件中各种标签的含义和用法。 5. 注解方式:学习使用注解方式来配置和使用MyBatis。了解常用的注解和使用方式,与映射文件相比,注解方式更加简洁和灵活。 6. 缓存机制:了解MyBatis的缓存机制,包括一级缓存和二级缓存的原理和使用方式。了解如何配置和优化缓存,提高系统的性能。 7. 整合框架:学习如何将MyBatis与其他框架进行整合,如Spring和Spring Boot。掌握整合的配置方式和常见问题的解决方法。 8. 实践项目:通过实践项目来巩固所学的知识,可以自己动手搭建一个简单的Web应用或者实现一些常见的数据库操作。在实践中不断提升自己的技能。 以上是一个大致的学习路线,你可以根据自己的实际情况和学习进度进行调整。希望对你有帮助!

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值