为什么要使用MyBatis?

MyBatis是一个半自动化的持久化层框架。

  • sql和java编码分开,功能边界清晰,一个专注业务、一个专注数据。
  • MyBatis 是支持定制化SQL、存储过程以及高级映射的优秀的持久层框架。
  • MyBatis可以使用简单的XML或注解用于配置和原始映射,将接口和Java的POJO(Plain Old Java Objects,普通的Java对象)映射成数据库中的记录。

下载MyBatis

传送门:https://github.com/mybatis/mybatis-3/

(这里使用的是3.4.1版本)

初探MyBatis

第一个例子

使用的IDE为MyEclipse

  • 创建一个数据库和一个数据表,并随意填入数据

    1
    2
    3
    4
    5
    6
    mysql> create table tbl_employee(
    -> id int(11) primary key auto_increment,
    -> last_name varchar(255),
    -> gender char(1),
    -> email varchar(255)
    -> );
    1
    2
    3
    4
    5
    6
    7
    8
    mysql> select * from tbl_employee;
    +----+-----------+--------+--------------+
    | id | last_name | gender | email |
    +----+-----------+--------+--------------+
    | 1 | tom | 0 | 13142@qq.com |
    | 2 | jerry | 1 | 4243@qq.com |
    +----+-----------+--------+--------------+
    2 rows in set
  • 创建一个javaEE web项目

  • 在项目中定义一个javabean

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    // Employee.java
    package com.myBatis;

    public class Employee {
    private Integer id;
    private String last_name;
    private String email;
    private String gender;
    public Integer getId() {
    return id;
    }
    public void setId(Integer id) {
    this.id = id;
    }
    public String getLast_name() {
    return last_name;
    }
    public void setLast_name(String last_name) {
    this.last_name = last_name;
    }
    public String getEmail() {
    return email;
    }
    public void setEmail(String email) {
    this.email = email;
    }
    public String getGender() {
    return gender;
    }
    public void setGender(String gender) {
    this.gender = gender;
    }
    @Override
    public String toString() {
    return "Employee [id=" + id + ", last_name=" + last_name + ", email="
    + email + ", gender=" + gender + "]";
    }

    }
  • 导入相关jar包

20210121192316851.png

  • 在根目录新建conf资源文件夹

  • 在conf文件夹下创建EmployeeMapper.xml文件,并添加如下代码

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    <?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.atguigu.mybatis.EmployeeMapper">
    <!-- namespace:命名空间
    id: 唯一标识
    resultType:返回值类型
    -->
    <select id="selectEmp" resultType="com.myBatis.Employee">
    <!-- tbl_employee为表名 -->
    select * from tbl_employee where id = #{id}
    </select>
    </mapper>
  • 在conf文件夹下再创建mybatis-config.xml文件,添加如下代码

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    <?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.jdbc.Driver" />
    <property name="url" value="jdbc:mysql://localhost:3306/mybatis" />
    <property name="username" value="root" />
    <property name="password" value="root" />
    </dataSource>
    </environment>
    </environments>
    <mappers>
    <!-- 将写好的sql文件映射到全局文件中 -->
    <mapper resource="EmployeeMapper.xml" />
    </mappers>
    </configuration>
  • 新建一个测试类test

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    // MybatisTest.java
    package com.test;

    import java.io.IOException;
    import java.io.InputStream;

    import org.apache.ibatis.io.Resources;
    import org.apache.ibatis.session.SqlSession;
    import org.apache.ibatis.session.SqlSessionFactory;
    import org.apache.ibatis.session.SqlSessionFactoryBuilder;

    import com.myBatis.Employee;

    public class MybatisTest {

    public void test() throws IOException {
    String resource = "mybatis-config.xml";
    InputStream inputStream = Resources.getResourceAsStream(resource);
    // 1.根据xml配置文件(全局配置文件)创建一个SqlSessionFactory对象
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder()
    .build(inputStream);
    // 2.获取sq1Sess1on实例,能直接执行已经映射的sq1语句
    SqlSession opensession = sqlSessionFactory.openSession();
    try {
    // 3.映射到查询命名空间.id的sql语句,传递一个参数1,查询第一个员工并自动封装到javabean中
    Employee employee = opensession.selectOne(
    "com.atguigu.mybatis.EmployeeMapper.selectEmp", 1);
    System.out.println(employee);
    } finally {
    // 关闭
    opensession.close();
    }
    }
    }

  • 新建一个servlet模块用于测试

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    // TestServlet.java
    package com.test;

    import java.io.IOException;
    import java.io.PrintWriter;

    import javax.servlet.ServletException;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;

    public class TestServlet extends HttpServlet {

    /**
    * Constructor of the object.
    */
    public TestServlet() {
    super();
    }

    /**
    * Destruction of the servlet. <br>
    */
    public void destroy() {
    super.destroy(); // Just puts "destroy" string in log
    // Put your code here
    }

    /**
    * The doGet method of the servlet. <br>
    *
    * This method is called when a form has its tag value method equals to get.
    *
    * @param request the request send by the client to the server
    * @param response the response send by the server to the client
    * @throws ServletException if an error occurred
    * @throws IOException if an error occurred
    */
    public void doGet(HttpServletRequest request, HttpServletResponse response)
    throws ServletException, IOException {

    // 创建测试类并进行测试
    MybatisTest mTest=new MybatisTest();
    try {
    mTest.test();
    } finally {
    // TODO: handle exception
    response.setContentType("text/html");
    PrintWriter out = response.getWriter();
    out.println("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">");
    out.println("<HTML>");
    out.println(" <HEAD><TITLE>A Servlet</TITLE></HEAD>");
    out.println(" <BODY>");
    out.print(" This is ");
    out.print(this.getClass());
    out.println(", using the GET method");
    out.println(" </BODY>");
    out.println("</HTML>");
    out.flush();
    out.close();
    }


    }

    /**
    * Initialization of the servlet. <br>
    *
    * @throws ServletException if an error occurs
    */
    public void init() throws ServletException {
    // Put your code here
    }

    }

  • 项目整体结构概览

20210121192316851.png

1
2
信息: Reloading Context with name [/HelloMyBatis] is completed
Employee [id=1, last_name=tom, email=13142@qq.com, gender=0]
  • 结束

使用接口进行改造(推荐)

  • 创建一个dao接口(com.dao/EmployeeMapper.java)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    // EmployeeMapper.java
    package com.dao;

    import com.myBatis.Employee;

    public interface EmployeeMapper {
    public Employee getEmpById(Integer id);
    }

  • conf/EmployeeMapper.xml文件中修改namespaceid

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    <?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.dao.EmployeeMapper">
    <!-- namespace:命名空间,这里将命名空间设为接口的全类名
    id: 唯一标识,这里将id设为接口的相应函数名
    resultType:返回值类型
    -->
    <select id="getEmpById" resultType="com.myBatis.Employee">
    <!-- tbl_employee为表名 -->
    select * from tbl_employee where id = #{id}
    </select>
    </mapper>
  • com.test/MybatisTest.java中新建一个test02方法进行测试

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    package com.test;

    import java.io.IOException;
    import java.io.InputStream;

    import org.apache.ibatis.io.Resources;
    import org.apache.ibatis.session.SqlSession;
    import org.apache.ibatis.session.SqlSessionFactory;
    import org.apache.ibatis.session.SqlSessionFactoryBuilder;

    import com.dao.EmployeeMapper;
    import com.myBatis.Employee;

    public class MybatisTest {

    //将获取SqlSessionFactory对象的方法进行封装
    public SqlSessionFactory getSqlSessionFactory() throws IOException {
    String resource = "mybatis-config.xml";
    InputStream inputStream = Resources.getResourceAsStream(resource);
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
    return sqlSessionFactory;
    }

    public void test02() throws IOException{
    // 1.获取sqlsessionfactory对象
    SqlSessionFactory sqlSessionFactory=getSqlSessionFactory();
    // 2.获取sqlsession对象
    SqlSession openSession=sqlSessionFactory.openSession();
    try {
    // 3.获取接口的实现类对象
    // 会为接口自动的创建一个代理对象,代理对象去执行增删改查方法
    EmployeeMapper mapper=openSession.getMapper(EmployeeMapper.class);
    // 获取查询结果
    Employee employee=mapper.getEmpById(1);
    System.out.println(employee);
    } finally {
    openSession.close();
    }
    }
    }
  • 在Servlet中调用该方法即可。

小结

  • 接口式编程
    原生:Dao ==> Daoimpl
    mybatis: Mapper(Dao) ==> xxMapper.xml

  • Sqlsession代表和据库的一次会话,用完必须关闭

  • SqlSess1on和 connection一样都是非线程安全,每次使用都应该去获取新的对象。

  • mapper接口没有实现类,但是 mybatis会为这个接口生一个代理对象.

    1
    2
    // 将接口进而xml进行绑定
    EmployeeMapper mapper=openSession.getMapper(EmployeeMapper.class);
  • 两个重要的置文件

    • mybatis的全局配置文件(conf/mybatis-config.xml):包含据库连接泡信息,事务管理器信息等,,,系统运行环境信息
    • sql映射文件:保存了每一个sql语句的映射信息。将sql抽取出来。

全局配置文件

第一步

  • 引入XML的dtd约束文件,方便编写XML的时候有提示。

  • 找到之前下载的mybatis.jar包,使用压缩软件打开,找到如下mybatis-3-mapper.dtdmybatis-3-config.dtd文件,并解压出来

20210121192316851.png
  • 在工具栏找到Window->Preferences->搜索XML Catalog,然后选择添加
20210121192316851.png 20210121192316851.png

properties

  • mybatis可以使用 properties来引入外 properties配置文件的内容
    • resource:引入类路径下的资源
    • url:引入网络路径或者路径下的资源
  • 创建一个properties文件写入mysql配置信息
20210121192316851.png
  • conf/mybatis-config.xml文件中进行改写,引入上面配置好的外部资源文件

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    <?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="dbconfig.properties"></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>
    <!-- 将写好的sql文件映射到全局文件中 -->
    <mapper resource="EmployeeMapper.xml" />
    </mappers>
    </configuration>

settings

  • 这是MyBatis 中极为重要的调整设置,它们会改变MyBatis 的运行时行为。

    20210121192316851.png

  • 一个例子

    1
    2
    3
    4
    5
    6
    <settings>
    <!-- 驼峰命名匹配
    当javabean中是lastName而mysql中是last_name时也可匹配
    -->
    <setting name="mapUnderscoreToCamelCase" value="true"/>
    </settings>

typeAliases别名处理器

  • typeAliases别名处理器:可以为我们的java类型起别名

    • type:指定要起别名的类型全类名;默认别名就是类名小写
    • alias:指定新的别名
  • 也可以为包下所有类取别名

    • package:为某个包下的所有类批量起别名
      name指定包名(为当前包以及下面所有的后代包的每一个类都起一个默认别名(类名小写)
  • 举例

    1
    2
    3
    4
    5
    <!-- 起别名 -->
    <typeAliases>
    <typeAlias type="com.myBatis.Employee" alias="emp"/>
    <package name="com.myBatis"/>
    </typeAliases>

environment

  • id:指定当前环境的唯一标识
  • transactionManager、和dataSource都必须有
  • transactionManager type:JDBC | MANAGED | 自定义
    • JDBC:使用了JDBC 的提交和回滚设置,依赖于从数据源得到的连接来管理事务范围。JdbcTransactionFactory
    • MANAGED:不提交或回滚一个连接、让容器来管理事务的整个生命周期(比如JEE 应用服务器的上下文)。ManagedTransactionFactory
    • 自定义:实现TransactionFactory接口,type=全类名/别名
  • dataSource type:UNPOOLED | POOLED | JNDI | 自定义
    • UNPOOLED:不使用连接池,UnpooledDataSourceFactory
    • POOLED:使用连接池,PooledDataSourceFactory
    • JNDI:在EJB 或应用服务器这类容器中查找指定的数据源
    • 自定义:实现DataSourceFactory接口,定义数据源的获取方式。
    • 实际开发中我们使用Spring管理数据源,并进行事务控制的配置来覆盖上述配置

databaseIdProvider

  • MyBatis 可以根据不同的数据库厂商执行不同的语句。
  • Type:DB_VENDOR
    • 使用MyBatis提供的VendorDatabaseIdProvider解析数据库厂商标识。也可以实现DatabaseIdProvider接口来自定义。
  • Property-name:数据库厂商标识
  • Property-value:为标识起一个别名,方便SQL语句使用databaseId属性引用

mapper

  • mapper逐个注册SQL映射文件

  • 也可使用批量注册(这种方式要求SQL映射文件名必须和接口名相同并且在同一目录下)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    mapper
    注册配置文件
    resource:引用类路径下的sq1映射文件
    mybatis/mapper/Employeemapper.xml
    ur1:引用网路路径或者路径下的sq1映射文件
    file: ///var/mappers/Authormapper.xml
    注册接口
    cass:引用(注册)接口
    1、有sq1映射文件,映射文件名必须和接口同名,并且放在与接口同一目录下
    2、没有sq1映射文件,所有的sql都是利用注解写在接口上

    <mappers>
    <!-- 将写好的sql文件映射到全局文件中 -->
    <mapper resource="EmployeeMapper.xml" />
    </mappers>

映射文件

映射文件指导着MyBatis如何进行数据库增删改查,有着非常重要的意义。

20210121192316851.png

在第一个例子上进行增删改查

  • com.dao.EmployeeMapper.java接口中添加增删改查函数

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    package com.dao;

    import com.myBatis.Employee;

    public interface EmployeeMapper {
    // 根据id查数据
    public Employee getEmpById(Integer id);

    // 增加数据
    public void addEmp(Employee employee);

    // 更新
    public void updateEmp(Employee employee);

    // 根据id删除数据
    public void deleteEmpById(Integer id);
    }
  • conf/EmployeeMapper.xml文件中添加代码进行绑定

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    <?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.dao.EmployeeMapper">
    <!-- namespace:命名空间,这里将命名空间设为接口的全类名
    id: 唯一标识,这里将id设为接口的相应函数名
    resultType:返回值类型
    -->
    <!-- 查询 -->
    <select id="getEmpById" resultType="com.myBatis.Employee">
    <!-- tbl_employee为表名 -->
    select * from tbl_employee where id = #{id}
    </select>

    <!-- 添加 parameterType可不写 -->
    <insert id="addEmp" parameterType="com.myBatis.Employee">
    insert into tbl_employee(last_name,gender,email)
    values(#{last_name},#{gender},#{email})
    </insert>

    <!-- 更新 -->
    <update id="updateEmp">
    update tbl_employee
    set last_name=#{last_name},gender=#{gender},email=#{email}
    where id=#{id}
    </update>


    <!-- 删除 -->
    <delete id="deleteEmpById">
    delete from tbl_employee where id=#{id}
    </delete>

    </mapper>
  • com.myBatis.Employee.java中加入构造器

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    public Employee() {
    super();
    }

    public Employee(Integer id, String last_name, String email, String gender) {
    super();
    this.id = id;
    this.last_name = last_name;
    this.email = email;
    this.gender = gender;
    }
  • com.test.MybatisTest.java中添加调用函数用于测试

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    package com.test;

    import java.io.IOException;
    import java.io.InputStream;

    import org.apache.ibatis.io.Resources;
    import org.apache.ibatis.session.SqlSession;
    import org.apache.ibatis.session.SqlSessionFactory;
    import org.apache.ibatis.session.SqlSessionFactoryBuilder;

    import com.dao.EmployeeMapper;
    import com.myBatis.Employee;

    public class MybatisTest {

    //将获取SqlSessionFactory对象的方法进行封装
    public SqlSessionFactory getSqlSessionFactory() throws IOException {
    String resource = "mybatis-config.xml";
    InputStream inputStream = Resources.getResourceAsStream(resource);
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
    return sqlSessionFactory;
    }

    // 查询
    public void test02() throws IOException{
    // 1.获取sqlsessionfactory对象
    SqlSessionFactory sqlSessionFactory=getSqlSessionFactory();
    // 2.获取sqlsession对象
    SqlSession openSession=sqlSessionFactory.openSession();
    try {
    // 3.获取接口的实现类对象
    // 会为接口自动的创建一个代理对象,代理对象去执行增删改查方法
    EmployeeMapper mapper=openSession.getMapper(EmployeeMapper.class);
    // 获取查询结果
    Employee employee=mapper.getEmpById(1);
    System.out.println(employee);
    } finally {
    openSession.close();
    }
    }

    // 添加
    public void test03() throws IOException {
    // 1.获取sqlsessionfactory对象
    SqlSessionFactory sqlSessionFactory=getSqlSessionFactory();
    // 2.获取sqlsession对象
    SqlSession openSession=sqlSessionFactory.openSession();
    try{
    // 3.获取接口的实现类对象
    EmployeeMapper mapper=openSession.getMapper(EmployeeMapper.class);
    Employee employee=new Employee(null,"xlh","xlh@qq.com","1");
    mapper.addEmp(employee);

    // 手动提交数据
    openSession.commit();
    }finally{
    openSession.close();
    }
    }

    // 修改
    public void test04() throws IOException {
    // 1.获取sqlsessionfactory对象
    SqlSessionFactory sqlSessionFactory=getSqlSessionFactory();
    // 2.获取sqlsession对象
    SqlSession openSession=sqlSessionFactory.openSession();
    try{
    // 3.获取接口的实现类对象
    EmployeeMapper mapper=openSession.getMapper(EmployeeMapper.class);
    Employee employee=new Employee(3,"zwx","zwx@qq.com","0");
    mapper.updateEmp(employee);

    // 手动提交数据
    openSession.commit();
    }finally{
    openSession.close();
    }
    }

    // 删除
    public void test05() throws IOException {
    // 1.获取sqlsessionfactory对象
    SqlSessionFactory sqlSessionFactory=getSqlSessionFactory();
    // 2.获取sqlsession对象
    SqlSession openSession=sqlSessionFactory.openSession();
    try{
    // 3.获取接口的实现类对象
    EmployeeMapper mapper=openSession.getMapper(EmployeeMapper.class);
    mapper.deleteEmpById(4);

    // 手动提交数据
    openSession.commit();
    }finally{
    openSession.close();
    }
    }
    }
  • 然后在Servlet中进行测试。

另外,可以直接返回Integer/Boolean/Long/void数据类型如下:

1
2
3
4
5
//com.dao.EmployeeMapper.java	更新
public boolean updateEmp(Employee employee);

// 在其调用函数中
return mapper.updateEmp(employee);
1
2
3
4
5
//com.dao.EmployeeMapper.java	更新
public boolean updateEmp(Employee employee);

// 在其调用函数中
return mapper.updateEmp(employee);

insert获取自增主键的值

  • 采用如下方式插入后,javabean的id为空
1
2
3
4
5
6
7
8
9
10
11
<!--  添加   parameterType可不写 -->
<insert id="addEmp" parameterType="com.myBatis.Employee">
insert into tbl_employee(last_name,gender,email)
values(#{last_name},#{gender},#{email})
</insert>


Employee employee=new Employee(null,"xlh","xlh@qq.com","1");
mapper.addEmp(employee);

// 此时employee.id为null
  • 进行修改
1
2
3
4
5
6
7
8
9
10
<!--  添加   parameterType可不写 -->
<!--
usegeneratedkeys="true"使用自主键获取主键值
keyproperty:指定对应的主键属性,也就是 mybatis获取到主健值以后,将这个值封装给 javabean的哪个属性
-->
<insert id="addEmp" parameterType="com.myBatis.Employee"
useGeneratedKeys="true" keyProperty="id" >
insert into tbl_employee(last_name,gender,email)
values(#{last_name},#{gender},#{email})
</insert>
  • 此时再调用addEmp后其javabean的id属性也会变

参数传递

单个参数

  • 可以接受基本类型,对象类型,集合类型的值。这种情况MyBatis可直接使用这个参数,不需要经过任何处理。

多个参数

  • 任意多个参数,都会被MyBatis重新包装成一个Map传入。Map的key是param1,param2,0,1…,值就是参数的值。

    1
    2
    3
    4
    5
    6
    <!-- 查询 -->
    <select id="getEmpById" resultType="com.myBatis.Employee">
    <!-- tbl_employee为表名 -->
    <!-- select * from tbl_employee where id = #{1} and last_name=#{2} -->
    select * from tbl_employee where id = #{param1} and last_name=#{param2}
    </select>
  • 命名参数

    • com.dao.EmployeeMapper.java接口的函数参数中添加注解

      1
      2
      //	根据id和name查数据
      public Employee getEmpBy(@Param("id")Integer id,@Param("last_name")String lastName );
    • 在映射文件中可以直接使用名字

      1
      2
      3
      4
      <!-- 查询 -->
      <select id="getEmpById" resultType="com.myBatis.Employee">
      select * from tbl_employee where id = #{id} and last_name=#{last_name}
      </select>
  • POJO

    当这些参数属于我们业务POJO时,我们直接传递POJO,即第一个例子使用的方法

  • Map

    我们也可以封装多个参数为map,直接传递.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    package com.dao;

    import java.util.Map;

    import com.myBatis.Employee;

    public interface EmployeeMapper {
    // map查询多参数
    public Employee getEmpByMap(Map<String, Object> map);
    }
    1
    2
    3
    4
    5
    <!-- 查询2 -->
    <select id="getEmpByMap" resultType="com.myBatis.Employee">
    <!-- tbl_employee为表名 -->
    select * from tbl_employee where id = #{id} and last_name=#{last_name}
    </select>
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    public void test06() throws IOException {
    // 1.获取sqlsessionfactory对象
    SqlSessionFactory sqlSessionFactory=getSqlSessionFactory();
    // 2.获取sqlsession对象
    SqlSession openSession=sqlSessionFactory.openSession(true); // 自动提交
    try{
    // 3.获取接口的实现类对象
    EmployeeMapper mapper=openSession.getMapper(EmployeeMapper.class);
    Map<String, Object> map=new HashMap<>();
    map.put("id", 1);
    map.put("last_name", "tom");
    Employee employee= mapper.getEmpByMap(map);
    System.out.println(employee);
    }finally{
    openSession.close();
    }
    }
  • 使用比较多的参数,可以另外编写一个类来实现

特别注意:如果是Collection(List、Set)类型或者是数组也会特殊处理,也是把传入的list或者数组封装在map中.

1
2
public Employee getEmpById(List<Integer> ids);
取值:#{ids[0]} #{list[1]}
  • #{key}:获取参数的值,预编译到SQL中。安全。
  • ${key}:获取参数的值,拼接到SQL中。有SQL注入问题。ORDER BY ${name}参数处理

Select元素

Select元素来定义查询操作。

  • Id:唯一标识符
    • 用来引用这条语句,需要和接口的方法名一致
  • parameterType:参数类型。
    • 可以不传,MyBatis会根据TypeHandler自动推断
  • resultType:返回值类型。
    • 别名或者全类名,如果返回的是集合,定义集合中元素的类型。不能和resultMap同时使用

20210121192316851.png

select返回List

  • EmployeeMapper.java
1
2
3
4
5
6
7
8
9
10
11
12
13
package com.dao;


import java.util.List;
import java.util.Map;

import com.myBatis.Employee;

public interface EmployeeMapper {
// 模糊查询
public List<Employee> getEmpsByLastname(String last_name);
}

  • EmployeeMapper.xml
1
2
3
4
5
6
<!-- 查询3  模糊查询,返回多个数据 List<Employee> getEmpsByLastname(String last_name);-->
<!-- 注意:这里的返回类型为List内部的类型,即Employee-->
<select id="getEmpsByLastname" resultType="com.myBatis.Employee">
<!-- tbl_employee为表名 -->
select * from tbl_employee where last_name like #{last_name}
</select>
  • MybatisTest.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public void test07() throws IOException {
// 1.获取sqlsessionfactory对象
SqlSessionFactory sqlSessionFactory=getSqlSessionFactory();
// 2.获取sqlsession对象
SqlSession openSession=sqlSessionFactory.openSession(true); // 自动提交
try{
// 3.获取接口的实现类对象
EmployeeMapper mapper=openSession.getMapper(EmployeeMapper.class);
List<Employee> listEmps=mapper.getEmpsByLastname("%小%");
for(Employee e : listEmps){
System.out.println(e);
}
}finally{
openSession.close();
}
}
  • 在Servlet中调用测试

    1
    2
    Employee [id=4, last_name=小明, email=xming@qq.com, gender=1]
    Employee [id=6, last_name=小红, email=xh@qq.com, gender=0]

select返回Map

  • EmployeeMapper.java

    1
    2
    // 返回一个map
    public Map<String, Object> getEmpInMap(Integer id);
  • EmployeeMapper.xml

    1
    2
    3
    4
    5
    <!-- 查询3 返回一个map,以键值对显示查询结果  getEmpInMap-->
    <select id="getEmpInMap" resultType="map">
    <!-- tbl_employee为表名 -->
    select * from tbl_employee where id = #{id}
    </select>
  • MybatisTest.java

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    public void test08() throws IOException {
    // 1.获取sqlsessionfactory对象
    SqlSessionFactory sqlSessionFactory=getSqlSessionFactory();
    // 2.获取sqlsession对象
    SqlSession openSession=sqlSessionFactory.openSession(true); // 自动提交
    try{
    // 3.获取接口的实现类对象
    EmployeeMapper mapper=openSession.getMapper(EmployeeMapper.class);
    Map<String,Object> map=mapper.getEmpInMap(1);
    System.out.println(map);
    }finally{
    openSession.close();
    }
    }
  • 在Servlet中调用测试

    1
    {id=1, email=13142@qq.com, last_name=tom, gender=0}

resultMap

resultMap实现高级结果集映射.

第一个例子
  • com.dao中新建一个EmployeeMapperPlus.java

    1
    2
    3
    4
    5
    6
    7
    8
    package com.dao;

    import com.myBatis.Employee;

    public interface EmployeeMapperPlus {
    // 根据id查数据
    public Employee getEmpById(Integer id);
    }
  • 在conf文件夹下创建一个EmployeeMapperPlus.xml

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    <?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.dao.EmployeeMapperPlus">
    <!-- type:自定义规则的java类型
    id:唯一标识
    -->
    <resultMap type="com.myBatis.Employee" id="MyEmp">
    <!-- id指定主键列的封装规则
    id定义主健,底层有优化
    column:指定哪一列
    property:指定对应的了javabean属性
    -->
    <id column="id" property="id" />
    <!-- result定义普通列的封装规则 -->
    <result column="last_name" property="last_name" />
    <result column="gender" property="gender" />
    <result column="email" property="email" />
    <!-- 其他不指定的列会自动封装 -->
    </resultMap>

    <select id="getEmpById" resultMap="MyEmp">
    select * from tbl_employee where id = #{id}
    </select>

    </mapper>
  • 记得在conf/mybatis-config.xml中添加映射文件

  • 添加测试代码

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    public void test09() throws IOException {
    // 1.获取sqlsessionfactory对象
    SqlSessionFactory sqlSessionFactory=getSqlSessionFactory();
    // 2.获取sqlsession对象
    SqlSession openSession=sqlSessionFactory.openSession(true); // 自动提交
    try{
    // 3.获取接口的实现类对象
    EmployeeMapperPlus mapper=openSession.getMapper(EmployeeMapperPlus.class);
    Employee emp=mapper.getEmpById(1);
    System.out.println(emp);
    }finally{
    openSession.close();
    }
    }
  • 调用servlet进行测试

多表查询(联合查询)

20210121192316851.png

(key为Employee的属性)

多表查询(嵌套结果集)

20210121192316851.png

多表查询(分段查询)

20210121192316851.png

开启延迟加载和属性按需加载

20210121192316851.png

Collection-集合类型&嵌套结果集

20210121192316851.png

20210121192316851.png

Collection-分步查询&延迟加载

20210121192316851.png

扩展-多列值封装map传递
  • 分步查询的时候通过column指定,将对应的列的数据传递过去,我们有时需要传递多列数据。使用{key1=column1,key2=column2…}的形式

20210121192316851.png

MyBatis-动态SQL

if

20210121192316851.png

choose

20210121192316851.png

trim

20210121192316851.png

set

20210121192316851.png

foreach

20210121192316851.png

MyBatis-缓存机制

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

一级缓存体验

  • 一级缓存(local cache), 即本地缓存, 作用域默认为sqlSession。当Session flush 或close 后, 该Session 中的所有Cache 将被清空。
  • 本地缓存不能被关闭, 但可以调用clearCache() 来清空本地缓存, 或者改变缓存的作用域.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public void test09() throws IOException {
// 1.获取sqlsessionfactory对象
SqlSessionFactory sqlSessionFactory=getSqlSessionFactory();
// 2.获取sqlsession对象
SqlSession openSession=sqlSessionFactory.openSession(true); // 自动提交
try{
// 3.获取接口的实现类对象
EmployeeMapperPlus mapper=openSession.getMapper(EmployeeMapperPlus.class);
// 第一次向数据库查询
Employee emp=mapper.getEmpById(1);
System.out.println(emp);
// 第二次查询时,同样的查询,则直接从缓存中获取
Employee emp2=mapper.getEmpById(1);
System.out.println(emp2);
// 因此两次查询获得的对象相同
System.out.println(emp==emp2);
}finally{
openSession.close();
}
}
1
2
3
Employee [id=1, last_name=tom, email=13142@qq.com, gender=0]
Employee [id=1, last_name=tom, email=13142@qq.com, gender=0]
true

一级缓存失效的情况

  • 不同的SqlSession对应不同的一级缓存
  • 同一个SqlSession但是查询条件不同
  • 同一个SqlSession两次查询期间执行了任何一次增删改操作
  • 同一个SqlSession两次查询期间手动清空了缓存

二级缓存

  • 二级缓存(second level cache),全局作用域缓存
  • 二级缓存默认不开启,需要手动配置
  • MyBatis提供二级缓存的接口以及实现,缓存实现要求POJO实现Serializable接口
  • 二级缓存在SqlSession 关闭或提交之后才会生效
  • 工作机制
    • 一个会话,查询一条数据,这个数据就会被放在当前会话的一级缓存中
    • 如果会话关闭:一级缓存中的数据会被保存到二级缓存中:新的会话查询信息,就可以参照二级缓存

使用

  • 全局配置文件(conf/mybatis-config.xml)中开启二级缓存

    1
    <setting name="cacheEnabled" value="true"/>
  • 需要使用二级缓存的映射文件(mapper.xml)处使用cache配置缓存

  • 注意:POJO需要实现Serializable接口(即 public class Employee implement Serializable)

  • 二级缓存相关属性

    • eviction=“FIFO”:缓存回收策略:
      • LRU –最近最少使用的:移除最长时间不被使用的对象。
      • FIFO –先进先出:按对象进入缓存的顺序来移除它们。
      • SOFT –软引用:移除基于垃圾回收器状态和软引用规则的对象。
      • WEAK –弱引用:更积极地移除基于垃圾收集器状态和弱引用规则的对象。
      • 默认的是LRU。
    • flushInterval:刷新间隔,单位毫秒
      • 默认情况是不设置,也就是没有刷新间隔,缓存仅仅调用语句时刷新
    • size:引用数目,正整数
      • 代表缓存最多可以存储多少个对象,太大容易导致内存溢出
    • readOnly:只读,true/false
      • true:只读缓存;会给所有调用者返回缓存对象的相同实例。因此这些对象不能被修改。这提供了很重要的性能优势
      • false:读写缓存;会返回缓存对象的拷贝(通过序列化)。这会慢一些,但是安全,因此默认是false。
    1
    2
    3
    <mapper namespace="com.atguigu.mybatis.dao.EmployeeMapper">
    <cache eviction="FIFO" flushInterval="60000" readOnly="false" size="1024"></cache>
    </mapper>
  • 二级缓存相关设置

    • 全局setting的cacheEnable
      • 配置二级缓存的开关。一级缓存一直是打开的。
    • select标签的useCache属性
      • 配置这个select是否使用二级缓存。一级缓存一直是使用的
    • sql标签的flushCache属性
      • 增删改默认flushCache=true。sql执行以后,会同时清空一级和二级缓存。查询默认flushCache=false。
    • sqlSession.clearCache()
      • 只是用来清除一级缓存。
    • 当在某一个作用域(一级缓存Session/二级缓存Namespaces) 进行了C/U/D 操作后,默认该作用域下所有select 中的缓存将被clear。

图解

20210121192316851.png