Mybatis

Mybatis

官网:https://mybatis.org/mybatis-3/

1. 简介

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

依赖jar包

<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis</artifactId>
    <version>3.4.6</version>
</dependency>

2. 持久化

持久化就是将程序的数据在持久状态和瞬时状态下的转换

对应生活的例子

​ 罐头,冷藏

3. 为什么使用Mybatis

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

4. 第一个Mybatis程序

配置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?useUnicode=true&amp;characterEncoding=utf8&amp;useSSL=true&amp;serverTimezone=Asia/Shanghai"/>
                <property name="username" value="root"/>
                <property name="password" value="123456"/>
            </dataSource>
        </environment>
    </environments>
</configuration>

编写工具类

public class MybatisUtils {

    private static SqlSessionFactory sqlSessionFactory;

    static{
       try {
           String resource = "mybatis-config.xml";
           InputStream inputStream = Resources.getResourceAsStream(resource);
           sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
       } catch (IOException e) {
           e.printStackTrace();
       }
   }


   //获取openSession
   public static SqlSession getSqlSession(){
        return sqlSessionFactory.openSession();
    }
}

编写测试环境

dao UserMapper

pojo User

编写 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">



<mapper namespace="com.immortal.dao.UserMapper">
<!-- 查询语句-->
    <select id="getUserList" resultType="com.immortal.pojo.User">
        select * from mybatis.user
  </select>
</mapper>

注意点

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

没有注册到mapper的注册中心里面区 ,就是mybatis的核心配置文件

解决:

重新测试

初始化异常 失败

Cause: java.io.IOException: Could not find resource com.immortal.dao.UserMapper.xml

没有找到这个UserMapper.xml maven的静态资源过滤问题

解决:

  <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 Test {

@org.junit.Test
public void TestSelect(){
    //获得SqlSession
    SqlSession sqlSession = MybatisUtils.getSqlSession();
    //获取Mapper
    UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
    //调用接口的方法
    List<User> userList = userMapper.getUserList();
    //遍历
    for (User user : userList) {
        System.out.println(user);
    }
    //关闭连接
    sqlSession.close();
}

5. CRUD

mamespace中的包名要和接口名字一致

Select

  • id 就是对应的方法名
  • resultType sql语句执行的返回值
  • paramterType 参数的类型

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

Insert

<insert id="addUser" parameterType="com.immortal.pojo.User">
    insert into mybatis.user (`id`,`name`,`pwd`) values(#{id},#{name},#{pwd})
</insert>

Update

<update id="updateUser" parameterType="com.immortal.pojo.User">
    update mybatis.user set `name`=#{name},`pwd`=#{pwd} where `id`=#{id}
</update>

Delete

<delete id="deleteUser">
    delete from mybatis.user where `id`=#{id}
</delete>

6. 万能的Map

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

Map传递参数,直接在sql中取出key即可

7. 模糊查询

java代码执行的时候,传递通配符 %李%

@org.junit.Test
public void getUserLike(){
    //获得SqlSession
    SqlSession sqlSession = MybatisUtils.getSqlSession();
    //获取Mapper
    UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
    //调用接口的方法
    List<User> userList = userMapper.getUserLike("%杜%");
    //遍历
    for (User user : userList) {
        System.out.println(user);
    }
    //关闭连接
    sqlSession.close();
}
<select id="getUserLike" resultType="com.immortal.pojo.User">
      select * from mybatis.user where `name` like #{value}
</select>

8. 配置解析

MyBatis 的配置文件包含了会深深影响 MyBatis 行为的设置和属性信息。 配置文档的顶层结构如下:

环境配置(environments)

MyBatis 可以配置成适应多种环境,这种机制有助于将 SQL 映射应用于多种数据库之中, 现实情况下有多种理由需要这么做。例如,开发、测试和生产环境需要有不同的配置;或者想在具有相同 Schema 的多个生产数据库中使用相同的 SQL 映射。还有许多类似的使用场景。

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

mybatis的默认事务管理器是jdbc它支持事务提交回滚操作 连接池是POOLED的

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

官方文档 写的很清楚

属性(properties)

db.properties

driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/mybatis?useUnicode=true&characterEncoding=utf8&useSSL=true&serverTimezone=Asia/Shanghai
username=root
password=123456

mybatis-config

<?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>
<!--    导入外部配置文件-->
    <properties resource="db.properties"/>

    <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/immortal/dao/UserMapper.xml"/>
    </mappers>
</configuration>

优先使用外部配置文件

类型别名(typeAliases)

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

<typeAliases>
    <typeAlias type="com.immortal.pojo.User" alias="User"/>
</typeAliases>

在其他地方可以使用User这个别名代表com.immortal.pojo.User

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

每一个在包 domain.blog 中的 Java Bean,在没有注解的情况下,会使用 Bean 的首字母小写的非限定类名来作为它的别名。 比如 domain.blog.Author 的别名为 author;若有注解,则别名为其注解值。见下面的例子:

@Alias("author")
public class Author {
    ...
}

下面是一些为常见的 Java 类型内建的类型别名。它们都是不区分大小写的,注意,为了应对原始类型的命名重复,采取了特殊的命名风格。

别名 映射的类型
_byte byte
_long long
_short short
_int int
_integer int
_double double
_float float
_boolean boolean
string String
byte Byte
long Long
short Short
int Integer
integer Integer
double Double
float Float
boolean Boolean
date Date
decimal BigDecimal
bigdecimal BigDecimal
object Object
map Map
hashmap HashMap
list List
arraylist ArrayList
collection Collection
iterator Iterator

设置(settings)

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

映射器(mappers)

既然 MyBatis 的行为已经由上述元素配置完了,我们现在就要来定义 SQL 映射语句了。 但首先,我们需要告诉 MyBatis 到哪里去找到这些语句。 在自动查找资源方面,Java 并没有提供一个很好的解决方案,所以最好的办法是直接告诉 MyBatis 到哪里去找映射文件。 你可以使用相对于类路径的资源引用,或完全限定资源定位符(包括 file:/// 形式的 URL),或类名和包名等。例如:

<!-- 使用相对于类路径的资源引用 -->
<mappers>
  <mapper resource="org/mybatis/builder/AuthorMapper.xml"/>
  <mapper resource="org/mybatis/builder/BlogMapper.xml"/>
  <mapper resource="org/mybatis/builder/PostMapper.xml"/>
</mappers>
<!-- 使用完全限定资源定位符(URL) -->
<mappers>
  <mapper url="file:///var/mappers/AuthorMapper.xml"/>
  <mapper url="file:///var/mappers/BlogMapper.xml"/>
  <mapper url="file:///var/mappers/PostMapper.xml"/>
</mappers>
<!-- 使用映射器接口实现类的完全限定类名 -->
<mappers>
  <mapper class="org.mybatis.builder.AuthorMapper"/>
  <mapper class="org.mybatis.builder.BlogMapper"/>
  <mapper class="org.mybatis.builder.PostMapper"/>
</mappers>
<!-- 将包内的映射器接口实现全部注册为映射器 -->
<mappers>
  <package name="org.mybatis.builder"/>
</mappers>

这些配置会告诉 MyBatis 去哪里找映射文件,剩下的细节就应该是每个 SQL 映射文件了,也就是接下来我们要讨论的。

mapperRegister若如果没有注册的话,就区mybatis-config.xml配置

生命周期于作用域

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

SqlSessionFactoryBuilder

  • 建造者模式
  • 一旦创建了SqlSessionFactory,就不再需要了
  • 局部变量

SqlSessionFactory

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

SqlSession

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

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

  • 连接完毕后需要赶紧关闭,否则会占用资源!

9. 解决属性名和字段不一致

如果数据库的字段名与pojo的属性没有不一致的话,无法进行赋值到属性上,导致空值

ResultMap

可以通过 ResultMap这个标签将数据库的字段名与JavaBean的属性名进行关联进行赋值

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">

<mapper namespace="com.immortal.dao.UserMapper">
    <resultMap id="userMap" type="com.immortal.pojo.User">
        <result column="pwd" property="password"/>
    </resultMap>

    <select id="getUserById" resultMap="userMap">
        select * from  mybatis.user where id = #{id}
    </select>
</mapper>

赋值到属性上面了

10. 日志

日志工厂

  • STDOUT_LOGGING简单,加上就可以用

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

    控制台输出

LOG4J

  • Log4j是Apache的一个开源项目,通过使用Log4j,我们可以控制日志信息输送的目的地是控制台、文件、GUI组件,甚至是套接口服务器、NT的事件记录器、UNIX Syslog守护进程等;
  • 我们也可以控制每一条日志的输出格式
  • 通过定义每一条日志信息的级别
  • 最令人感兴趣的就是,这些可以通过一个配置文件来灵活地进行配置,而不需要修改应用的代码。
  • 导入jar包
<!-- https://mvnrepository.com/artifact/log4j/log4j -->
<dependency>
    <groupId>log4j</groupId>
    <artifactId>log4j</artifactId>
    <version>1.2.17</version>
</dependency>

编写配置文件

将mybatis的日志实现换成log4j的 就可以了

11. 分页

为什么要分页

  • 减少数据的处理量

语法

select * from user limit startIndex,pageSize;
select * from user limit 0,5;

基于Map实现,不正规,爽

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

RowBounds

<select id="getListUserRowBounds" resultMap="userMap">
    select * from mybatis.user
</select>
@org.junit.Test
public void getListUserRowBounds(){
    SqlSession sqlSession = MybatisUtils.getSqlSession();

    RowBounds rowBounds = new RowBounds(0,5);

    List<User> userList = sqlSession.selectList("com.immortal.dao.UserMapper.getListUserRowBounds", null, rowBounds);

    for (User user : userList) {
        System.out.println(user);
    }

    sqlSession.close();
}

分页插件

按照文档一步一步操作就行 easy

12. 使用注解开发

关于接口的理解 解耦

接口更深的理解就是定义实现分离

接口本事反应了系统设计人员对系统的抽象理解

接口有两类

  • 第一种是对一个个体的抽象,它对应一个抽象体
  • 第二种是对一个个体莫一方面的抽象,既成为一个抽象面

官网上很具体了 !!! 复杂的sql不建议使用

13. Mybatis执行流程

  1. Resources对象获取加载全局配置文件
  2. 实例化SqlSessionFactoryBuilder构造器
  3. 解析配置文件流XMLConfigBuilder
  4. 返回Configuration所有的配置信息
  5. sqlSessionFactory实例化
  6. 事务管理器transaction
  7. 创建执行器executor
  8. 创建sqlSession
  9. 实现crud
  10. 是否执行成功 失败回滚

14. 多对一,一对多

多个学生关联一个老师 【多对一】

对于老师而言就是 【一对多】

如果java实体类中包含其他java类 需要进行结果集映射

<?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.immortal.dao.StudentMapper">

    <select id="getStudentList" resultMap="StudentTeacher">
        select * from mybatis.student;
    </select>
    <!--这里将结果集通过association 进行映射 用子查询将结果映射到属性上-->
    <resultMap id="StudentTeacher" type="com.immortal.pojo.Student">
        <result property="id" column="id"/>
        <result property="name" column="name"/>
        <association property="teacher" column="tid" javaType="com.immortal.pojo.Teacher" select="getTeacher"/>
    </resultMap>

    <select id="getTeacher" resultType="com.immortal.pojo.Teacher">
        select * from mybatis.teacher where id = #{tid}
    </select>
</mapper>

还可以向将数据查询出来 在将数据映射到具体的属性上

    <select id="getStudentList" resultMap="StudentTeacher">
        select student.id as sid,student.name as sname,teacher.name as tname,teacher.id as tid
        from mybatis.student,mybatis.teacher
        where student.tid = teacher.id
    </select>
<!--映射查询的数据-->
    <resultMap id="StudentTeacher" type="com.immortal.pojo.Student">
        <result property="id" column="sid"/>
        <result property="name" column="sname"/>
        <association property="teacher" javaType="com.immortal.pojo.Teacher">
            <result property="id" column="tid"/>
            <result property="name" column="tname"/>
        </association>
    </resultMap>

其实我们在解决我问题就是 javaBean字段与数据库字段无法映射的问题

提供映射关系

15. 动态SQL

根据不同的条件生成不同的sql

动态 SQL 是 MyBatis 的强大特性之一。如果你使用过 JDBC 或其它类似的框架,你应该能理解根据不同条件拼接 SQL 语句有多痛苦,例如拼接时要确保不能忘记添加必要的空格,还要注意去掉列表最后一个列名的逗号。利用动态 SQL,可以彻底摆脱这种痛苦。

使用动态 SQL 并非一件易事,但借助可用于任何 SQL 映射语句中的强大的动态 SQL 语言,MyBatis 显著地提升了这一特性的易用性。

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

  • if

    •   <select id="selectBlogIF" resultType="com.immortal.pojo.Blog" parameterType="map">
              select * from Blog
              <where>
                  <if test="title != null">
                      title = #{title}
                  </if>
                  <if test="author != null">
                      and author = #{author}
                  </if>
              </where>
        </select>
      
  • choose (when, otherwise)

    • <select id="findActiveBlogLike"
           resultType="Blog">
        SELECT * FROM BLOG WHERE state = ‘ACTIVE’
        <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>
      </select>
      
  • trim (where, set)

  • 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>
      

报错

org.apache.ibatis.binding.BindingException: Mapper method 'com.immortal.dao.BlogMapper.addBlog attempted to return null from a method with a primitive return type (int).

这里脑子问题将select 标签写错了 导致这个问题 将改成insert标签就可以了

https://mybatis.org/mybatis-3/zh/dynamic-sql.html 官网 上面写的很好

SQL片段

我们有的时候可能会将一些公共的部分抽取出来

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


<select id="selectBlogIF" resultType="com.immortal.pojo.Blog" parameterType="map">
    select * from Blog
    <where>
        <include refid="if-title-author"/>
    </where>
</select>

16. 缓存

  1. 什么是缓存?
    • 内存中存储的临时数据,
    • 将用户经常查询的数据放在缓存中,用户区查询数据就不用次磁盘上查询,从缓存上查询会大大提高效率,提高性能
  2. 为什么使用缓存
    • 减少与数据库的交互,减小系统的开销,提高系统的效率
  3. 什么数据能使用缓存
    • 经常查询,不变的数据

Mybatis缓存

MyBatis 内置了一个强大的事务性查询缓存机制,它可以非常方便地配置和定制。 为了使它更加强大而且易于配置

  • Mybatis系统默认定义两种缓存,一级缓存二级缓存
    • 默认情况下,只有一级缓存(sqlSession基本的缓存,也称为本地缓存)
    • 二级缓存需要手动配置和开启,它是基于namespace级别的
    • 为了提高扩展性,Mybatis定义了缓存接口Cache,我们可以通过实现接口,自定义二级缓存
  • 可用的清除策略有:
    • LRU – 最近最少使用:移除最长时间不被使用的对象。
    • FIFO – 先进先出:按对象进入缓存的顺序来移除它们。
    • SOFT – 软引用:基于垃圾回收器状态和软引用规则移除对象。
    • WEAK – 弱引用:更积极地基于垃圾收集器状态和弱引用规则移除对象。

一级缓存

这个简单语句的效果如下:

  • 映射语句文件中的所有 select 语句的结果将会被缓存。
  • 映射语句文件中的所有 insert、update 和 delete 语句会刷新缓存。
  • 缓存会使用最近最少使用算法(LRU, Least Recently Used)算法来清除不需要的缓存。
  • 缓存不会定时进行刷新(也就是说,没有刷新间隔)。

开启日志

测试在一个session中查询两次记录

 @org.junit.Test
    public void selectBlogIF(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);

        Map<String, Object> map = new HashMap<String, Object>();

        map.put("author","james");

        List<Blog> blogs = mapper.selectBlogIF(map);

        List<Blog> blogs1 = mapper.selectBlogIF(map);

        System.out.println(blogs == blogs1);

        sqlSession.close();

查看日志输出

缓存失效的情况:

  • 增删改 可能会改变原来的数据,所有必定会刷新缓存!
  • 查询不同的东西
  • 查询不同的mapper.xml
  • 手动清除

二级缓存

二级缓存也称全局缓存,一级的作用域太低了

基于namespace级别的缓存,一个命名空间,对应一个二级缓存

工作机制

  • 一个会话查询一条数据,这个数据会存储在当前会话的一级缓存中
  • 如果当前会话关闭了,这个对话的一级缓存就没有了,但是我们想要的是会话关闭了,一级缓存的数据就会被保存在二级缓存中
  • 新的会话查询信息,就可以在二级缓存中获取信息
  • 不同的mapperhu查出的数据,会放在自己对应的缓存中

步骤

  • 需要在setting中中显示开启二级缓存

  • 设置名 描述 有效值 默认值
    cacheEnabled 全局性地开启或关闭所有映射器配置文件中已配置的任何缓存。 true | false true
  • 在mapper中 配置二级缓存

  • 加上

  • <cache
      eviction="FIFO"   //缓存策略
      flushInterval="60000"  //刷新实现
      size="512"			//缓存大小
      readOnly="true"/>    //只读
    

缓存原理

posted @ 2021-01-03 15:24  immortal_mode  阅读(85)  评论(0)    收藏  举报