简介

什么是 MyBatis?

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

环境

  • JDK 1.8
  • Mysql 5.7
  • maven 3.6.1
  • IDEA

回顾

  • JDBC
  • MySQL
  • Java 基础
  • Maven
  • Junit

1、简介

1.1 什么是 Mybatis

mybatis

  • MyBatis 是一款优秀的持久层框架
  • 它支持定制 SQL、存储过程以及高级映射
  • MyBatis 几乎避免了所有的 JDBC 代码和手动设置参数以及获取结果集
  • MyBatis 可以使用简单的 XML 或者注解来配置和映射原生类型、接口和 Java 的 POJO(Plan Old Java Objects, 普通老式 Jva 对象)为数据库中的记录
  • MyBatis 本事 apache 的一个开源项目 iBatis,2010 年这个项目由 apache software foundation 迁移到 google code,并改名为 MyBatis
  • 2013 年 11 月迁移到 GitHub

如何获得 MyBatis?

  • Maven 仓库 https://mvnrepository.com/artifact/org.mybatis/mybatis
<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis</artifactId>
    <version>3.5.3</version>
</dependency>
  • GitHub https://github.com/mybatis/mybatis-3
  • Document https://mybatis.org/mybatis-3/

1.2 持久化

数据持久化

  • 持久化就是将程序的数据在持久状态和瞬时状态转换的过程
  • 内存: 断电即失
  • 数据库(jdbc),io 文件持久化

为什么需要持久化?

  • 不想让一些对象丢掉

1.3 持久层

Dao 层、Service 层、Controller 层

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

1.4 为什么需要 MyBatis

  • 方便
  • 传统的 JDBC 代码太复杂。简化。框架 自动化
  • 将数据存入数据库
  • 不用 MyBatis 也可以

优点:

  • 简单
  • 灵活
  • 接触 SQL 与代码的耦合 - SQL 和代码分离

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.cj.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=true&amp;
                                            useUnicode=true&amp;characterEncoding=utf-8&amp;serverTimeZone=GMT"/>
                <property name="username" value="root"/>
                <property name="password" value="12345678"/>
            </dataSource>
        </environment>
    </environments>

</configuration>

XML 映射文件

SQL 映射文件只有很少的几个顶级元素(按照应被定义的顺序列出):

  • cache – 对给定命名空间的缓存配置。
  • cache-ref – 对其他命名空间缓存配置的引用。
  • resultMap – 是最复杂也是最强大的元素,用来描述如何从数据库结果集中来加载对象。
  • parameterMap – 已被废弃!老式风格的参数映射。更好的办法是使用内联参数,此元素可能在将来被移除。文档中不会介绍此元素。
  • sql – 可被其他语句引用的可重用语句块。
  • insert – 映射插入语句
  • update – 映射更新语句
  • delete – 映射删除语句
  • select – 映射查询语句

映射器 mappers

MapperRegistry: 注册绑定 Mapper 文件

1⃣️ 使用使用相对于类路径的资源引用

👑 关键词resource

Example🌰:

<mappers>
  <mapper resource="org/mybatis/builder/AuthorMapper.xml"/>
  <mapper resource="org/mybatis/builder/BlogMapper.xml"/>
  <mapper resource="org/mybatis/builder/PostMapper.xml"/>
</mappers>

① 使用映射器接口实现类的完全限定类名

👑 关键词class

Example🌰:

<mappers>
  <mapper class="org.mybatis.builder.AuthorMapper"/>
  <mapper class="org.mybatis.builder.BlogMapper"/>
  <mapper class="org.mybatis.builder.PostMapper"/>
</mappers>

⚠️ 第一种方法使用的是/地址分隔
⚠️ 第二种方法使用.运算符,显示类的全名

② 将包内的映射器接口实现全部注册为映射器

👑 关键词<package name="..." />

Example🌰:

<mappers>
  <package name="org.mybatis.builder"/>
</mappers>

类型别名 typeAliases

  • 类型别名是为 Java 类型设置一个简短的名字
  • 通过typeAliases标签设置
  • ⚠️ typeAliases标签需要在整个配置文件的第三个位置

方式 1⃣️ = 给一个类设置别名

<configuration>
    <properties>...</properties>
    <settings>...</settings>
    <typeAliases>
        <typeAlias type="com..." alias="__" />
    </typeAliases>
</configuration>

配置的别名可以在 config 文件作用域下的任何地方使用

方式 2⃣️ = 给一个包下的所有类设置别名

<configuration>
    <properties>...</properties>
    <settings>...</settings>
    <typeAliases>
        <package name="com.faraj..." />
    </typeAliases>
</configuration>

⚠️:给一个包下的所有类型设置别名后,调用这个类只需要将这个类型的首字母小写即可(官方建议)但是也可以大写


☝️ 1⃣️ 方式起的别名可以 diy,但是 2⃣️ 方式不可以

如果想使用 2⃣️ 方式的时候 diy 别名,可以在实体类上 ➕ 注解

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

🧠 基本类型和包装类的别名

像 int、double、float、…这种基本类型,可以使用它的别名_int,_double,_float

如果是包装类比如 Integer、Double、Float 他们的别名就是对应的小写int,double,float

设置 settings

⚙️ 设置是 mybatis 配置中极为重要的调整部分

These are extremely important tweaks that modify👀 the way that MyBatis behaves at runtime.

All Settins

其他配置

  • typeHandlers(类型处理器)
  • objectFactory(对象工厂)
  • plugins(插件)
    • MyBatis Generator Core
    • MyBatis Plus
    • 通用 mapper
  • databaseIdProvider(数据库厂商标识)

lombok

Lombok 使用步骤

  • 1⃣️ 安装 lombok 插件
  • 2⃣️ 导入 jar🎒
  • 3⃣️ 在实体类上加注解
<!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.12</version>
</dependency>

日志

⚙️

name=logImpl📝

指定 MyBatis 所用日志的具体实现,未指定时将自动查找。
value = 👇

  • SLF4J
  • LOG4J
  • LOG4J2
  • JDK_LOGGING
  • COMMONS_LOGGING
  • STDOUT_LOGGING
  • NO_LOGGING

STDOUT_LOGGING

👉 默认日志工厂实现,无序额外配置(标准的日志工厂 🏭)

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

🚫 在 name 和 value 中不能发生任何 🙅 错误

LOG4J

  • Log4j 是 Apache 的一个开源项目
    log4J
  • 可以控制日志信息输送的目的地是控制台、文件、GUI 组件,甚至是套接口服务器、NT 的事件记录器、UNIX Syslog 守护进程等
  • 我们也可以控制每一条日志的输出格式
  • 通过定义每一条日志信息的级别,我们能够更加细致地控制日志的生成过程
  • 通过配置文件设置,不用更改源代码

第 1⃣️ 步 导包 🎒

⚠️!! 是 LOG4J 的 🎒 不是 Log4j core

    <dependency>
        <groupId>log4j</groupId>
        <artifactId>log4j</artifactId>
        <version>1.2.17</version>
    </dependency>

log4j.properties

# 将等级为DEBUG的日志信息输出到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/log4j.log
log4j.appender.file.MaxFileSize = 10Mb
log4j.appender.file.Threshold = DEBUG
log4j.appender.file.layout = org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern = [%p][%d{yy-MM-dd}][%c]%m%n

# 日志输出级别
log4j.logger.org.mybatis = DEBUG
log4j.logger.java.sql = DEBUG
log4j.logger.java.sql.Statement = DEBUG
log4j.logger.java.sql.ResultSet = DEBUG
log4j.logger.java.sql.PreparedStatement = DEBUG

log4j 的实现 ⏲

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

自定义 log4j 输出内容

在要输入日志的类中定义静态字段

static Logger logger = Logger.getLogger(类.class);

三种日志级别

  • info - information 一般级别 🎩
  • debug - debug debug 级别 🎓
  • error - error 错误级别 ⛑
public void log4jTest(){
    logger.info("INFO:进入log4jTest");
    logger.debug("DEBUG:进入log4jTest");
    logger.error("ERROR:进入log4jTest");
}

⚠️ 使用 LOG4J 的时候导的包 👝 是org.apache.log4j.Logger不是其他的 💼

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="org.mybatis.example.BlogMapper">

</mapper>

sql

分页

创建接口 🔌

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

xml 实现 🔫

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

测试 🔨

@Test
public void getUsersLimitTest(){
    SqlSession sqlSession = MybatisUtil.getSqlSession();
    Map<String,Integer> map = new HashMap<String,Integer>();
    map.put("startIndex",4);
    map.put("pageSize" ,4);
    List<User> users = sqlSession.getMapper(UserMapper.class).getUsersLimit(map);
    logger.info("获取5-8的记录");
    logger.info("==============================");
    for (User user : users) {
        logger.info(user);
    }

    sqlSession.close();
}

一对多

方法 1⃣️:“子查询”

<select id="getStudentsByTeacherId" resultMap="TeSt" parameterType="_int">
    select * from mybatis.teacher where id = #{tid}
</select>

<resultMap id="TeSt" type="Teacher">
    <result property="id" column="id"/>
    <result property="name" column="name"/>
    <collection property="students" column="id" javaType="ArrayList" ofType="student" select="getStu"/>
</resultMap>

<select id="getStu" resultType="student">
    select * from mybatis.student where teacher_id = #{tid}
</select>

方法 1⃣️:“联表查询”🏆

<select id="getStudentsByTeacherId" resultMap="TeaStu" parameterType="_int">
    select t.id TeacherId, t.name TeacherName, stu.id StudentId, stu.name StudentName
    from mybatis.student stu, mybatis.teacher t
    where stu.teacher_id = t.id and t.id = #{tid}
</select>
<resultMap id="TeaStu" type="Teacher">
    <result property="id" column="TeacherId"/>
    <result property="name" column="TeacherName"/>
    <collection property="students" ofType="student">
        <result property="id" column="StudentId"/>
        <result property="name" column="StudentName"/>
        <result property="teacher_id" column="teacher_id"/>
    </collection>
</resultMap>

多对一

从两个表中查询数据

方法 1⃣️:“子查询”

<resultMap id="TeacherStudent" type="student">
    <result property="id" column="id"/>
    <result property="name" column="name"/>
    <association property="teacher" column="teacher_id" javaType="teacher" select="getTeacher"/>
</resultMap>

<select id="getStudents" resultMap="TeacherStudent">
    select * from mybatis.student
</select>

<select id="getTeacher" resultType="teacher">
    select * from mybatis.teacher
</select>

方法 2⃣️:“联表查询”👑

<select id="getStudents" resultMap="StuTea">
    select s.id sid, s.name sname, t.name tname, t.id tid
    from mybatis.teacher t, mybatis.student s
    where s.teacher_id = t.id
</select>

<resultMap id="StuTea" type="student">
    <result property="id" column="sid" />
    <result property="name" column="sname" />
    <association property="teacher" javaType="teacher">
        <result property="name" column="tname" />
        <result property="id" column="tid" />
    </association>
</resultMap>

动态 sql

根据不同的条件生成不同的 SQL 语句 👍

IF 🤖

<select id="queryBlogIf" parameterType="map" resultType="blog">
    select * from mybatis.blog where 1 = 1
    <if test="title != null">
        and title = #{title}
    </if>
    <if test="author != null">
        and author = #{author}
    </if>
</select>

choose (when otherwise)

where✔️ trim (where set)

🏷️ where 标签会自动加上 where,如果第一个是 and 就自动转换成 where✅,防止出现 sql 语句错误

<select id="queryBlogIf" parameterType="map" resultType="blog">
    select * from mybatis.blog
    <where>
        <if test="title != null">
            title = #{title}
        </if>
        <if test="author != null">
            and author = #{author}
        </if>
    </where>
</select>

foreach 🐘

<select id="queryBlogForeach" parameterType="map" resultType="blog">
    select * from mybatis.blog
    <where>
        id in
        <foreach collection="ids" item="id" open="(" separator="," close=")">
            #{id}
        </foreach>
    </where>
</select>

本质就是在 map 中 put 一个名为 ids 的数组或集合,遍历里面的 id,然后传给#{id}

sql 片段

可以将一些 SQL 片段单独抽出来放在 sql 标签中,可以实现 sql 复用

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

缓存 cache

一级缓存

一级缓存就是一个 SqlSession 存在期间如果有重复的查询记录,则不会反复的从数据库 🀄️ 查询,会将第一次查询的记录 📝 在内存中,第二次查询直接走内存中的数据


  • 1⃣️ 一级缓存是 Mybatis 自动实现的,默认打开
  • 2⃣️ 一级缓存也叫做本地缓存
  • 3⃣️ 一级缓存的作用域在一个 SqlSession 之间,无法手动关闭

缓存失效的情况 🙀

  • 1⃣️ 查询不同的数据
  • 2⃣️ 数据表中 🈶️ 任意一条数据有增删改的操作都会刷新缓存
  • 3⃣️ 查询不同的 Mapper
  • 4⃣️ 手动清理缓存 👉 sqlSession.cleanCache()

二级缓存

二级缓存是全局缓存,由于一级缓存的作用域太局限,所以二级缓存诞生了

二级缓存的实现
当一级缓存作用域结束的时候,如果开启了全局缓存,也就是二级缓存,一级缓存里的数据会丢给二级缓存

开启二级缓存

1⃣️ 在配置文件中打开缓存 👇

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

2⃣️ 在 mapper 映射文件中中添加 👇

<cache/>

也可以是自定义配置:

<cache
  eviction="FIFO"
  flushInterval="60000"
  size="512"
  readOnly="true"/>

eviction: 清楚策略

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

flushInterval: 缓存刷新时间 ⌚️ (毫秒)
size: 缓存最大的条目,默认是 1024
readOnly: 是否只读,

整合第三方 ehcache



java   mybatis      Mybatis

本博客所有文章除特别声明外,均采用 CC BY-SA 3.0协议 。转载请注明出处!