全网最全MyBatis入门到精通

120 阅读11分钟

MyBatis

本文内容参考互联网,kuangstudy,感谢!!

概念

什么是mybatis

MyBatis本是apache的一个开源项目iBatis,2010年这个项目由apache software foundation迁移到了[google code](baike.baidu.com/item/google code/2346604),并且改名为MyBatis。

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

为什么需要mybatis

一个框架产生的缘由无非就是为了方便开发,减少代码量。

mybatis的优点

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

入门

学习途径

  1. 官网:mybatis.org/mybatis-3/z…
  2. B站:通常来说对于一项技术的初级阶段,看视频还是非常有帮助的。
  3. 书籍:适合自律性强的人,比较乏味,但是通常书籍涉及的知识点非常全。
  4. 看博客:通常都是他人对一项技术的总结,能够了解到该技术的主要技术点,但是通常知识点不会覆盖的很全面,而且学习的思路也是别人整理后的思路,不利于形成自己的学习方式。

搭建环境

  • 创建maven项目

image-20220228112111438.png

  • 创建数据库,并创建表student
CREATE TABLE `student` (
  `id` varchar(30) DEFAULT NULL,
  `name` varchar(30) DEFAULT NULL,
  `age` int(11) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

<dependencies>
    <!--mysql连接工具-->
    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <version>8.0.27</version>
    </dependency>
    <!--mybatis依赖-->
    <dependency>
      <groupId>org.mybatis</groupId>
      <artifactId>mybatis</artifactId>
      <version>3.5.7</version>
    </dependency>
    <!--单元测试-->
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.13.1</version>
    </dependency>
</dependencies>
  • 创建mybatis配置文件mybatis-config.xml,
<?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">
                <!--mysql驱动-->
                <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
                <!--mysql链接url-->
                <property name="url" value="jdbc:mysql://10.36.2.65:6612/ta"/>
                <!--mysql用户名-->
                <property name="username" value="root"/>
                <!--密码-->
                <property name="password" value="talent"/>
            </dataSource>
        </environment>
    </environments>
    <mappers>
        <mapper resource="example.xml"/>
    </mappers>
</configuration>

//在该文件中我们可以看到可以配置多个数据源,配置多个environment,但是生效的只能有一个。
  • 创建mybatis工具类
public class MybatisUtils {
	//创建SqlSessionFactory实例
    public static SqlSessionFactory getSqlSessionFactory() throws IOException {
        String resource = "mybatis-config.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        return sqlSessionFactory;
    }
	//获取SqlSession实例
    public static SqlSession getSqlSession() throws IOException {
        SqlSession sqlSession = MybatisUtils.getSqlSessionFactory().openSession();
        return sqlSession;
    }
}

  • 创建bean
public class Student {
    private String id;

    private String name;

    private int age;

    public Student() {
    }

    public Student(String id, String name, int age) {
        this.id = id;
        this.name = name;
        this.age = age;
    }

    public String getId() {
        return id;
    }

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

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Student{" +
                "id='" + id + '\'' +
                ", name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
  • 创建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.example.dao.ExampleDao">

    <select id="getOne" resultType="com.example.bean.Student">
        select *  from student limit 0,1
    </select>
</mapper>
  • 创建dao接口
public interface  ExampleDao {
    Student getOne();
}
  • 测试
//有两种方式调用dao接口
public class ExampleDaoTest {
    @Test
    public void getOne() throws IOException {
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        ExampleDao mapper = sqlSession.getMapper(ExampleDao.class);
        Student one = mapper.getOne();
        System.out.printf(one.toString());
    }
    @Test
    public void getOne2() throws IOException {
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        Student student = (Student)sqlSession.selectOne("com.example.dao.ExampleDao.getOne");
        System.out.printf(student.toString());
    }
}

sql和dao接口绑定

image-20220228171300554.png

作用域和生命周期

我们知道最终执行sql操作的是SqlSession,而获取SqlSession实例需要通过SqlSessionFactory,SqlSessionFactory的创建需要通多SqlSessionFactoryBuilder。

public class MybatisUtils {
    SqlSessionFactory sqlSessionFactory;
    public  SqlSessionFactory getSqlSessionFactory() throws IOException {
        String resource = "mybatis-config.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
        sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        return sqlSessionFactory;
    }
    
    public static SqlSession getSqlSession() throws IOException {
        MybatisUtils mybatisUtils = new MybatisUtils();
        if (mybatisUtils.sqlSessionFactory == null){
            mybatisUtils.getSqlSessionFactory();
        }
        SqlSession sqlSession = mybatisUtils.sqlSessionFactory.openSession();
        return sqlSession;
    }
}

通过代码可以看到

  • SqlSessionFactoryBuilder:主要是用来获取SqlSessionFactory实例的,所以一经创建SqlSessionFactory后就不在需要了,所以它的作用域也就是方法作用域。

  • SqlSessionFactory:使用来初始化SqlSession的,而在整个应用期间需要多次初始化SqlSession,所以SqlSessionFactory需要一直存在,所以最佳作用域是应用作用域

  • SqlSession:每个线程都应该有自己的SqlSession,因为不是线程安全的,所以每个请求完成后需要销毁。最佳的作用域是请求或方法作用域。

实现CRUD操作

select

  • select是在项目开发中用到的最多的标签
  • 在select标签中有很多属性
    1. id:命名空间中的唯一标识,同时也是用来绑定dao层接口的方法名
    2. parameterType:参数类型
    3. resultType:sql的返回值类型。

实例:根据id查询student表

  1. 在ExampleDao接口中添加接口

    public interface  ExampleDao {
        Student getOne();
    
        Student getOneById(@Param("id")String id);
    }
    
  2. 在mapper.xml文件中添加sql

    <?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.example.dao.ExampleDao">
    
        <select id="getOne" resultType="com.example.bean.Student">
            select *  from student limit 0,1
        </select>
    
        <select id="getOneById" resultType="com.example.bean.Student">
            select *  from student where  id = "${id}"
        </select>
    </mapper>
    
  3. 写测试类测试

    @Test
    public void getOneById() throws IOException {
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        ExampleDao mapper = sqlSession.getMapper(ExampleDao.class);
        Student one = mapper.getOneById("1");
        System.out.printf(one.toString());
    }
    

注意:

  1. 在接口方法的参数前加 @Param属性,注意 尽量每个参数都写,养成好的习惯和代码规范,Sql语句编写的时候,直接取@Param中设置的值即可,不需要单独设置参数类型
  2. 使用万能的Map,在接口方法中,参数直接传递Map;编写sql语句的时候,需要指定传递参数类型parameterType,参数类型为map,在使用方法的时候,Map的 key 为 sql中取的值即可,没有顺序要求!非常适用参数较多的情况。

insert

insert标签是做插入和新增数据操作的,其中的属性和select差不太多。

实例:新增student数据

  1. 书写dao层接口

    public interface  ExampleDao {
        Student getOne();
    
        Student getOneById(@Param("id")String id);
    
        int addOne(Student student);
    }
    
  2. 书写mapper.xml文件中的sql

    <insert id="addOne" parameterType="com.example.bean.Student">
            insert into student(id,name,age) value (#{id},#{name},#{age})
        </insert>
    
  3. 编写测试用例

    @Test
    public void addOne() throws IOException {
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        ExampleDao mapper = sqlSession.getMapper(ExampleDao.class);
        Student student = new Student("2","222",23);
        int one = mapper.addOne(student);
        System.out.printf(one+"");
        sqlSession.commit();
    }
    

注意:增删改必须提交事务:sqlSession.commit();

update

update标签做更新操作

实例:更新student表中id为1的数据。

  1. 书写dao层接口

    public interface  ExampleDao {
        Student getOne();
    
        Student getOneById(@Param("id")String id);
    
        int addOne(Student student);
    
        int updateById(Student student);
    }
    
  2. 书写mapp.xml文件中的sql

    <update id="updateById" parameterType="com.example.bean.Student">
            update student set name = #{name},age= #{age} where id = #{id}
        </update>
    
  3. 编写测试用例

    @Test
    public void updateById() throws IOException {
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        ExampleDao mapper = sqlSession.getMapper(ExampleDao.class);
        Student student = new Student("2","333",41);
        int one = mapper.updateById(student);
        System.out.printf(one+"");
        sqlSession.commit();
    }
    

delete

delete标签是用来删除数据的

实例:根据条件删除student表数据

  1. 书写dao层接口

    public interface  ExampleDao {
        Student getOne();
    
        Student getOneById(@Param("id")String id);
    
        int addOne(Student student);
    
        int updateById(Student student);
    
        int deleteByInfo(Student student);
    }
    
  2. 书写mapp.xml文件中的sql

    <delete id="deleteByInfo" parameterType="com.example.bean.Student">
            delete from student where id = #{id}
        </delete>
    
  3. 编写测试用例

    @Test
    public void deleteByInfo() throws IOException {
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        ExampleDao mapper = sqlSession.getMapper(ExampleDao.class);
        Student student = new Student("2","333",41);
        int one = mapper.deleteByInfo(student);
        System.out.printf(one+"");
        sqlSession.commit();
    }
    

中级

配置文件

核心配置文件

在mybatis中的核心配置文件是mybatis-config.xml ,通过该文件可以配置mybatis的一些核心功能。

核心配置项如下:

configuration(配置)
    properties(属性)
    settings(设置)
    typeAliases(类型别名)
    typeHandlers(类型处理器)
    objectFactory(对象工厂)
    plugins(插件)
    environments(环境配置)
        environment(环境变量)
            transactionManager(事务管理器)
            dataSource(数据源)
    databaseIdProvider(数据库厂商标识)
    mappers(映射器)

属性(properties)

通过配置属性可以将mybatis-config.xml中的一些配置信息抽离出来房子啊单独的文件中或者定义成变量,并可以进行动态替换。

//创建mysql.properties文件,将相关信息提出来
driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://10.36.2.65:6612/ta
password=talent
<?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属性引入外部配置文件-->
    <properties resource="mysql.properties">
        <!--内部配置变量-->
        <property name="username" value="root"/>
    </properties>
    <!--数据库链接配置-->
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <!--mysql驱动-->
                <property name="driver" value="${driver}"/>
                <!--mysql链接url-->
                <property name="url" value="${url}"/>
                <!--mysql用户名-->
                <property name="username" value="${username}"/>
                <!--密码-->
                <property name="password" value="${password}"/>
            </dataSource>
        </environment>
    </environments>
    <mappers>
        <mapper resource="example.xml"/>
    </mappers>
</configuration>

如上是通过在mybatis-config.xml中使用properties标签的resource属性将外部配置文件引入

除了这种方式还有另外的两种方式

也可以在 SqlSessionFactoryBuilder.build() 方法中传入属性值。例如:
1.SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader, props);

2.SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader, environment, props);
//通过这两行代码可以看到我们可以指定外部配置文件,也可指定生效的数据源。

如果一个配置项在多个地方进行了配置,加载顺序如下:

  • 首先读取在 properties 元素体内指定的属性。
  • 然后根据 properties 元素中的 resource 属性读取类路径下属性文件,或根据 url 属性指定的路径读取属性文件,并覆盖之前读取过的同名属性。
  • 最后读取作为方法参数传递的属性,并覆盖之前读取过的同名属性。

从 MyBatis 3.4.2 开始,你可以为占位符指定一个默认值。例如:

<dataSource type="POOLED">
  <!-- ... -->
  <property name="username" value="${username:ut_user}"/> <!-- 如果属性 'username' 没有被配置,'username' 属性的值将为 'ut_user' -->
</dataSource>

这个特性默认是关闭的。要启用这个特性,需要添加一个特定的属性来开启这个特性。例如:

<properties resource="org/mybatis/example/config.properties">
  <!-- ... -->
  <property name="org.apache.ibatis.parsing.PropertyParser.enable-default-value" value="true"/> <!-- 启用默认值特性 -->
</properties>

environment

environment是数据源配置,通过观察配置文件可以看到可以在environments节点中配置多套数据源信息,但是生效的只能有一套,通过在environments节点的default属性进行指定。

  1. 子元素节点:transactionManager - [ 事务管理器 ]

    <transactionManager type="[ JDBC | MANAGED ]"/>
    
  2. 子元素节点:dataSource,数据源配置,必须配置

    其中属性type用于指定数据源类型,内置的数据源类型有三种 UNPOOLED 、 POOLED 、 JNDI

    1. UNPOOLED :每次请求都会打开和关闭,在企业级应用中性能比较差
    2. POOLED :提出了池的概念,每次请求都会去池中获取链接,使用完成后就会将连接放入到池中。
    3. JNDI:这个数据源的实现是为了能在如 Spring 或应用服务器这类容器中使用,容器可以集中或在外部配置数据源,然后放置一个 JNDI 上下文的引用。
    4. 还有一些第三方的数据源类型:比如dbcp,c3p0,druid等等….

设置(settings)

完整的 settings 元素的示例如下:

<settings>
  <setting name="cacheEnabled" value="true"/>
  <setting name="lazyLoadingEnabled" value="true"/>
  <setting name="multipleResultSetsEnabled" value="true"/>
  <setting name="useColumnLabel" value="true"/>
  <setting name="useGeneratedKeys" value="false"/>
  <setting name="autoMappingBehavior" value="PARTIAL"/>
  <setting name="autoMappingUnknownColumnBehavior" value="WARNING"/>
  <setting name="defaultExecutorType" value="SIMPLE"/>
  <setting name="defaultStatementTimeout" value="25"/>
  <setting name="defaultFetchSize" value="100"/>
  <setting name="safeRowBoundsEnabled" value="false"/>
  <setting name="mapUnderscoreToCamelCase" value="false"/>
  <setting name="localCacheScope" value="SESSION"/>
  <setting name="jdbcTypeForNull" value="OTHER"/>
  <setting name="lazyLoadTriggerMethods" value="equals,clone,hashCode,toString"/>
</settings>

类型别名(typeAliases)

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

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

也可以指定一个包名,MyBatis 会在包名下面搜索需要的 Java Bean,比如:

<typeAliases>
  <package name="domain.blog"/>
</typeAliases>

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

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

映射器(mappers)

有三种方式注册mapper.xml文件

<!-- 使用相对于类路径的资源引用 -->
<mappers>
  <mapper resource="org/mybatis/builder/AuthorMapper.xml"/>
</mappers>
<!-- 使用映射器接口实现类的完全限定类名 -->
//注意这种方式必须将xml文件和dao接口放在同一目录下,需要配置文件名称和接口名称一致,否则报错
<mappers>
  <mapper class="org.mybatis.builder.AuthorMapper"/>
</mappers>
<!-- 将包内的映射器接口实现全部注册为映射器 但是需要配置文件名称和接口名称一致,并且位于同一目录下-->
<mappers>
  <package name="org.mybatis.builder"/>
</mappers>

resultMap

resultMap的主要作用是解决数据库字段名和bean类属性名不一致的问题。

也能解决一些复杂的一对多和多对一的复杂查询.

测试实例:

  1. 我们将student表的age字段修改成rel_age。

  2. 书写dao层接口

    public interface  ExampleDao {
        Student getOne();
    }
    
  3. 书写mapper.xml文件resultMap和select

    <mapper namespace="com.example.dao.ExampleDao">
    
        <resultMap id="student" type="com.example.bean.Student">
            <!--使用result字段解决数据库表字段和实体类字段不一致 property代表实体类字段  column代表表字段 -->
            <result property="age" column="rel_age"></result>
        </resultMap>
    
        <select id="getOne" resultMap="student">
            select *  from student limit 0,1
        </select>
    </mapper>
    
  4. 书写测试用例

    @Test
    public void getOne() throws IOException {
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        ExampleDao mapper = sqlSession.getMapper(ExampleDao.class);
        Student one = mapper.getOne();
        System.out.printf(one.toString());
    }
    

分页查询

分页查询我们通常有以下几种方式:针对mysql数据库

  1. limit关键字--基于sql层面实现分页查询
  2. RowBounds类--基于java代码层面实现分页查询
  3. PageHelper插件--基于第三方插件实现分页查询

我们在此处重点演示基于RowBounds实现分页查询的操作.

  1. 书写dao层接口

    public interface  ExampleDao {
        List<Student> getAll();
    }
    
  2. 书写mapper.xml中的sql

    <select id="getAll" resultType="com.example.bean.Student">
            select *
            from student;
        </select>
    
  3. 书写测试用例

    @Test
    public void getALL() throws IOException {
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        int currentPage = 2;  //第几页
        int pageSize = 2;  //每页显示几个
        RowBounds rowBounds = new RowBounds((currentPage-1)*pageSize,pageSize);
        List<Student> students = sqlSession.selectList("com.example.dao.ExampleDao.getAll", null, rowBounds);
        System.out.printf(String.valueOf(students));
    }
    

注解开发

mybatis最初配置信息是基于 XML ,映射语句(SQL)也是定义在 XML 中的。在MyBatis 3后提供了新的基于注解的配置。Java 注解的的表达力和灵活性十分有限。最强大的 MyBatis 映射并不能用注解来构建

sql 类型主要分成:

  • @select()
  • @update()
  • @insert()
  • @delete()

括号中书写sql,参数接收可在xml中一致

高级

多对一

在resultMap标签中使用association标签

比如:

public class Student {
     private int id;
     private String name;
     //多个学生可以是同一个老师,即多对一
     private Teacher teacher;
 }

在mapper.xml文件中编写查询,获取所有的学生信息(包括学生信息中对应的老师信息)

  • 方式一:通过嵌套查询的方式,在association标签中通过select属性绑定了另一个查询.
<mapper namespace="com.kuang.mapper.StudentMapper">
    <!--
    需求:获取所有学生及对应老师的信息
    思路:
        1. 获取所有学生的信息
        2. 根据获取的学生信息的老师ID->获取该老师的信息
        3. 思考问题,这样学生的结果集中应该包含老师,该如何处理呢,数据库中我们一般使用关联查询?
            1. 做一个结果集映射:StudentTeacher
            2. StudentTeacher结果集的类型为 Student
            3. 学生中老师的属性为teacher,对应数据库中为tid。
               多个 [1,...)学生关联一个老师=> 一对一,一对多
            4. 查看官网找到:association – 一个复杂类型的关联;使用它来处理关联查询
    -->
    <select id="getStudents" resultMap="StudentTeacher">
      select * from student
    </select>
    <resultMap id="StudentTeacher" type="Student">
        <!--association关联属性  property属性名 javaType属性类型 column在多的一方的表中的列名-->
        <association property="teacher"  column="tid" javaType="Teacher" select="getTeacher"/>
    </resultMap>
    <!--
    这里传递过来的id,只有一个属性的时候,下面可以写任何值
    association中column多参数配置:
        column="{key=value,key=value}"
        其实就是键值对的形式,key是传给下个sql的取值名称,value是片段一中sql查询的字段名。
    -->
    <select id="getTeacher" resultType="teacher">
        select * from teacher where id = #{id}
    </select>
</mapper>
  • 方式二:通过处理查询结果实现

    <!--
    按查询结果嵌套处理
    思路:
        1. 直接查询出结果,进行结果集的映射
    -->
    <select id="getStudents2" resultMap="StudentTeacher2" >
        select s.id sid, s.name sname , t.name tname
        from student s,teacher t
        where s.tid = t.id
    </select>
    <resultMap id="StudentTeacher2" type="Student">
        <id property="id" column="sid"/>
        <result property="name" column="sname"/>
        <!--关联对象property 关联对象在Student实体类中的属性-->
        <association property="teacher" javaType="Teacher">
            <result property="name" column="tname"/>
        </association>
    </resultMap>
    

一对多

在resultMap标签中使用collection标签

比如:

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

实例:实现获取指定老师,及老师下的所有学生

  • 方式一:通过处理查询结果实现

    <mapper namespace="com.kuang.mapper.TeacherMapper">
        <!--
        思路:
            1. 从学生表和老师表中查出学生id,学生姓名,老师姓名
            2. 对查询出来的操作做结果集映射
                1. 集合的话,使用collection!
                    JavaType和ofType都是用来指定对象类型的
                    JavaType是用来指定pojo中属性的类型
                    ofType指定的是映射到list集合属性中pojo的类型。
        -->
        <select id="getTeacher" 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=#{id}
        </select>
        <resultMap id="TeacherStudent" type="Teacher">
            <result  property="name" column="tname"/>
            <collection property="students" ofType="Student">
                <result property="id" column="sid" />
                <result property="name" column="sname" />
                <result property="tid" column="tid" />
            </collection>
        </resultMap>
    </mapper>
    
  • 方式二:通过嵌套查询实现,通过在collection标签中使用select属性绑定子查询

    <select id="getTeacher2" resultMap="TeacherStudent2">
      select * from teacher where id = #{id}
    </select>
    <resultMap id="TeacherStudent2" type="Teacher">
        <!--column是一对多的外键 , 写的是一的主键的列名-->
        <collection property="students" javaType="ArrayList" ofType="Student" column="id" select="getStudentByTeacherId"/>
    </resultMap>
    <select id="getStudentByTeacherId" resultType="Student">
        select * from student where tid = #{id}
    </select>
    

注意:

JavaType和ofType都是用来指定对象类型的

  • JavaType是用来指定pojo中属性的类型
  • ofType指定的是映射到list集合属性中pojo的类型。

动态sql&sql片段

这块内容不想写了,以后在写吧

mybatis缓存

什么是缓存

将经常查询的数据存放在内存中

为什么需要缓存

频繁的和数据库交互是非常降低性能的,所以可以将经常查询且不常改变的数据放入内存中,我们获取数据时去内存中获取数据.

mybatis中的缓存

在mybatis中分为一级缓存和二级缓存.

  • 一级缓存:默认情况下是开启的,SqlSession级别的缓存,也称为本地缓存

    可以通过打印日志发现,对于同一个sqlSession进行的同一查询,会发现只和数据库交互了一次,但是对于不同的SqlSession,会交互多次.

    一级缓存失效:

    1. SqlSession调用了close方法
    2. 对于同一SqlSession,执行了增删改操作后,缓存也会失效.
    3. SqlSession调用了clearCache()方法手动清理缓存.
  • 二级缓存:二级缓存需要手动开启,是基于namespace的缓存,也就是说在同一namespace的mapper.xml文件中.

    因为一级缓存的作用域太低了,所以有了二级缓存.在一个namespace下对应一个二级缓存.

    二级缓存生效:开启二级缓存后,当一级缓存关闭后,也就是调用了close后,会将数据放入二级缓存.

    不同的mapper查出的数据会放在自己对应的缓存(map)中;

  • 为了提高扩展性,MyBatis定义了缓存接口Cache。我们可以通过实现Cache接口来自定义二级缓存

二级缓存使用

  1. 开启二级缓存

    在mybatis-config.xml配置文件中添加缓存开启配置

    <setting name="cacheEnabled" value="true"/>
    
  2. 在mapper.xml中配置使用二级缓存

    <cache
        eviction="FIFO"  //缓存清除策略
        flushInterval="60000"  //缓存刷新间隔
        size="512" //缓存可以存放的数目
        readOnly="true"/>
    

    这个更高级的配置创建了一个 FIFO 缓存,每隔 60 秒刷新,最多可以存储结果对象或列表的 512 个引用,而且返回的对象被认为是只读的,因此对它们进行修改可能会在不同线程中的调用者产生冲突。

    可用的清除策略有:

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

    默认的清除策略是 LRU。

mybatis执行流程图

kuangstudyd3552e34-af83-4ce8-a4ab-45feb0fee43f.png

这个图在www.kuangstudy.com/zl/ssm#head….

源码分析

这块之后再写