1. PageHelper插件进行分页
  2. 批量操作
  3. 存储过程
  4. typeHandler处理枚举

PageHelper插件进行分页

  1. PageHelper是MyBatis中非常方便的第三方分页插件
  2. 官方文档:https://github.com/pagehelper/Mybatis-PageHelper/blob/master/wikis/zh/HowToUse.md
  3. 我们可以对照官方文档的说明,快速的使用插件

使用步骤

  1. 导入相关包pagehelper-x.x.x.jarjsqlparser-0.9.5.jar

    1. <dependency>
    2. <groupId>com.github.pagehelper</groupId>
    3. <artifactId>pagehelper</artifactId>
    4. <version>最新版本</version>
    5. </dependency>
  2. 在MyBatis全局配置文件中配置分页插件。

    <plugins>
     <!--com.github.pagehelper.PageInterceptor-->
     <plugin interceptor="com.github.pagehelper.PageInterceptor">
         <!--使用下面的方式配置参数,后面会有所有参数介绍-->
         <property name="param1" value="value1"/>
     </plugin>
    </plugins>
    
  3. 使用PageHelper提供的方法进行分页

  4. 可以使用更强大的PageInfo封装返回结果

批量操作

  1. 默认的openSession()方法没有参数,他会创建有如下特征的
    • 会开启一个事务(也就是不自动提交)
    • 连接对象会从由活动环境配置的数据源实例得到。
    • 事务隔离级别将会使用驱动或数据源的默认设置
    • 预处理语句不会被复用,也不会批量处理更新。
  2. openSession方法的ExecutorType类型的参数,枚举类型:
    • ExecutorType.SIMPLE:这个执行器类型不做特殊处理的事情(这是默认装配的)。它为每个语句的执行创建了一个新的预处理语句。
    • ExecutorType.REUSE:这个执行器类型会复用预处理语句。
    • ExecutorType.BATCH:这个执行器会批量执行所有更新语句。 ``` SqlSession openSession(boolean autoCommit); SqlSession openSession(Connection connection); SqlSession openSession(TransactionIsolationLevel level);

SqlSession openSession(Executor execType); SqlSession openSession(Executor execType, boolean autoCommit); SqlSession openSession(Executor execType, TransactionIsolationLevel level); SqlSession openSession(Executor execType, Connection connection);


4. 批量操作我们是使用MyBatis提供的BatchExecutor进行的,他的底层就是通过jdbc攒sql的方式进行的。我们可以让他攒够一定数量后发送给数据库一次。
```java
public void test01() {
    SqlSession openSession = build.openSession(ExecutorType.BATCH);
    UserDao mapper = openSession.getMapper(UserDao.class);
    long start = System.currentTimeMillis();
    for (int i = 0; i < 1000000; i++) {
    String name = UUID.randomUUID().toString().substring(0, 5);
    mapper.addUser(new User(null, name, 13));
    }
    openSession.commit();
    openSession.close();
    long end = System.currentTimeMillis();
    System.out.println("耗时时间:"+(end-start));
}
// 100w记录添加测试结果:耗时:75567
  1. 与Spring整合中,我们推荐,额外的配置一个可以专门用来执行批量操作的sqlSession

    <bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
     <constructor-arg name="sqlSessionFactory" ref="sqlSessionFactoryBean"></constructor-arg>
     <constructor-arg name="executor" value="BATCH"></constructor-arg>
    </bean>
    
  2. 需要用到批量操作的时候,我们可以注入配置的这个批量SqlSession。通过他获取到mapper映射器进行操作。 ```java @Service public class EmployeeService {

    @Autowired private EmployeeMapper employeeMapper;

    @Autowired private SqlSession sqlSession;

    public List getEmp() {

     EmployeeMapper mapper = sqlSession.getMapper(EmployeeMapper.class);
     return mapper.getEmp();
    

    }

}


10. <br />

<a name="138a6766"></a>
## 注意

1. 批量操作是在session.commit() 以后才发送sql语句给数据库进行执行的。
1. 如果我们想让其提前执行,以方便后续可能的查询操作获取数据,我们可以使用sqlSession.flushStatements() 方法,让其直接冲刷到数据库进行执行。

<a name="a74b3354"></a>
# 存储过程

1. 在实际开发中,我们通常也会写一写存储过程,MyBatis也支持对存储过程的调用。
1. 一个最简单的存储过程
```shell
delimiter $$
create procedure test()
begin
    select 'hello';
end $$
delimiter;
  1. 存储过程的调用
    • select标签中statementType=”CALLABLE”
    • 标签体中调用方法:
      {call procedure_name(#{param1_info}, #{param2_info})}

存储过程-游标处理

  1. MyBatis对存储过程的游标提供了一个JdbcType=CURSOR的支持,可以智能的把游标读取到的数据,映射到我们声明的结果集中
  2. 调用实例:
// xml配置文件
<select id="getPage" paramterType="PageEmp" statementType="CALLABLE" databaseId="oracle">
    {call PAGE_EMP(
    #{start, mode=IN, jdbcType=INTEGER},
    #{end, mode=IN, jdbcType=INTEGER},
    #{count, mode=OUT, jdbcType=INTEGER},
    #{emps, mode=OUT, jdbcType=CURSOR, javaType=ResultSet, resultMap=TestEmp}
    )}
</select>

<resultMap type="Emp" id="TestEmp">
    <id column="EMPNO" property="id"/>
</resultMap>
# properties文件
orcl.driver=oracle.jdbc.OracleDriver
orcl.url=jdbc:oracle:thin:@localhost:1521:orcl
orcl.username=scott
orcl.password=123456
// bean对象
public class PageEmp {
    private int start;
    private int end;
    private int count;
    private List<Emp> emps;
}
<!--mybatis-config.xml-->
<environment id="oracle_dev">
    <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>
<databaseIdProvider type="DB_VENDOR">
    <property name="MySQL" value="mysql"/>
    <property name="Oracle" value="oracle"/>
</databaseIdProvider>
create or replace procedure
hello_test(p_start in int, 
            p_end in int, 
            p_count out int, 
            ref_cur out sys_refcursor) AS
BEGIN
    select count(*) into p_count from emp;
    open ref_cur for
        select * from (select e.* rownum as r1 from emp e where rownum < p_end)
        where r1 > p_start;
END hello_test;

自定义TypeHandler处理枚举

  1. 我们可以通过自定义TypeHandler的形式来在设置参数或者取出结果集的时候自定义参数封装策略。
  2. 步骤:
    • 实现TypeHandler接口或者继承BaseTypeHandler
    • 使用@MappedTypes 定义处理的java类型;使用@MappedJdbcTypes 定义jdbcType类型
    • 在自定义结果集标签或者参数处理的时候声明使用自定义TypeHandler进行处理,或者在全局TypeHandler要处理的javaType

测试实例

一个代表部门状态的枚举类

public enum DeptStatus {
    WORKING(100, "部门正在开会中");
    MEETING(200, "部门正在工作中");
    VOCATION(300, "部门正在休假中");
}
  1. 测试全局配置EnumOrdinalTypeHandler

    <typeHandlers>
     <typeHandler handler="org.apache.ibatis.type.EnumOrdinalTypeHanlder" javaType="com.zh.bean.DeptStateus"/>
    </typeHandlers>
    
  2. 测试全局配置EnumTypeHandler

    <typeHandlers>
     <typeHandler handler="org.apache.ibatis.type.EnumTypeHanlder" javaType="com.zh.bean.DeptStateus"/>
    </typeHandlers>
    
  3. 测试参数位置设置自定义TypeHandler

    <insert id="addDept">
     insert into department(dept_name, status)
     values(#{deptName}, #{status, typeHandler = com.zh.type.MyEnumTypeHandler})
    </insert>
    

自定义TypeHandler

package com.zh.mybatis.typeHandler;

import com.zh.mybatis.bean.EmpStatus;
import org.apache.ibatis.type.JdbcType;
import org.apache.ibatis.type.TypeHandler;

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

/**
 * @author cai-xiansheng
 * @Description
 * 1. 实现TypeHandler,或者继承BaseTypeHandler
 * @create 2020-10-04 13:00
 */
public class MyEnumEmpStatusHandler implements TypeHandler<EmpStatus> {


    /**
     * 定义参数如何保存进数据库中
     * @param ps
     * @param i
     * @param parameter
     * @param jdbcType
     * @throws SQLException
     */
    @Override
    public void setParameter(PreparedStatement ps, int i, EmpStatus parameter, JdbcType jdbcType) throws SQLException {
        ps.setString(i, parameter.getCode().toString());
        System.out.println("调用MyEnumEmpStatusHandler");
    }

    @Override
    public EmpStatus getResult(ResultSet rs, String columnName) throws SQLException {
        // 需要根据从数据库中拿到的枚举的状态码返回一个枚举状态
        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 {
        // 需要根据从数据库中拿到的枚举的状态码返回一个枚举状态
        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 {
        // 需要根据从数据库中拿到的枚举的状态码返回一个枚举状态
        int code = cs.getInt(columnIndex);
        System.out.println("从数据库中获取的状态码:" + code);
        EmpStatus status = EmpStatus.getEmpStatusByCode(code);
        return status;
    }
}

注册Handler

<!--mybatis-config.xml-->
    <!--类型处理器-->
    <typeHandlers>
        <!--1. 配置我们自定义的类型处理器TypeHandler-->
        <typeHandler handler="com.zh.mybatis.typeHandler.MyEnumEmpStatusHandler" javaType="com.zh.mybatis.bean.EmpStatus"/>
        <!--
            2. 也可以在处理某个字段的时候告诉MyBatis用什么类型处理器
                保存:#{empStatus, typeHandler=xxx}
                查询:
                    <resultMap id="MyEmp" type="com.zh.mybatis.bean.Employee">
                        <id column="id" property="id"/>
                        <result column="empStatus" property="empStatus" typeHandler="xxx"/>
                    </resultMap>
                注意:如果在参数位置修改TypeHandler,应该保证数据的查询和存储用的同一个typeHandler
        -->
    </typeHandlers>

枚举类

package com.zh.mybatis.bean;

/**
 * @author cai-xiansheng
 * @Description 希望数据库保存的是100,200这些状态码,而不是1,0索引
 * @create 2020-10-04 12:33
 */
public enum EmpStatus {
    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 String getMsg() {
        return msg;
    }

    public void setCode(Integer code) {
        this.code = code;
    }

    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;
        }
    }
}