MyBatis 简介

MyBatis


链接:www.kuangstudy.com

MyBatis

1、简介

1.1什么是mybatis

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

如何获得Mybatis?

  • maven仓库
<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis</artifactId>
    <version>3.5.7</version>
</dependency>

  • Github
  • 查询地址: https://github.com/search?q=mybatis
  • 查询地址: https://github.com/mybatis/mybatis-3
  • 下载地址: https://github.com/mybatis/mybatis-3/releases/tag/mybatis-3.5.2
  • 中文文档: https://mybatis.org/mybatis-3/zh/index.html

1.2持久层

数据持久化

  • 持久化就是讲程序的数据在持久状态和瞬时状态转化的过程
  • 内存:断电即失
  • 数据库(jdbc),io文件持久化
  • 生活中: 冷藏
    为什么需要持久化
  • 有一些对象,不能让他丢掉
  • 内存太贵

1.3持久层

Dao层,service层,Controller层

  • 完成持久化工作的代码块
  • 层界限十分明显

1.4为什么要用Mybatis

  • 方便

  • 帮助程序员将数据存入到数据库中

  • 传统的JDBC代码太复杂,自动化操作,简化,框架

  • 不用mybatis也可以
    优点

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

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

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

  • 提供映射标签,支持对象与数据库的orm字段关系映射。

  • 提供对象关系映射标签,支持对象关系组建维护。

  • 提供xml标签,支持编写动态sql
    最重要的一点!使用的人多
    spring,springMVC,Mybatis

2、第一个Mybatis程序

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

2.1 搭建环境

2.1.1搭建数据库
CREATE TABLE `user` (
  `id` int NOT NULL,
  `name` varchar(255) DEFAULT NULL,
  `password` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
2.1.2 新建一个普通maven项目
  1. 删除src目录,作为父工程
  2. 导入maven依赖
<!--mysql依赖-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.26</version>
        </dependency>
        <!--mybatis依赖-->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.5.7</version>
        </dependency>
        <!--junit依赖-->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>

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>
    <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="root"/>
            </dataSource>
        </environment>
    </environments>
    <mappers>
        <mapper resource="org/mybatis/example/BlogMapper.xml"/>
    </mappers>
</configuration>
  • 编写mybatis工具类
public class MybatisUtils {
    private static SqlSessionFactory sqlSessionFactory = null;
    static {
        try {
            //使用mybatis第一步:获取SqlSessionFactory对象
            String resource = "org/mybatis/example/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(){
        SqlSession sqlSession = sqlSessionFactory.openSession();
        return sqlSession;
    }
}

2.3 编写代码

  • 实体类
public class User {
    private int id;
    private String name;
    private String password;

    public User() {
    }

    public User(int id, String name, String password) {
        this.id = id;
        this.name = name;
        this.password = password;
    }

    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 getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }
}
  • 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="com.yx.dao.UserDao">

    <select id="getUserList" resultType="com.yx.pojo.User">
        select * from user
    </select>

</mapper>

2.4 测试

注意点:MapperRegistry(核心配置文件中注册 mappers)

  • junit 测试
public class UserDaoTest {
    @Test
    public void test(){
        //第一步: 获得getSqlSession对象
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        //执行SQL
        UserDao mapper = sqlSession.getMapper(UserDao.class);
        List<User> userList = mapper.getUserList();
        for (User user : userList) {
            System.out.println(user);
        }
        //关闭
        sqlSession.close();
    }
}

可能会遇到的错误:

 <!--maven由于他的约定大于配置,可能遇到写的配置文件无法被导出或者生效的问题-->
    <!--在build中配置resources,来防止我们资源导出失败的问题-->
    <build>
        <resources>
            <resource>
                <directory>src/main/java</directory>
                <includes>
                    <include>**/*.xml</include>
                    <include>**/*.properties</include>
                    <include>**/*.yml</include>
                </includes>
                <filtering>false</filtering>
            </resource>
            <resource>
                <directory>src/main/resources</directory>
                <includes>
                    <include>**/*.xml</include>
                    <include>**/*.properties</include>
                    <include>**/*.yml</include>
                </includes>
                <filtering>false</filtering>
            </resource>
        </resources>
    </build>
  • 配置文件没有注册
  • 绑定接口错误
  • 方法名不对
  • 返回类型不对
  • Maven导出资源问题

3、CRUD

3.1.1 namespace

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

3.1.2 select

选择,查询语句

  • id: 就是对应的namespace中的方法名
  • resultType: sql语句执行的返回值!
  • parameterType: 参数类型
    1.写接口
//根据id查询用户
    User getUserById(int id);

2.写对应mapper文件中的sql

 <select id="getUserById" resultType="com.yx.pojo.User" parameterType="int">
        select * from user where id =#{id}
 </select>

3.测试

 @Test
    public void getUserById(){
        //第一步: 获得getSqlSession对象
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        //执行SQL
        UserDao mapper = sqlSession.getMapper(UserDao.class);
        User userById = mapper.getUserById(1);
        System.out.println(userById);
        //关闭
        sqlSession.close();
    }
3.1.3 insert
<insert id="addUser" parameterType="com.yx.pojo.User">
        insert into user (id,name,password) values(#{id},#{name},#{password})
    </insert>
3.1.4 update
<update id="updateUser" parameterType="com.yx.pojo.User">
        update user set name =#{name},password=#{password} where id=#{id}
    </update>
3.1.5 delete
<delete id="deleteUser" parameterType="int">
        delete from user where id=#{id}
    </delete>

注意点

  • 增删改需要提交事务
//增删改  需要提交事务
    @Test
    public void addUser(){
        //第一步: 获得getSqlSession对象
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        //执行SQL
        UserDao mapper = sqlSession.getMapper(UserDao.class);
        int res = mapper.addUser(new User(7, "阿萨德", "123"));
        if(res>0){
            System.out.println("插入成功");
        }
        //提交事务
        sqlSession.commit();
        //关闭
        sqlSession.close();
    }
3.1.6 模糊查询
//模糊查询
    List<User> getUserLike(String value);
<!--模糊查询-->
    <select id="getUserLike" resultType="com.yx.pojo.User">
        select * from user where name like #{value};
    </select>
   @Test
    public void getUserLike(){
        //第一步: 获得getSqlSession对象
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        //执行SQL
        UserDao mapper = sqlSession.getMapper(UserDao.class);

        List<User> zhang = mapper.getUserLike("%张%");
        for (User user : zhang) {
            System.out.println(user);
        }
        //关闭
        sqlSession.close();
    }

3.2 万能Map

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

    //根据id查询用户2
    User getUserById2(Map<String,Object> map);
    //insert一个用户2
    int addUser2(Map<String,Object> map);
	<!--万能map-->
    <!--对象中的属性,可以直接取出来    传递map的key-->
    <insert id="addUser2" parameterType="map">
        insert into user (id,name,password) values(#{userid},#{userName},#{password})
    </insert>
    <select id="getUserById2" resultType="com.yx.pojo.User" parameterType="map">
        select * from user where id =#{id} and name=#{name}
    </select>
 @Test
    public void getUserById2(){
        //第一步: 获得getSqlSession对象
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        //执行SQL
        UserDao mapper = sqlSession.getMapper(UserDao.class);
        HashMap<String,Object> map = new HashMap<>();
        map.put("id",1);
        map.put("name","张三");
        User userById2 = mapper.getUserById2(map);
        System.out.println(userById2);
        //关闭
        sqlSession.close();
    }
    @Test
    public void addUser2(){
        //第一步: 获得getSqlSession对象
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        //执行SQL
        UserDao mapper = sqlSession.getMapper(UserDao.class);
        HashMap<String,Object> map = new HashMap<>();
        map.put("userid",6);
        map.put("userName","发的发大水");
        map.put("password","123456");
        int res = mapper.addUser2(map);
        if (res>0){
            System.out.println("插入成功");
        }
        //提交事务
        sqlSession.commit();
        //关闭
        sqlSession.close();
    }

Map传递参数,直接在SQL中去除key即可!
对象出艾迪参数,直接在sql中取出对象的属性即可!
只有一个基本类型参数的情况下,可以直接在SQL中取到!
多个参数用Map,或者注解

4、 配置解析

4.1 核心配置文件

  • mybatis-config.xml
  • MyBatis的配置文件包含了会深深影响Mybatis行为的设置和属性信息
configuration(配置)
properties(属性)
settings(设置)
typeAliases(类型别名)
typeHandlers(类型处理器)
objectFactory(对象工厂)
plugins(插件)
environments(环境配置)
environment(环境变量)
transactionManager(事务管理器)
dataSource(数据源)
databaseIdProvider(数据库厂商标识)
mappers(映射器)

4.2环境配置(environments)

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

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

  • JDBC – 这个配置直接使用了 JDBC 的提交和回滚设施,它依赖从数据源获得的连接来管理事务作用域。
  • MANAGED – 这个配置几乎没做什么。
    连接池是POOLED

4.3 属性(properties)

我们可以通过properties属性来实现引用配置文件

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

4.3.1 编写一个配置文件

db.properties

driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/mybatis?useSSL=true&amp;useUnicode=true&amp;characterEncoding=UTF-8
username=root
password=root
4.3.2 在核心文件中引入

在这里插入图片描述

<!--引入外部配置文件-->
    <properties resource="db.properties">
    </properties>

4.4 类型别名(typeAliases)

  • 类型别名是为JAVA类型设置一个短的名字
  • 存在的意义用来减少类完全限定名的冗余
	<typeAliases>
        <typeAlias type="com.yx.pojo.User" alias="User"/>
    </typeAliases>

也可以制定一个包名

<typeAliases>
  <package name="com.yx.pojo"/>
</typeAliases>
@Alias("author")
public class Author {
    ...
}

在实体类比较少的时候,使用第一种方式
实体类多使用第二种
第一种可以DIY别名,第二种不行,但是可以在实体类增加注解@Alias()

4.5 设置 setting

https://mybatis.org/mybatis-3/zh/configuration.html#settings

4.6 其他配置

插件

  • MyBatis Plus
  • 通用mapper

4.7 映射器(mappers)

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

<!-- 使用相对于类路径的资源引用 -->
<mappers>
  <mapper resource="org/mybatis/builder/AuthorMapper.xml"/>
  <mapper resource="org/mybatis/builder/BlogMapper.xml"/>
  <mapper resource="org/mybatis/builder/PostMapper.xml"/>
</mappers>

方式二

<!-- 使用映射器接口实现类的完全限定类名 -->
<mappers>
  <mapper class="org.mybatis.builder.AuthorMapper"/>
  <mapper class="org.mybatis.builder.BlogMapper"/>
  <mapper class="org.mybatis.builder.PostMapper"/>
</mappers>
注意点
  • 接口和其他的Mapper配置文件必须同名
  • 接口和其他的Mapper配置文件必须在同一个包下

5、生命周期合作域

在这里插入图片描述
生命周期和作用域是至关重要的,错误使用会导致严重的并发问题
SqlSessionFactoryBuilder

  • 一旦创建了SqlSessionFactory,就不需要他了
  • 局部变量
    SqlSessionFactory
  • 可以想象为: 数据库连接池
  • SqlSessionFactory一旦被创建就应该在应用运行期间一直存在,没有任何理由丢其他护着重新创建里一个实例
  • 因此SqlSessionFactory的最佳作用域是应用作用域
  • 最简单的就是使用单例模式或者静态单例模式
    SqlSession
  • 连接到连接池的一个请求
  • SqlSession 的实例不是线程安全的,因此是不能被共享的,所以它的最佳的作用域是请求或方法作用域
  • 用完后关闭
    在这里插入图片描述
    这里面每一个Mapper就带变一个具体的业务

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

    <resultMap id="map" type="com.yx.pojo.User">
        <!--column数据库中的字段  property实体类中的字段-->
        <id property="id" column="id" />
        <result property="name" column="name"/>
        <result property="password" column="password"/>
    </resultMap>

    <select id="getUserById" resultMap="map">
        select * from user where id =#{id}
    </select>

7、日志

7.1 日志工厂

如果一个数据库操作,出现了异常,我们就需要排错,日志就是最好的助手
曾经: sout debug
现在: 日志工厂
在这里插入图片描述

  • SLF4J
  • LOG4J[掌握]
  • LOG4J2
  • JDK_LOGGING
  • COMMONS_LOGGING
  • STDOUT_LOGGING [掌握]
  • NO_LOGGING
    在mybatis中,具体使用哪一个日志来实现,在设置中设定
7.1.1STDOUT_LOGGING标准日志输出

在这里插入图片描述

7.1.2 LOG4J日志输出
1.先导入log4j的包
<!-- https://mvnrepository.com/artifact/log4j/log4j -->
<dependency>
    <groupId>log4j</groupId>
    <artifactId>log4j</artifactId>
    <version>1.2.17</version>
</dependency>

2. log4j.properties
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/yx.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
3.配置log4j的实现
<settings>
        <!--<setting name="logImpl" value="STDOUT_LOGGING"/>-->
        <setting name="logImpl" value="LOG4J"/>
    </settings>
4. log4j的使用,直接测试
简单使用
  1. 在要使用的Log4j的类中,导入import org.apache.log4j.Logger;
  2. 日志对象,参数为当前类的class
static Logger logger = Logger.getLogger((UserDaoTest.class));

3.日志级别

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

8、分页

8.1 为什么要是用分页?

  • 减少数据的处理量
使用Limit分页
select * from user limit 0,1

使用mybatis分页,核心SQL

1.接口
 //分页
    List<User> getUserLimit(Map<String,Object> map);
2.mapper.xml
<select id="getUserLimit" parameterType="map" resultType="com.yx.pojo.User">
        select * from user limit #{startIndex},#{pageSize}
    </select>
3.测试
    @Test
    public void getUserLimit(){
        //第一步: 获得getSqlSession对象
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        //执行SQL
        UserDao mapper = sqlSession.getMapper(UserDao.class);
        //传参是map,所以构建一个HasMap
        HashMap<String,Object> map = new HashMap<>();
        map.put("startIndex",0);
        map.put("pageSize",2);
        List<User> userLimit = mapper.getUserLimit(map);
        for (User user : userLimit) {
            logger.debug(user);
        }
        //关闭
        sqlSession.close();
    }

8.2 分页插件

MyBatis 分页插件 PageHelper

https://pagehelper.github.io/

9、使用注解开发

面向接口开发
关于接口的理解

  • 接口从更深层次的理解,应该是定义(规范 ,约束)与实现(名实分离的原则)的分离
  • 接口的本身反映了系统设计人员对系统的抽象理解
  • 接口应有的两类:
  • 第一类是对一个个体的抽象,他可度应为一个抽象体(abstract class)
  • 第二类是对一个个体某一方面的抽象,即形成一个抽象面(interface)
  • 一个个体有可能有对多个抽象面,抽象体与抽象面是有区别的
    三个面向区别
  • 面向对象是指:我们考虑问题是,以对象为单位,考虑他的属性及方法
  • 面向过程是指:我们考虑问题时,以一个具体流程(事务过程)为单位,考虑他的实现
  • 接口设计与非接口设计是针对复用技术而言的,与面向对象(过程)不是一个问题,更多的体现就是对系统整体的架构

9.1 mybatis执行流程

  • Resources 获取加载全局配置文件
  • 实例化SqlSessionFactoryBuilder 构造器
  • 解析配置文件流 XMLConfigBuild
  • ConfigBuild所有的配置信息
  • 实例化SQLSessionFactory
  • transactional 事务
  • 创建exector执行器
  • 创建SQLSession
  • 实现CRUD
  • 查看是否执行成功
  • 提交事务
  • 关闭

10、Lombok

注解

  • @Data 无参构造,get,set,tostring,hashcode,equals
    @NoArgsConstructor 无参构造
    @AllArgsConstructor 有参构造

11、多对一处理

@Data
public class Student {
    private int id;
    private String name;
    //学还需要关联一个老师
    private Teacher teacher;
}
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Teacher {
    private int id;
    private String name;
}

11.1 按照查询嵌套处理

复杂属性要单独处理, 对象用association(多对一),集合用collection(一对多)***
<mapper namespace="com.yx.dao.StudentMapper">
<!--
    思路:
    1.查询所有学生的信息
    2.根据查出来的学生的tid,寻找对应的老师
-->
    <resultMap id="StudentTeacher" type="com.yx.pojo.Student">
        <result property="id" column="id"/>
        <result property="name" column="name"/>
        <!--复杂属性要单独处理, 对象用association,集合用collection -->
        <association property="teacher" column="tid" javaType="com.yx.pojo.Teacher" select="getTeacher"/>
    </resultMap>
    <select id="getStudent" resultType="com.yx.pojo.Student" resultMap="StudentTeacher">
        select * from many_less.student
    </select>
    <select id="getTeacher" resultType="com.yx.pojo.Teacher">
        select * from many_less.teacher where id=#{id}
    </select>
</mapper>

11.2 按照结果嵌套处理

 <!--按照结果嵌套处理-->
    <resultMap id="StudentTeacher2" type="com.yx.pojo.Student">
        <result property="id" column="sid"/>
        <result property="name" column="sname"/>
        <association property="teacher" javaType="com.yx.pojo.Teacher">
            <result property="id" column="tid"/>
            <result property="name" column="tname"/>
        </association>
    </resultMap>
    <select id="getStudent2" resultMap="StudentTeacher2">
        select s.id sid, s.name sname,t.name tname,t.id tid
         from many_less.student s ,many_less.teacher t where s.tid = t.id
    </select>
11.2.1 回顾Mysql多对一查询方式
  • 子查询
  • 连表查询

12、一对多处理

@Data
public class Student {
    private int id;
    private String name;
    private int tid;
}

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Teacher {
    private int id;
    private String name;
    //一个老师拥有多个学生
    private List<Student> students; 
}

12.1 按照查询嵌套处理

 <select id="getTeacher2" resultMap="StudentTeacher2">
        select * from many_less.teacher where id=#{tid}
    </select>
    <resultMap id="StudentTeacher2" type="com.yx.pojo.Teacher">
        <collection property="students" javaType="ArrayList" ofType="com.yx.pojo.Student" select="getStudentByTeacherId" column="id"/>

    </resultMap>
    <select id="getStudentByTeacherId" resultType="com.yx.pojo.Student">
        select * from many_less.student where tid = #{tid}
    </select>

12.2 按照结果嵌套处理

<resultMap id="StudentTeacher2" type="com.yx.pojo.Teacher">
        <result column="tid" property="id"/>
        <result column="tname" property="name"/>
   <!--复杂属性要单独处理, 对象用association,集合用collection, javaType=""  指定属性的类型,集合中的泛型使用  ofType 获取
        -->
        <collection property="students"  ofType="com.yx.pojo.Student">
            <result property="id" column="sid"/>
            <result property="name" column="sname"/>
            <result property="tid" column="stid"/>
        </collection>
    </resultMap>
    <select id="getTeacher" resultMap="StudentTeacher2">
        select s.id sid, s.name sname,t.name tname,t.id tid
        from many_less.student s ,many_less.teacher t
        where s.tid = t.id and t.id =#{tid}
    </select>
小结
  1. 关联 : 对象用association(多对一)
  2. 集合 : 集合用collection(一对多)
  3. javaTape : 用来指定实体类中属性的类型
  4. ofType : 用来指定映射到List或者结合中的pojo类型,泛型中的约束类

13、动态sql

什么是动态sql : 就是指根据不同的条件生成不同的sql语句

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

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

13.0 随机id生成

public class IDUtils {
    public static String getId(){
        return UUID.randomUUID().toString().replaceAll("-","");
    }

    @Test
    public void test(){
        System.out.println(IDUtils.getId());
    }
}

13.1搭建环境

13.1.1 实体类
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Blog {
    private int id;
    private String title;
    private String author;
    private Date createTime;
    private int views;
}
13.1.2 编写mapper接口 mapper.xml文件

13.2 动态SQL - IF

13.2.1 接口
//查询博客
    List<Blog> queryBlogIf(Map map);
13.2.2 编写sql
<select id="queryBlogIf" parameterType="map" resultType="com.yx.pojo.Blog">
        select * from blog
        <where>
        <if test="title != null">
             title = #{title}
        </if>
        <if test="author != null">
            and author =#{author}
        </if>
        </where>
    </select>
13.2.3 测试
  @Test
    public void testIF(){
        //第一步: 获得getSqlSession对象
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        //执行SQL
        BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
        HashMap map = new HashMap();
        map.put("title","标题1");
        List<Blog> blogs = mapper.queryBlogIf(map);
        for (Blog blog : blogs) {
            System.out.println(blog);
        }
        //关闭
        sqlSession.close();
    }

13.3 trim、where、set

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

13.4 choose、when、otherwise

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

13.4 sql标签(SQL片段)

13.4.1使用sql标签冲去公共的部分
    <sql id="If-title-author">
        <if test="title != null">
            title = #{title}
        </if>
        <if test="author != null">
            and author =#{author}
        </if>
    </sql>
13.4.2 在需要的地方使用 include 标签引用即可
    <select id="findBlog" resultType="com.yx.pojo.Blog">
        select * from blog where id =#{id}
    </select>
    <select id="queryBlogIf" parameterType="map" resultType="com.yx.pojo.Blog">
        select * from blog
        <where>
            <include refid="If-title-author"></include>
       </where>
    </select>

13.5 ForEach

13.5.1 编写接口
//查询1,2,3号的博客
    List<Blog> queryForEach(Map map);
13.5.2 编写sql文件
<!-- select * from blog where 1=1 and(id=1 or id=2 or id=3)
    我们现在穿第一个万能的Map,这个Map中可以存在一个集合
 -->
    <select id="queryForEach" parameterType="map" resultType="com.yx.pojo.Blog">
        select * from blog
        <where>
            <foreach collection="ids" item="id" open="and (" close=")" separator="or">
                id = #{id}
            </foreach>
        </where>
    </select>
13.5.3 测试
    @Test
    public void testForEach(){
        //第一步: 获得getSqlSession对象
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        //执行SQL
        BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
        HashMap map = new HashMap();
        ArrayList<Integer> ids = new ArrayList<>();
        ids.add(1);
        ids.add(3);
        map.put("ids",ids);
        map.put("title","标题1");
        List<Blog> blogs = mapper.queryForEach(map);
        for (Blog blog : blogs) {
            System.out.println(blog);
        }
        //关闭
        sqlSession.close();
    }

14、缓存

14.1简介

查询    :   连接数据,  消耗资源!!!!
     一次查询的记过,给他暂存到一个可以直接取到的地方!   -->  内存: 缓存
我们再次查询相同的数据的时候.直接走缓存,就不用了走数据库了 
14.1.1 什么是缓存
  • 存在内存中的数据
  • 将用户警察查询的数据放在缓存(内存)中,用户去查询数据就不用从次胖上(关系型数据库数据文件)查询,从缓存中查询,从而提高查询效率,解决高并发系统的性能问题
14.1.2 为什么使用缓存
  • 减少和数据库的交互次数,减少系统开销,提高系统效率
14.1.3 什么样的数据能使用缓存
  • 经常查询并且不经常改变的数据 [可以使用缓存]

14.2 MyBatis缓存

14.2.1缓存清除策略
LRU – 最近最少使用:移除最长时间不被使用的对象。
FIFO – 先进先出:按对象进入缓存的顺序来移除它们。
SOFT – 软引用:基于垃圾回收器状态和软引用规则移除对象。
WEAK – 弱引用:更积极地基于垃圾收集器状态和弱引用规则移除对象。

默认的清除策略是 LRU。

flushInterval(刷新间隔)属性可以被设置为任意的正整数,设置的值应该是一个以毫秒为单位的合理时间量。 默认情况是不设置,也就是没有刷新间隔,缓存仅仅会在调用语句时刷新。

size(引用数目)属性可以被设置为任意正整数,要注意欲缓存对象的大小和运行环境中可用的内存资源。默认值是 1024。

readOnly(只读)属性可以被设置为 true 或 false。只读的缓存会给所有调用者返回缓存对象的相同实例。 因此这些对象不能被修改。这就提供了可观的性能提升。而可读写的缓存会(通过序列化)返回缓存对象的拷贝。 速度上会慢一些,但是更安全,因此默认值是 false。

提示: 二级缓存是事务性的。这意味着,当 SqlSession 完成并提交时,或是完成并回滚,但没有执行 flushCache=true 的 insert/delete/update 语句时,缓存会获得更新。
  • MYbatis包含一个非常强大的查询缓存特性,他可以非常方便的定制和配置缓存
  • Mybatis系统中默认定义了两级缓存: 一级缓存二级缓存
    - 默认情况下,只有一级缓存开启(SQLSession级别的缓存,也叫本地缓存)
    - 二级缓存需要手动开启和配置,它是基于namespace级别的缓存
    - 为了提高扩展性,Mybatis定义了缓存接口Cache,可以通过实现Cache接口来定义耳机缓存

14.3 一级缓存

  • 一级缓存也叫本地缓存
    - 与数据库同一次会话期间查询到的数据会放在本地缓存中
    - 以后如果需要获取相同的数据,直接从缓存中拿,没必要再去查询数据库
14.3.1 测试成功缓存
  1. 开启日志
  <settings>
        <!--<setting name="logImpl" value="STDOUT_LOGGING"/>-->
        <setting name="logImpl" value="LOG4J"/>
        <!--是否开启驼峰命名自动映射,即从经典数据库列名 A_COLUMN 映射到经典 Java 属性名 aColumn-->
        <setting name="mapUnderscoreToCamelCase" value="true"/>
    </settings>
  1. 测试在一个Session中查询 两次相同的记录
  2. 查看日志输出
    在这里插入图片描述
14.3.1 测试失效缓存
  1. 查询不同的东西

  2. 增删改操作,可能会改变原来的数据,所以必应会刷新缓存
    在这里插入图片描述

  3. 查询不同的Mapper.xml

  4. 手动清理缓存
    在这里插入图片描述

14.4 二级缓存

  • 二级缓存也叫全局缓存,一级缓存作用域太低了,所以诞生了二级缓存
  • 基于namespace级别的缓存,一个名称空间对应一个二级缓存
  • 工作机制
    - 一个会话查询一条数据,这个数据会被放在当前会话的一级缓存中
    - 如果当前会话关闭了,这个会话对应的一级缓存就没了,但是我们想要的是,会话关闭了,一级缓存中的数据保存到二级缓存中
    - 新的会话查询信息,就可以冲二级缓存中获取内容
    - 不同的mapper查出的数据会放在自己对应的缓存(map)中
14.4.1 开启二级缓存步骤
  1. 开启全局缓存
   <settings>
        <!--<setting name="logImpl" value="STDOUT_LOGGING"/>-->
        <setting name="logImpl" value="LOG4J"/>
        <!--是否开启驼峰命名自动映射,即从经典数据库列名 A_COLUMN 映射到经典 Java 属性名 aColumn-->
        <setting name="mapUnderscoreToCamelCase" value="true"/>
        <!--显示开启全局缓存-->
        <setting name="cacheEnabled" value="true"/>
    </settings>
  1. 在要是用二级缓存的Mapper.xml中开启
<!--显示的开启全局缓存-->
<cache/>

也可以自定义参数

<!--在当前Mapper.xml中 使用二级缓存-->
    <cache
            eviction="FIFO"
            flushInterval="60000"
            size="512"
            readOnly="true"/>

也可以在sql执行中自行修改 useCache=“true/false”

    <select id="queryById" parameterType="map" resultType="com.yx.pojo.User" useCache="true">
        select * from user where id = #{id}
    </select>
  1. 测试
问题

需要将实体类序列化!!!否则会报错!!!

public class User implements Serializable

14.5 缓存原理

在这里插入图片描述

14.6 自定义缓存 ehcache

14.6.1 导包
<!-- https://mvnrepository.com/artifact/org.mybatis.caches/mybatis-ehcache -->
<dependency>
    <groupId>org.mybatis.caches</groupId>
    <artifactId>mybatis-ehcache</artifactId>
    <version>1.1.0</version>
</dependency>

14.6.2 创建ehcache.xml
<?xml version="1.0" encoding="UTF-8" ?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd"
         updateCheck="false">

    <diskStore path="./tmpdir/Tmp_EhCache"/>

    <defaultCache
            eternal="false"
            maxElementsInMemory="10000"
            overflowToDisk="false"
            diskPersistent="false"
            timeToIdleSeconds="1800"
            timeToLiveSeconds="259200"
            memoryStoreEvictionPolicy="LRU"/>

    <cache
            name="cloud_user"
            eternal="false"
            maxElementsInMemory="5000"
            overflowToDisk="false"
            diskPersistent="false"
            timeToIdleSeconds="1800"
            timeToLiveSeconds="1800"
            memoryStoreEvictionPolicy="LRU"/>
              <!--
       defaultCache:默认缓存策略,当ehcache找不到定义的缓存时,则使用这个缓存策略。只能定义一个。
     -->
    <!--
      name:缓存名称。
      maxElementsInMemory:缓存最大数目
      maxElementsOnDisk:硬盘最大缓存个数。
      eternal:对象是否永久有效,一但设置了,timeout将不起作用。
      overflowToDisk:是否保存到磁盘,当系统当机时
      timeToIdleSeconds:设置对象在失效前的允许闲置时间(单位:秒)。仅当eternal=false对象不是永久有效时使用,可选属性,默认值是0,也就是可闲置时间无穷大。
      timeToLiveSeconds:设置对象在失效前允许存活时间(单位:秒)。最大时间介于创建时间和失效时间之间。仅当eternal=false对象不是永久有效时使用,默认是0.,也就是对象存活时间无穷大。
      diskPersistent:是否缓存虚拟机重启期数据 Whether the disk store persists between restarts of the Virtual Machine. The default value is false.
      diskSpoolBufferSizeMB:这个参数设置DiskStore(磁盘缓存)的缓存区大小。默认是30MB。每个Cache都应该有自己的一个缓冲区。
      diskExpiryThreadIntervalSeconds:磁盘失效线程运行时间间隔,默认是120秒。
      memoryStoreEvictionPolicy:当达到maxElementsInMemory限制时,Ehcache将会根据指定的策略去清理内存。默认策略是LRU(最近最少使用)。你可以设置为FIFO(先进先出)或是LFU(较少使用)。
      clearOnFlush:内存数量最大时是否清除。
      memoryStoreEvictionPolicy:可选策略有:LRU(最近最少使用,默认策略)、FIFO(先进先出)、LFU(最少访问次数)。
      FIFO,first in first out,这个是大家最熟的,先进先出。
      LFU, Less Frequently Used,就是上面例子中使用的策略,直白一点就是讲一直以来最少被使用的。如上面所讲,缓存的元素有一个hit属性,hit值最小的将会被清出缓存。
      LRU,Least Recently Used,最近最少使用的,缓存的元素有一个时间戳,当缓存容量满了,而又需要腾出地方来缓存新的元素的时候,那么现有缓存元素中时间戳离当前时间最远的元素将被清出缓存。
   -->
</ehcache>
14.6.3 在Mapper.xml中指定使用我的eCache实现
<cache type="org.mybatis.caches.ehcache.EhcacheCache"/>
14.6.4 编写工具类
public class MyCache implements Cache

面试高频

  • Mysql引擎
  • InnoDB底层原理
  • 索引
  • 索引优化
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值