MyBatis入门学习笔记

博主 960 2020-07-13

第一章 框架概述

1.1 软件开发常用架构
三层架构:界面层,业务层,数据访问层
1.2 JDBC编程的问题
重复操作多,开发效率多,需要自己处理查询过程和查询结果,对业务代码和数据库的操作混合在一起等
1.3 MyBatis概述
一个持久层框架,早期称为iBatis,代码开源在github
MyBatis是一个MyBatis Mapper Framerwork for Java(SQL映射框架)
1)sql mapper:sql映射
可以把数据表中的一行数据映射为一个java对象,操作这个对象就相当于操作数据库中的表
2)Data Access Objects(DAOs):数据访问,对数据库执行增删改查
MyBatis提供的功能:
1)提供了创建Connection,Statement,ResultSet的能力,不用开发人员再创建这些对象
2)提供了执行sql语句的能力,不用自己执行sql
3)提供了循环sql,把sql的结果转化为java对象,List集合的能力
4)提供了关闭资源的能力,关闭Connection,Statement,ResultSet资源
总结:开发人员需要做的就是提供sql语句。
MyBatis让开发人员集中精神写sql就可以了,不必自己创建连接,关闭连接等繁琐的操作

第二章 快速入门

实现步骤

  1. 新建的student表
  2. 加入maven的mybatis坐标,mysql驱动坐标
  3. 创建实体类,student 保存表中的一行数据
  4. 创建持久层的dao接口,定义操作数据库的方法
  5. 创建一个mybatis使用的配置文件
    叫做sql映射文件:写sql语句的,一般一个表一个sql映射文件
    这个文件是一个xml文件:文件创建在dao接口所在的目录,名字和接口名字相同
  6. 创建mybatis的主配置文件
    一个项目就一个主配置文件,其中提供了数据库的连接信息和sql映射文件的位置信息
  7. 创建使用mybatis类
    通过mybatis访问数据库

新建表

CREATE TABLE 
student(id INT PRIMARY KEY, 
`name` VARCHAR(50), 
email VARCHAR(100), 
age INT)
CHARSET = utf8;

INSERT INTO student VALUES 
(1001, "李四", "lisi@qq.com", 20),
(1002, "张三", "zhangsan@sina.com", 28);

添加依赖到pom.xml

<!-- mybatis依赖-->
    <dependency>
      <groupId>org.mybatis</groupId>
      <artifactId>mybatis</artifactId>
      <version>3.5.4</version>
    </dependency>
<!--mysql驱动-->
    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <version>5.1.6</version>
    </dependency>
<!-- 由于不再resources的非java文件不会被扫描,需要添加配置将与dao同目录下的文件赋值到target目录中-->
 <build>
    <resources>
      <resource>
        <directory>src/main/java</directory><!-- 文件所在的目录-->
        <includes><!-- 包括目录下的.properties和.xml文件都会扫描到-->
          <include>**/*.properties</include>
          <include>**/*.xml</include>
        </includes>
        <filtering>false</filtering>
      </resource>
    </resources>
  </build>

创建实体类:

public class Student {
    private Integer id;
    private String name;
    private String email;
    private Integer age;

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

    public Student() {
    }

    @Override
    public String toString() {
        return "Student{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", email='" + email + '\'' +
                ", age=" + age +
                '}';
    }

    public Integer getId() {
        return id;
    }

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

    public String getName() {
        return name;
    }

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

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    public Integer getAge() {
        return age;
    }

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

创建持久层的dao接口

package com.learn.dao;

import com.learn.domain.Student;

import java.util.List;

/**
 * @author wuminggao
 * @create 2020-07-13-下午2:19
 */
public interface StudentDao {

    //获取所有的student组成的集合数据
    public List<Student> getAllStudent();

}

在与dao相同目录下创建sql映射文件

<?xml version="1.0" encoding="UTF-8"?>

<!--指定约束文件的
    mybatis-3-mapper.dtd是约束文件的名称,扩展名是dtd的
    用来限制和检查,出现在当前文件中出现的标签,属性,必须符合mybatis的要求
-->
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<!--mapper是当前文件的跟标签,是必须有的
    namespace:命名空间,是一个唯一值,可以是自定义的字符串
    要求使用dao接口的全限定名称

    在当前文件中,可以使用特定的标签,表示数据库的特定操作
    <select:>查询语句
    <update:>更新语句
    <insert:>插入语句
    <delete:>删除语句

    select:表示查询操作
    属性的id:你要执行的sql语法的唯一标志,mybatis会使用这个id的值来找到要执行的sql语句
    可以自定义,但是要求使用接口中的方法名称
    resultType表示结果类型,是sql语句执行后得到的resultSet,遍历这个ResultSet得到的java对象的类型
    值写的是类型的全限定名称
-->
<mapper namespace="com.learn.dao.StudentDao">
    <select id="getAllStudent" resultType="com.learn.domain.Student">
        select id, `name`, email, age from student
    </select>
</mapper>

在resources目录下创建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">
<!--
    当前文件是mybatis的主配置文件
    主要定义了数据库的配置信息,sql映射文件的位置
    同样开头的
    <!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
     是约束文件,mybatis-3-config.dtd是约束文件的名称

     configuration是配置文件的跟标签
-->
<configuration>
<!--    环境配置:数据库的连接信息
        environments中的default属性的值必须和某个environment的id一样
-->
    <environments default="mydev">
<!--       environment:是一个数据库信息的配置,环境
           id:一个唯一值,自定义,表示环境的名称
 -->
        <environment id="mydev">
<!--
            transactionManager:mybatis的事务类型
            type的值有两个:JDBC(表示使用jdbc中的Connection对象的commit,rollback做事务处理)

            dataSource表示数据源,连接数据库的
            type:POOLED(表示使用数据库连接池)
-->
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
<!--
                properties属性定义了mybatis与数据库的连接信息
                name为driver url username password是固定的,不能自定义
                value
                url=jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf8&rewriteBatchedStatements=true
-->
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/ssm"/>
                <property name="username" value="root"/>
                <property name="password" value="123456"/>
            </dataSource>
        </environment>
    </environments>
<!--
    mappers表示sql映射文件的位置
    每个mapper标签制定一个文件的位置,从类路径开始的路径信息
    target/classes(类路径)/ 下的文件位置
-->
    <mappers>
<!--        <mapper resource="org/mybatis/example/BlogMapper.xml"/>-->
        <mapper resource="com/learn/dao/StudentDao.xml"></mapper>
    </mappers>
</configuration>

创建使用mybatis类,访问数据库

public class MyTest {
    public static void main(String[] args) throws IOException {
        //访问mybatis读取student数据

        //1. 定义mybatis的主配置文件的名称
        String config = "mybatis.xml";
        //2. 读取这个config表示的配置文件
        InputStream inputStream = Resources.getResourceAsStream(config);
        //3.创建sqlSessionFactoryBuilder对象
        SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
        //4.创建sqlSessionFactory对象
        SqlSessionFactory factory = builder.build(inputStream);
        //5.获取SqlSession对象,从SQLSessionFactory中获取sqlSession
        SqlSession sqlSession = factory.openSession();
        //6.指定要执行的sql语句的标志,sql映射文件中的namespace + "." + 标签的id值
        String sqlId = "com.learn.dao.StudentDao.getAllStudent";
        //7.执行sql语句,通过sqlId找到语句
        List<Student> students = sqlSession.selectList(sqlId);
        //8.输出结果
        students.forEach(System.out::println);
        //9.关闭sqlSession对象
        sqlSession.close();
    }
}

使用占位符执行插入操作

    <insert id="insertStudent">
        insert into student values (#{id}, #{name}, #{email}, #{age})
    </insert>

开启日志功能:
在mybatis.xml中加入

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

  1. 主要类的介绍

1)Resources类:MyBatis中的一个类,读取主配置文件,创建输入流
InputStream inputStream =
Resources.getResourceAsStream(config);

2)SqlSessionFactoryBuilder类:创建SqlSessionFactory对象
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
SqlSessionFactory factory = builder.build(inputStream);

3)SqlSessionFactory:重量级对象,程序创建一个实例耗时比较长,使用资源比较多,在整个项目中有一个就够用了。
SqlSessionFactory是一个接口,实现类有DefaultSqlSessionFactory
SqlSessionFactory作用:获取SqlSession对象,SqlSession sqlSession = factory.openSession();
openSession()方法说明:
openSession()无参数方法,获取的是非自动提交事务的SqlSession对象
openSession(boolean )有参数的,设置为true,则获取自动提交事务的SqlSession

4)SqlSession接口:接口定义了很多操作数据的方法
例如:selectOne(),selectList(),insert(),update(),delete(),commit(),rollback()
接口的默认实现类DefaultSqlSession
使用要求:SqlSession对象不是线程安全的,需要在方法内部使用,在执行sql语句之前,使用openSession()获取SqlSession对象,在执行完sql语句后,需要关闭它,执行close方法,这样能保证它的使用是线程安全的。

第三章 MyBatis框架Dao代理

3.1 使用MyBatis的动态代理
在定义好DAO接口和mapper映射文件后,在测试类中添加方法

@Test
    public void testStudentDao(){
        /**
         * 使用mybatis的动态代理机制,使用SqlSession.getMapper(dao接口)
         * getMapper获取接口的实现类对象
         *
         */
        SqlSession sqlSession = MyBatisUtils.getSqlSession();
        StudentDao studentDao = sqlSession.getMapper(StudentDao.class);
        List<Student> students = studentDao.selectStudents();
        students.forEach(System.out::println);
        sqlSession.close();
    }

即使用sqlSession.getMapper(dao接口.class )通过动态代理创建接口实现类,调用实现类的方法完成CRUD

3.2 如何把参数传入mapper映射文件的sql语句中
1)parameterType: 写在mapper文件中的一个属性。表示dao接口中方法的参数的数据类型。
例如StudentDao接口,public Student selectStudentById(Integer id)
首先在mappper文件中添加

<mapper namespace="com.learn.dao.StudentDao">
    <select id="selectStudentById" parameterType="java.lang.Integer" resultType="com.learn.domain.Student">
        select id, name, email, age from student where id = #{id};
    </select>
</mapper>

parameterType的值是java的数据类型全限定名称或者是mybatis定义的别名
注意:parameterType不是强制需要写的,mybatis通过反射机制能够发现接口参数的参数类型,一般不写。

3.3 将一个简单类型的参数传入mapper文件
简单类型:基本数据类型+字符串类型
在mapper文件获取简单类型的一个参数的值,使用 #(任意字符) eg: #

实际上是JDBC中的PreparedStatement的预编译语句中的占位符 '?',然后使用参数填充占位符。

3.4 传入多个参数到mapper文件
3.4.1 使用@Param命名参数
现在StudentDao接口中存在一个方法

List<Student> selectMultiParam(@Param("myName") String name,
                                   @Param("myAge") Integer age);

使用 @Param("参数名") 形参
mapper文件:

<select id="selectMultiParam" resultType="com.learn.domain.Student">
                select id, name, email, age from student where name = #{myName} or age = #{myAge};
    </select>

3.4.2 传递对象
在mapper映射文件中,写占位符时按如下格式写(很少使用):
#{对象的属性名, javaType=Java中类型名称, jdbcType=数据库中数据类型}
eg:#{paramName, javaType=java.lang.String, jdbcType=VARCHAR}
简化方式:#{属性名},javaType和jdbcType通过mybatis框架反射自动获取
现在StudentDao接口中存在一个方法

    List<Student> selectMultiByQueryParam(QueryParam queryParam);

//传递一个自定义类型的对象
public class QueryParam {
    private String paramName;
    private Integer paramAge;
	。。。下面是set和get方法,省略
}

mapper映射文件:

<select id="selectMultiByQueryParam" resultType="com.learn.domain.Student">
         select id, name, email, age from student where name = #{paramName} or age =  #{paramAge};
    </select>

3.4.3 按位置传递参数(了解)
在MyBatis3.3版本及之前通过#{0},#{1}方式,从MyBatis3.4开始使用#
方式。

3.4.4 使用map传递参数(了解)
形参写为map,在mapper映射文件使用#
获取存放的值

3.5 #和$的区别
井号#的意思:占位符,告诉MyBatis使用实际的参数填充占位符,并且使用PreparedStatement对象执行SQL语句,这样做更安全,更迅速,通常是首选做法
$的意思:告诉MyBatis使用$包含的”字符串“替换所在位置,使用Statement把SQL语句和${}的内容连接起来,主要用在替换表名,列名,不同列排序等操作
$就相当于Statement的拼串处理,存在sql注入问题

#和$的区别
1)#使用?在sql语句中做占位符,使用PreparedStatement执行sql,效率高
$相当于字符串拼串,使用Statement执行sql,效率低
2)#能避免sql注入问题,$不可以

3.6 MyBatis的输出结果
MyBatis执行了sql语句,得到java对象
1)resultType结果类型是自定义对象,指sql语句执行完毕后,数据转为java对象。
处理流程:
->MyBatis执行sql语句后,然后调用类的无参构造器,创建实例
->MyBatis将查询结果ResultSet的指定列值使用set方法注入实例中的属性,注意查询列名和属性名需要一致

2)resultType返回简单类型,比如查询行数,返回整型
<select id="selectCount" resultType="int">select count(*) from student;</select>
此处resultType的值int是java.lang.Integer的别名

定义自定义类型的别名方法:在主配置文件使用typeAlias标签,定义位置在settings标签后面
使用typeAlias指定全限定名称type和其别名alias

<!--    定义别名-->
    <typeAliases>
        <typeAlias type="com.learn.domain.Student" alias="stu"/>
    </typeAliases>

或者使用package标签,表示该包下所有类以其类名作为别名

    <typeAliases>
        <package name="com.learn.domain"/>
    </typeAliases>

注意!不建议使用别名,可能出现冲突

3)方法返回结果是一个Map类型
返回Map时,框架将列名作为键,查询得到的值作为键的值,返回这个Map。同时要求查询记录只有一行。

3.7 使用resultMap结果映射
指定列名和java对象的属性对应关系,用在想要自定义哪个列赋值给哪个属性时,或者当列名和属性列不一样时。

<!--    id自定义名称,type表示要转换的对象-->
    <resultMap id="studentMap" type="com.learn.domain.Student">
<!--        id标签是主键列 column是列名 property是java类型的属性名
result标签是非主键列
-->
        <id column="id" property="id"/>
        <result column="name" property="name"/>
        <result column="age" property="age"/>
        <result column="email" property="email"/>
    </resultMap>

还有另一种解决方式,在sql语句给字段取别名

<select id="selectAllStudents2" resultType="com.learn.domain.Student2">
        select id anId, name aName, email anEmail, age anAge from student;
    </select>

3.8 模糊查询
1)在java代码指定like的内容,将内容作为参数传入

<select id="selectLikeOne" resultType="com.learn.domain.Student">
        select id, name, email, age from student where name like #{name};
    </select>

eg:调用方法stuendtDao.selectLikeOne("%李%")

2)在mapper文件中拼接like的内容

<select id="selectLikeOne" resultType="com.learn.domain.Student">
        select id, name, email, age from student where name like "%" #{name} "%";
    </select>

不够灵活,不推荐

第四章 MyBatis框架动态SQL

动态SQL:sql的内容是变化的,可以根据条件获取到不同的sql语句,主要是where部分发生变化。
动态SQL的实现使用的是MyBatis提供的标签

4.1 环境准备
4.2 动态SQL之<if>

<!--
    if标签的使用
    使用参数java对象的属性值作为判断条件,直接使用属性=xxx值

-->
    <select id="selectStudentIf" resultType="com.learn.domain.Student">
        select id,name,age,email from student
        where 1=1
        <if test="name != null and name != '' ">
            name = #{name}
        </if>
        <if test="age > 0">
            or age >= #{age}
        </if>
    </select>

这个存在问题,前后多个if判断时的逻辑运算符and和or可能会语法错误!

4.3 动态SQL之<where>
where 标签是用来包含if标签的,当多个if有一个成立的时候,where会自动增加一个where关键字,并去掉if中多余的and,or

    <select id="selectStudentWhere" resultType="com.learn.domain.Student">
        select id,name,age,email from student
        <where>
            <if test="name != null and name != '' ">
                name = #{name}
            </if>
            <if test="age > 0">
                or age >= #{age}
            </if>
        </where>
    </select>

4.4 动态SQL之<foreach>
foreach用来做循环处理,循环java中的数组或者list集合,主要用在sql的in子句中
首先认识foreach标签的属性

        <foreach collection="" item="" open="" close="" separator="">

        </foreach>

collection:表示接口中的方法参数的类型,如果是数组使用array,如果是list集合使用list
item:自定义的,表示数组和集合成员的遍历
open:循环开始时的字符
close:循环结束时的字符
separator:集合成员之间的分隔符
接口中的方法:

List<Student> selectForEachOne(List<Integer> idList);

mapper映射文件:

    <select id="selectForEachOne" resultType="com.learn.domain.Student">
        select * from student where id in
        <foreach collection="list" item="id" open="(" close=")" separator=",">
            #{id}
        </foreach>
    </select>

传入的集合可以是对象集合,元素item是对象,如下,传入的参数是一个Student对象组成的list集合,使用对象.属性获取值

    <select id="selectForEachOneByStudentId" resultType="com.learn.domain.Student">
        select * from student where id in
        <foreach collection="list" item="stu" open="(" close=")" separator=",">
            #{stu.id}
        </foreach>
    </select>

4.5 动态SQL之代码片段
可以重复使用的sql语句
使用sql标签定义代码片段,id是自定义的代码片段id

<sql id="selectAllColumns">
        select id,name,age,email from student
</sql>

使用只需要在需要的地方include进去即可,refid填写代码片段id

    <select id="selectForEachOneByStudentId" resultType="com.learn.domain.Student">
        <include refid="selectAllColumns"/> where id in
        <foreach collection="list" item="stu" open="(" close=")" separator=",">
            #{stu.id}
        </foreach>
    </select>

第五章 MyBatis配置文件

5.1 主配置文件
以这个主配置文件为例

<?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>
    <settings>
        <setting name="logImpl" value="STDOUT_LOGGING"/>
    </settings>

    定义别名
    <typeAliases>
<!--        <typeAlias type="com.learn.domain.Student" alias="stu"/>-->
        <package name="com.learn.domain"/>
    </typeAliases>

    <environments default="mydev">
        <environment id="mydev">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url"
                          value="jdbc:mysql://localhost:3306/ssm?useUnicode=true&amp;characterEncoding=utf8"/>
                <property name="username" value="root"/>
                <property name="password" value="123456"/>
            </dataSource>
        </environment>
    </environments>

    <mappers>
        <mapper resource="com/learn/dao/StudentDao.xml"></mapper>
    </mappers>
</configuration>

settings:是一些全局配置,可以参考官方文档

environments:是一些环境配置,其中transactionManager是提交事务,回滚事务的方式,有几个值:1)JDBC: 表示mybatis底层使用JDBC中的Connection对象的commit,rollback 2)MANAGED:表示把mybatis的事务处理委托给其他的容器(例如服务器软件,spring框架)

5.2 dataSource标签
dataSource:表示数据源,java体系中,规定实现了javax.sql.DataSource接口的都是数据源。数据源表示Connection对象
dataSoruce标签有type属性:1)POOLED,使用连接池,mybatis会创建PooledDataSource类。2)UNPOOLED,不使用连接池,每次执行sql语句都创建连接并关闭连接。3)JNDI:java命名和目录服务(windows注册表)

5.3 事务

5.4 使用数据库属性配置文件
把数据库连接信息放到一个单独的文件中,和mybatis主配置文件分开,便于修改,保存,处理多个数据库的信息
1)在resources目录中定义一个properties文件
配置文件中,定义数据,格式是key-value
key:一般使用多级目录的表示法。
例如 jdbc.mysql.driver
2)在主配置文件使用${key}的方式获得配置文件中的值

5.5 typeAliases(类型别名)
typeAliases:配置类的别名,有两种方式
package和typeAlias

<typeAlias type="com.learn.domain.Student" alias="stu"/>
<package name="com.learn.domain"/>

5.6 mapper(映射器)
如果有多个映射文件,需要都进行指定
方式一:手动指定多个mapper文件

<mappers>
        <mapper resource="com/learn/dao/StudentDao.xml"></mapper>
        <mapper resource="com/learn/dao/xxxx2Dao.xml"></mapper>
        <mapper resource="com/learn/dao/xxxx1Dao.xml"></mapper>
    </mappers>

方式二:使用包名
这种方式中,所有xml文件一次都全部加载给mybatis
需要mapper文件名称需要和对应的接口名称一样,区分大小写
还需要mapper文件和dao接口需要在同一目录

<mappers>
        <package name="com.learn.dao"/>
</mappers>

第六章 扩展

6.1 PageHelper
用于做数据分页
1)添加maven依赖

    <dependency>
      <groupId>com.github.pagehelper</groupId>
      <artifactId>pagehelper</artifactId>
      <version>5.1.10</version>
    </dependency>

2)在主配置文件的environment前添加

    <plugins>
        <plugin interceptor="com.github.pagehelper.PageInterceptor"></plugin>
    </plugins>

3)在需要分页的查询前调用PageHelper.startPage(3, 3);
第一个参数pageNum表示第几页,第二个参数pageSize表示一页有多少条记录