MyBatis

1. 简介

1.1 介绍

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

1.2 特点

  • 简单易学:本身就很小且简单。没有任何第三方依赖,最简单安装只要两个jar文件+配置几个sql映射文件易于学习,易于使用,通过文档和源代码,可以比较完全的掌握它的设计思路和实现。
  • 灵活:mybatis不会对应用程序或者数据库的现有设计强加任何影响。 sql写在xml里,便于统一管理和优化。通过sql语句可以满足操作数据库的所有需求。
  • 解除sql与程序代码的耦合:通过提供DAO层,将业务逻辑和数据访问逻辑分离,使系统的设计更清晰,更易维护,更易单元测试。sql和代码的分离,提高了可维护性。
  • 提供映射标签,支持对象与数据库的orm字段关系映射
  • 提供对象关系映射标签,支持对象关系组建维护
  • 提供xml标签,支持编写动态sql

1.3 链接


2. 对象生命周期

1. SqlSessionFactoryBuilder

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

2. SqlSessionFactory

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

3. SqlSession

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


3. 搭建环境

  1. 使用MyBatis,需要将只需将 mybatis-x.x.x.jar 文件置于类路径(classpath)中即可
  2. 如果使用Maven来构建项目,则需将下面的依赖代码置于pom.xml文件中:
    1. <dependency>
    2. <groupId>org.mybatis</groupId>
    3. <artifactId>mybatis</artifactId>
    4. <version>x.x.x</version>
    5. </dependency>

4. XML配置(核心配置文件)

MyBatis的核心配置文件如下:

  1. <?xml version="1.0" encoding="UTF-8" ?>
  2. <!DOCTYPE configuration
  3. PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
  4. "http://mybatis.org/dtd/mybatis-3-config.dtd">
  5. <configuration>
  6. <environments default="development">
  7. <environment id="development">
  8. <transactionManager type="JDBC"/>
  9. <dataSource type="POOLED">
  10. <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
  11. <property name="url" value="jdbc:mysql://localhost:3306/db_mybatis?useSSL=false&amp;serverTimezone=UTC"/>
  12. <property name="username" value="root"/>
  13. <property name="password" value="Falsche"/>
  14. </dataSource>
  15. </environment>
  16. </environments>
  17. <!-- 在核心配置文件下,注册Mapper -->
  18. <mappers>
  19. <mapper resource="com/falsche/mapper/UserMapper.xml"/>
  20. </mappers>
  21. </configuration>

5. 编写代码(增删改查)

5.1 MyBatis工具类

  1. public class MyBatisUtils {
  2. private static InputStream inputStream;
  3. private static SqlSessionFactory sqlSessionFactory;
  4. static {
  5. try {
  6. // 1、获取sqlSessionFactiory对象
  7. String resource = "mybatis-config.xml";
  8. inputStream = Resources.getResourceAsStream(resource);
  9. sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
  10. } catch (IOException e) {
  11. e.printStackTrace();
  12. }
  13. }
  14. // 从 SqlSessionFactory 中获取 SqlSession
  15. // SqlSession 提供了在数据库执行 SQL 命令所需的所有方法
  16. public static SqlSession getSqlSession(){
  17. return sqlSessionFactory.openSession();
  18. }
  19. }

5.2 实体类

User类

5.3 Dao/Mapper接口

  1. public interface UserMapper {
  2. public List<User> findAllUser();
  3. }

5.4 接口实现

用xml配置文件来代替传统的Impl实现类,具体如下:

  1. <?xml version="1.0" encoding="UTF-8" ?>
  2. <!DOCTYPE mapper
  3. PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
  4. "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
  5. <!-- namespace:绑定一个对应的Dao/Mapper接口 -->
  6. <mapper namespace="com.falsche.mapper.UserMapper">
  7. <!-- select标签
  8. id:接口方法名字
  9. resultType:返回集合的泛型
  10. parameterType:返回方法的参数类型
  11. -->
  12. <!-- #{}表示占位符"?" -->
  13. <select id="findAllUser" resultType="com.falsche.model.User">
  14. select * from user
  15. </select>
  16. <select id="findUserById" parameterType="int" resultType="com.falsche.model.User">
  17. select * from user where id = #{id}
  18. </select>
  19. <select id="addUser" parameterType="com.falsche.model.User">
  20. insert into user(id,name,password) values(#{id}, #{name}, #{password})
  21. </select>
  22. </mapper>

其中#{}与${}的区别如下:

  • {}:可以防止sql注入

  • ${}:不可以防止sql注入
  • 在传入“表名”作为参数时,一定要使用“${tableName}”的格式,而不能使用“#{tableName}”的格式。

5.5 测试类

注意:增删改必须要提交事务

提交事务方法:

  1. SqlSession.commit();

6. resultMap结果集映射

  • resultMap元素是 MyBatis 中最重要最强大的元素
  • 用于解决属性名和字段名不一致的问题

配置resultMap标签

  1. <resultMap id="UserMap" type="User">
  2. <!--property是实体类的属性,column是数据库的列属性-->
  3. <id property="id" column="user_id" />
  4. <result property="username" column="user_name"/>
  5. <result property="password" column="hashed_password"/>
  6. </resultMap>

在sql语句中设置resultMap属性

  1. <select id="selectUsers" resultMap="UserMap">
  2. select user_id, user_name, hashed_password
  3. from some_table
  4. where id = #{id}
  5. </select>

6.1 association关联(多对一)

本章节实例在项目mybatis-03

  1. <select id="getStudent" resultMap="StudentMap">
  2. select s.id sid, s.`name` sname, t.id tid, t.`name` tname
  3. from student s, teacher t
  4. where s.tid = t.id
  5. </select>
  6. <!--按结果集嵌套-->
  7. <resultMap id="StudentMap" type="Student">
  8. <result property="id" column="sid"/>
  9. <result property="name" column="sname"/>
  10. <association property="teacher" javaType="Teacher">
  11. <result property="id" column="tid"/>
  12. <result property="name" column="tname"/>
  13. </association>
  14. </resultMap>

其中,在标签中

  • property=关联的pojo实体类中属性的名称
  • javaType=关联的pojo实体类中属性的类型

6.2 collection集合(一对多)

本章节实例在项目mybatis-04

  1. <select id="getTeacher" resultMap="TeacherMap">
  2. select t.id tid, t.name tname, s.id sid, s.`name` sname
  3. from student s, teacher t
  4. where t.id = s.tid
  5. </select>
  6. <resultMap id="TeacherMap" type="Teacher">
  7. <result property="id" column="tid"/>
  8. <result property="name" column="tname"/>
  9. <collection property="list" ofType="Student" javaType="List">
  10. <result property="id" column="sid"/>
  11. <result property="name" column="sname"/>
  12. </collection>
  13. </resultMap>

其中,在标签中

  • ofType=集合中泛型的属性类型

7. 日志

7.1 日志工厂

设置名(name):logImpl

有效值(value):

  • SLF4J
  • LOG4J(重点)
  • LOG4J2
  • JDK_LOGGING
  • COMMONS_LOGGING
  • STDOUT_LOGGING(重点)
  • NO_LOGGING

标准日志工厂:STDOUT_LOGGING

  1. <!--设置标准日志工厂-->
  2. <settings>
  3. <setting name="logImpl" value="STDOUT_LOGGING"/>
  4. </settings>

7.2 log4j

  • Log4j是Apache的一个开源项目
  • 控制日志信息输送的目的地是控制台、文件、GUI组件
  • 通过定义每一条日志信息的级别,我们能够更加细致地控制日志的生成过程
  • 通过一个配置文件来灵活地进行配置,而不需要修改应用的代码

步骤:

  1. 使用Maven,导入log4j包

    1. <dependency>
    2. <groupId>log4j</groupId>
    3. <artifactId>log4j</artifactId>
    4. <version>1.2.17</version>
    5. </dependency>
  2. 创建log4j.properties配置文件 ```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/falsche.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. 3. 获取日志对象,参数为当前类的class
  2. ```java
  3. static Logger logger = Logger.getLogger(UserMapperTest.class); // 获取Logger对象
  1. 日志级别
    1. logger.info("info");
    2. logger.debug("debug");
    3. logger.error("error")

8. 分页

RowBounds、PageHelper

分页插件PageHelper

  1. 当前${pageInfo.pageNum}页,总共${pageInfo.pages}页,总共${pageInfo.total}条记录

9. 注解开发

使用注解来代替mapper.xml配置文件,但如果你需要做一些很复杂的操作,最好用 XML 来映射语句

  1. 在接口上方写注解,注解内容为sql语句

    1. @Select("select * from user where id = #{id}")
    2. User findUserById(@Param("id") int id); //根据id查询用户
  2. 在mybatis核心配置文件,注册Mapper

    1. <mappers>
    2. <mapper class="com.falsche.mapper.UserMapper"/>
    3. </mappers>
  3. 底层与本质

    • 底层:动态代理
    • 本质:反射机制实现

关于@Param()注解

  • 基本类型的参数或者String类型,需要加上
  • 引用类型不需要加
  • 如果只有一个基本类型的话,可以不加,但是建议都加上
  • 在sql注解中,”#{}”里面的参数,引用的就是@Param()中的属性

10. Lombok插件

  • Lombok项目是一个Java库
  • 自动插入编辑器和构建工具中
  • Lombok提供了一组有用的注释
  • 用来消除Java类中的大量样板代码

常用注解:

  • @Data: 注解在类,生成setter/getter、equals、canEqual、hashCode、toString方法,如为final属性,则不会为该属性生成setter方法。
  • @AllArgsConstructor: 注解在类,生成有参的构造方法。
  • @NoArgsConstructor: 注解在类,生成无参的构造方法。
  • @Setter 注解在类或字段,注解在类时为所有字段生成setter方法,注解在字段上时只为该字段生成setter方法。
  • @Getter 使用方法同上,区别在于生成的是getter方法。
  • @ToString 注解在类,添加toString方法。
  • @EqualsAndHashCode: 注解在类,生成hashCode和equals方法。
  • @RequiredArgsConstructor: 注解在类,为类中需要特殊处理的字段生成构造方法,比如final和被@NonNull注解的字段。
  • @Slf4j: 注解在类,生成log变量,严格意义来说是常量。

11. 动态SQL

本章节实例在mybatis-05

动态SQL就是在拼接SQL语句

trim、where、set标签:

  • trim:用来定义 where 标签和set标签的作用
  • where:可以自动生成 “where” ,并将子句开头的 “and” 或 “or” 删除
  • set:跟 where 标签同理,set 标签还可以将子句结尾的 “,” 删除

11.1 if

if标签作用于条件查询,其中test属性内容为判断式

  1. <select id="queryUser1ByCondition" resultType="User1" parameterType="map">
  2. select * from user1
  3. <where>
  4. <if test="name != null">
  5. and name like #{name}
  6. </if>
  7. <if test="address != null">
  8. and address like #{address}
  9. </if>
  10. <if test="email != null">
  11. and email like #{email}
  12. </if>
  13. </where>
  14. </select>

11.2 choose、when、otherwise

单选择查询,跟JSTL的 choose、when、otherwise 同理

11.3 SQL片段(sql标签)

sql 标签用于提取一些动态sql的功能,以便复用

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

    1. <sql id="name-address-email">
    2. <if test="name != null">
    3. and name like #{name}
    4. </if>
    5. <if test="address != null">
    6. and address like #{address}
    7. </if>
    8. <if test="email != null">
    9. and email like #{email}
    10. </if>
    11. </sql>
  2. 引用时使用 include 标签

    1. <include refid="name-address-email"/>

11.4 foreach

foreach 标签用于循环查询,比如查询多个 id

  • collection:查询值所在集合
  • item:查询对象
  • open:设置子句开头的内容
  • close:设置子句末尾的内容
  • sqparator:设置子句中间的连接符
  1. <select id="queryUser1ByForeach" resultType="User1" parameterType="map">
  2. select * from user1
  3. <where>
  4. <foreach collection="ids" item="id" open="and (" close=")" separator="or">
  5. id = #{id}
  6. </foreach>
  7. </where>
  8. </select>

12. 缓存

12.1 简介

  1. 什么是缓存?
    • 存在内存中的临时数据
    • 将用户经常查询的数据放在缓存(内存)中,用户查询数据就不用从磁盘上查询,从而提高查询效率,解决了高并发系统的性能问题
  2. 为什么使用缓存?
    • 减少和数据库的交互次数,减少系统开销,提高系统效率
  3. 什么样的数据能使用缓存?
    • 经常查询并且不经常改变的数据

12.2 MyBatis缓存

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

12.3 一级缓存

  1. 一级缓存也叫本地缓存
    • 与数据库同一次会话期间查询到的数据会放在本地缓存中
    • 以后如果需要获取相同的数据,直接从缓存中拿,没必要再去查询数据库
  2. 缓存失效的情况:
    • 查询不同的数据
    • 增删改操作
    • 查询不同的Mapper.xml文件
    • 手动清除缓存
      1. SqlSession.clearCache();

12.4 二级缓存

  1. 二级缓存也叫全局缓存,一级缓存作用域太低,所以诞生了二级缓存
  2. 基于namespace级别的缓存,一个名称空间,对应一个二级缓存
  3. 工作机制
    • 一个会话查询一条数据,这个数据就会被放在当前会话的一级缓存中
    • 当前会话关闭,一级缓存的数据保存在二级缓存中
    • 新的会话查询信息,可以从二级缓存中获取内容
    • 不同的mapper查出的数据会放在自己对应的缓存中

步骤:

  1. 开启全局缓存
    MyBatis中是默认开启全局缓存的,为了方便阅读代码,我们可以显性的写出来

    1. <!--默认是开启的-->
    2. <setting name="cacheEnabled" value="true"/>
  2. 在Mapper中开启
    注意:Mapper开启了二级缓存才能用

    1. <cache/>
    2. <!--或者-->
    3. <cache
    4. eviction="FIFO"
    5. flushInterval="60000"
    6. size="512"
    7. readOnly="true"/>
    • eviction:清除策略(默认是LRU
      可用的清除策略:
      • LRU – 最近最少使用:移除最长时间不被使用的对象。
      • FIFO – 先进先出:按对象进入缓存的顺序来移除它们。
      • SOFT – 软引用:基于垃圾回收器状态和软引用规则移除对象。
      • WEAK – 弱引用:更积极地基于垃圾收集器状态和弱引用规则移除对象。
    • flushInterval:刷新间隔(默认是不设置),以毫秒为单位
    • size:引用大小(默认是1024)

注意:开启二级缓存,需要序列化实体类,即,需要实现 io 包下的 Serialable 接口


13. tk.mybatis

导入依赖

  1. <dependency>
  2. <groupId>tk.mybatis</groupId>
  3. <artifactId>mapper-spring-boot-starter</artifactId>
  4. <version>2.1.5</version>
  5. </dependency>
  6. <dependency>
  7. <groupId>tk.mybatis</groupId>
  8. <artifactId>mapper</artifactId>
  9. <version>4.1.5</version>
  10. </dependency>

实体类配置

  1. import lombok.AllArgsConstructor;
  2. import lombok.Data;
  3. import lombok.NoArgsConstructor;
  4. import javax.persistence.*;
  5. import java.io.Serializable;
  6. import java.util.Date;
  7. @Data
  8. @AllArgsConstructor
  9. @NoArgsConstructor
  10. @Table(name = "biz_type")
  11. public class BizType implements Serializable {
  12. @Id //主键
  13. @GeneratedValue(strategy = GenerationType.IDENTITY)
  14. private Long typeId;
  15. private String typeName;
  16. private String description;
  17. private boolean available;
  18. private Date createTime;
  19. private Date updateTime;
  20. }

通用Service类