背景

  • 分页操作是特别常见的需求,不管做什么系统基本都是绕不开的

    依赖导入

  • maven依赖

    1. <dependency>
    2. <groupId>com.github.pagehelper</groupId>
    3. <artifactId>pagehelper</artifactId>
    4. <version>4.2.0</version>
    5. </dependency>

    配置

  • mybatis-config.xml ```java <?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">

  1. <settings>
  2. <!-- 开启驼峰命名匹配-->
  3. <setting name="mapUnderscoreToCamelCase" value="true"/>
  4. </settings>
<plugins>
    <plugin interceptor="com.github.pagehelper.PageHelper" >
    </plugin>
</plugins>
<environments default="development">
    <environment id="development">
        <transactionManager type="JDBC" />
        <dataSource type="POOLED">
            <property name="driver" value="${jdbc.driver}" />
            <property name="url" value="${jdbc.url}" />
            <property name="username" value="${jdbc.username}" />
            <property name="password" value="${jdbc.password}" />
        </dataSource>
    </environment>
</environments>
<!-- 将我们写好的sql映射文件(EmployeeMapper.xml)一定要注册到全局配置文件(mybatis-config.xml)中 -->
<mappers>
    <mapper resource="EmployeeMapper.xml" />
</mappers>


- 注意:配置文件有严格的书写顺序,如果报错百度或者看提示修改即可。
<a name="ow1PC"></a>
# 简单使用
```java
 @Test
    public void testMyBatis3Simple() throws IOException {
        SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
        SqlSession openSession = sqlSessionFactory.openSession();
        try {


            EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class);
            Page<Object> page = PageHelper.startPage(1, 5);
            List<Employee> emps = mapper.getEmps();

//            PageInfo<Employee> employeePageInfo = new PageInfo<>(emps);
            //     public PageInfo(List<T> list, int navigatePages) 两参数构造器 设置分页的数据以及连续展示几页
            PageInfo<Employee> employeePageInfo = new PageInfo<>(emps, 5);


            for (Employee emp : emps) {
                System.out.println(emp);
            }
            System.out.println("当前页码:" + page.getPageNum());
            System.out.println(page.getPages());
            System.out.println(page.getPageSize());

            System.out.println(employeePageInfo.getPageNum());
            System.out.println(employeePageInfo.getTotal());
            System.out.println(employeePageInfo.getPageSize());
            System.out.println(employeePageInfo.getPages());
            System.out.println(employeePageInfo.isIsFirstPage());
            System.out.println(employeePageInfo.isIsLastPage());

            int[] navigatepageNums = employeePageInfo.getNavigatepageNums();
            for (int i = 0; i < navigatepageNums.length; i++) {
                System.out.println(navigatepageNums[i]);
            }

        } finally {
            openSession.close();
        }
    }

插件生效原理

image.png

  • mybatis 在执行查询的过程中会创建三大对象,三大对象都会同时调用插件链,然后分别完成不同阶段的插件增强方法执行

    /**
       * 插件原理
       * 在四大对象创建的时候
       * 1、每个创建出来的对象不是直接返回的,而是
       *         interceptorChain.pluginAll(parameterHandler);
       * 2、获取到所有的Interceptor(拦截器)(插件需要实现的接口);
       *         调用interceptor.plugin(target);返回target包装后的对象
       * 3、插件机制,我们可以使用插件为目标对象创建一个代理对象;AOP(面向切面)
       *         我们的插件可以为四大对象创建出代理对象;
       *         代理对象就可以拦截到四大对象的每一个执行;
       * 
          public Object pluginAll(Object target) {
              for (Interceptor interceptor : interceptors) {
                target = interceptor.plugin(target);
              }
              return target;
            }
    
       */
    

    自定义TypeHandler

  • 理解typeHandler是什么

image.png

  • 在mybatis中负责完成javabean与jdbc数据数据类型映射的一个对象。
  • 简单来说,在java程序中,字符串定义为String类型,而数据库中则是char,varchar,typehandler就负责将这个过程进行转换。

    使用场景

  • 比如我们要将java中的枚举类型放入db中,首先复习一下枚举类 ```java — 简单定义 public enum StatusEnum { LOGIN,FAIL,LOGOUT;
    } — 但是实际应用场景一般都是比这更加复杂且封装更多

/**

  • 希望数据库保存的是100,200这些状态码,而不是默认0,1或者枚举的名
  • @author lfy / public enum EmpStatus { // 定义枚举对象,由 integer String组成 LOGIN(100,”用户登录”),LOGOUT(200,”用户登出”),REMOVE(300,”用户不存在”);
private Integer code;
private String msg;
private EmpStatus(Integer code,String msg){
    this.code = code;
    this.msg = msg;
}
public Integer getCode() {
    return code;
}

public void setCode(Integer code) {
    this.code = code;
}
public String getMsg() {
    return msg;
}
public void setMsg(String msg) {
    this.msg = msg;
}

//按照状态码返回枚举对象
public static EmpStatus getEmpStatusByCode(Integer code){
    switch (code) {
        case 100:
            return LOGIN;
        case 200:
            return LOGOUT;    
        case 300:
            return REMOVE;
        default:
            return LOGOUT;
    }
}

}

<a name="Cl3ze"></a>
# 自定义TypeHandler来完成存放状态码到db中
```java
package com.atguigu.mybatis.typehandler;

import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

import org.apache.ibatis.type.JdbcType;
import org.apache.ibatis.type.TypeHandler;

import com.atguigu.mybatis.bean.EmpStatus;

/**
 * 1、实现TypeHandler接口。或者继承BaseTypeHandler
 * @author lfy
 *
 */
public class MyEnumEmpStatusTypeHandler implements TypeHandler<EmpStatus> {

    /**
     * 定义当前数据如何保存到数据库中
     */
    @Override
    public void setParameter(PreparedStatement ps, int i, EmpStatus parameter,
            JdbcType jdbcType) throws SQLException {
        // TODO Auto-generated method stub
        System.out.println("要保存的状态码:"+parameter.getCode());
        ps.setString(i, parameter.getCode().toString());
    }

    @Override
    public EmpStatus getResult(ResultSet rs, String columnName)
            throws SQLException {
        // TODO Auto-generated method stub
        //需要根据从数据库中拿到的枚举的状态码返回一个枚举对象
        int code = rs.getInt(columnName);
        System.out.println("从数据库中获取的状态码:"+code);
        EmpStatus status = EmpStatus.getEmpStatusByCode(code);
        return status;
    }

    @Override
    public EmpStatus getResult(ResultSet rs, int columnIndex)
            throws SQLException {
        // TODO Auto-generated method stub
        int code = rs.getInt(columnIndex);
        System.out.println("从数据库中获取的状态码:"+code);
        EmpStatus status = EmpStatus.getEmpStatusByCode(code);
        return status;
    }

    @Override
    public EmpStatus getResult(CallableStatement cs, int columnIndex)
            throws SQLException {
        // TODO Auto-generated method stub
        int code = cs.getInt(columnIndex);
        System.out.println("从数据库中获取的状态码:"+code);
        EmpStatus status = EmpStatus.getEmpStatusByCode(code);
        return status;
    }

}

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>
    <properties resource="dbconfig.properties"></properties>
    <typeHandlers>
        <!--1、配置我们自定义的TypeHandler  -->
        <typeHandler handler="com.atguigu.mybatis.typehandler.MyEnumEmpStatusTypeHandler" javaType="com.atguigu.mybatis.bean.EmpStatus"/>
        <!--2、也可以在处理某个字段的时候告诉MyBatis用什么类型处理器
                保存:#{empStatus,typeHandler=xxxx}
                查询:
                    <resultMap type="com.atguigu.mybatis.bean.Employee" id="MyEmp">
                         <id column="id" property="id"/>
                         <result column="empStatus" property="empStatus" typeHandler=""/>
                     </resultMap>
                注意:如果在参数位置修改TypeHandler,应该保证保存数据和查询数据用的TypeHandler是一样的。
          -->
    </typeHandlers>
    <plugins>
        <plugin interceptor="com.github.pagehelper.PageInterceptor"></plugin>
    </plugins>
    <environments default="dev_mysql">
        <environment id="dev_mysql">
            <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="123456" />
            </dataSource>
        </environment>

        <environment id="dev_oracle">
            <transactionManager type="JDBC" />
            <dataSource type="POOLED">
                <property name="driver" value="${orcl.driver}" />
                <property name="url" value="${orcl.url}" />
                <property name="username" value="${orcl.username}" />
                <property name="password" value="${orcl.password}" />
            </dataSource>
        </environment>
    </environments>

    <databaseIdProvider type="DB_VENDOR">
        <property name="MySQL" value="mysql"/>
        <property name="Oracle" value="oracle"/>
        <property name="SQL Server" value="sqlserver"/>
    </databaseIdProvider>
    <!-- 将我们写好的sql映射文件(EmployeeMapper.xml)一定要注册到全局配置文件(mybatis-config.xml)中 -->
    <mappers>
        <mapper resource="EmployeeMapper.xml" />
    </mappers>
</configuration>

测试类

st
    public void testEnumUse(){
        EmpStatus login = EmpStatus.LOGIN;
        System.out.println("枚举的索引:"+login.ordinal());
        System.out.println("枚举的名字:"+login.name());

        System.out.println("枚举的状态码:"+login.getCode());
        System.out.println("枚举的提示消息:"+login.getMsg());
    }

    /**
     * 默认mybatis在处理枚举对象的时候保存的是枚举的名字:EnumTypeHandler
     * 改变使用:EnumOrdinalTypeHandler:
     * @throws IOException
     */
    @Test
    public void testEnum() throws IOException{
        SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
        SqlSession openSession = sqlSessionFactory.openSession();
        try{
            EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class);
            Employee employee = new Employee("test_enum", "enum@atguigu.com","1");
            //mapper.addEmp(employee);
            //System.out.println("保存成功"+employee.getId());
            //openSession.commit();
            Employee empById = mapper.getEmpById(30026);
            System.out.println(empById.getEmpStatus());
        }finally{
            openSession.close();
        }
    }

批量操作

  • 当需要批量插入数量级很大的sql记录的时候,最好使用批量操作 ```java @Test public void testBatch() throws IOException{

      SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
    
      //可以执行批量操作的sqlSession
      SqlSession openSession = sqlSessionFactory.openSession(ExecutorType.BATCH);
      long start = System.currentTimeMillis();
      try{
          EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class);
          for (int i = 0; i < 10000; i++) {
              mapper.addEmp(new Employee(UUID.randomUUID().toString().substring(0, 5), "b", "1"));
          }
          openSession.commit();
          long end = System.currentTimeMillis();
          //批量:(预编译sql一次==>设置参数===>10000次===>执行(1次))
          //Parameters: 616c1(String), b(String), 1(String)==>4598
          //非批量:(预编译sql=设置参数=执行)==》10000    10200
          System.out.println("执行时长:"+(end-start));
      }finally{
          openSession.close();
      }
    

    }

```

批量非批量之间的区别

  • 批量
    • 预编译sql一次->设置参数->10000次->执行 只1次
  • 非批量
    • 预编译sql->设置参数->执行, 重复10000次,效率大打折扣。