MyBatis | Use plug-ins in MyBatis, use PageHelper plug-ins, custom type processors

table of Contents

 

1. Plug-in principle

Two, plug-in writing (single plug-in)

1. Write the implementation class of Interceptor

2. Use @Intercepts annotation to complete the signature of the plug-in

3. Register the written plugin in the global configuration file

Third, the execution order of multiple plugins

Fourth, use the PageHelper plug-in

1. Guide package

2. Register the PageHelper plugin in the global configuration file

3. Coding

Five, use BatchExecutor for batch operations

Six, custom TypeHandler to handle enumerated types

1. Custom TypeHandler class

2. Register the TypeHandler in the global configuration file


1. Plug-in principle

When the four objects are created, they have the following characteristics:

  • Each created object is not returned directly, butinterceptorChain.pluginAll(parameterHandler);
  • This method gets all Interceptor(the interfaces that the plug-in needs to implement), and the call interceptor.plugin(target);returns the targetwrapped object.
    public Object pluginAll(Object target) {
        Interceptor interceptor;
        for(Iterator var2 = this.interceptors.iterator(); var2.hasNext(); target = interceptor.plugin(target)) {
            interceptor = (Interceptor)var2.next();
        }

        return target;
    }

Therefore, we can use the plug-in to create a proxy object for the target object, similar to AOP (Aspect Oriented Programming). Our plug-in can create proxy objects for the four major objects, and the proxy object can intercept the execution of each of the four major objects.


Two, plug-in writing (single plug-in)

1. The written Interceptorimplementation class

package com.cerr.dao;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.plugin.*;
import java.util.Properties;
public class MyFirstPlugin implements Interceptor {

    //拦截目标对象的目标方法的执行
    public Object intercept(Invocation invocation) throws Throwable {
        System.out.println("intercept..."+invocation);
        //执行目标方法
        Object proceed = invocation.proceed();
        //返回执行后的返回值
        return proceed;
    }

    //包装目标对象,为目标对象创建一个代理对象
    public Object plugin(Object target) {
        System.out.println("plugin..."+target);
        //借助这个方法来使用当前Interceptor包装我们目标对象
        Object wrap = Plugin.wrap(target,this);
        //返回当前target创建的动态代理对象
        return wrap;
    }

    //将插件注册时的property属性设置进去
    public void setProperties(Properties properties) {
        System.out.println("插件配置的信息"+properties);

    }
}

2. Use @Interceptsannotations to complete the signature of the plug-in

Mark the written plug-in class @Intercepts, the marked plug-in class is as follows:

package com.cerr.dao;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.plugin.*;
import java.util.Properties;

//完成插件签名:告诉MyBatis当前插件来用拦截哪个对象的哪个方法
@Intercepts({
        @Signature(type = StatementHandler.class,method = "parameterize",
            args = java.sql.Statement.class)
})
public class MyFirstPlugin implements Interceptor {

    //拦截目标对象的目标方法的执行
    public Object intercept(Invocation invocation) throws Throwable {
        System.out.println("intercept..."+invocation);
        //执行目标方法
        Object proceed = invocation.proceed();
        //返回执行后的返回值
        return proceed;
    }

    //包装目标对象,为目标对象创建一个代理对象
    public Object plugin(Object target) {
        System.out.println("plugin..."+target);
        //借助这个方法来使用当前Interceptor包装我们目标对象
        Object wrap = Plugin.wrap(target,this);
        //返回当前target创建的动态代理对象
        return wrap;
    }

    //将插件注册时的property属性设置进去
    public void setProperties(Properties properties) {
        System.out.println("插件配置的信息"+properties);

    }
}

3. Register the written plugin in the global configuration file

Use <plugins>tags to register in the global configuration file :

    <plugins>
        <plugin interceptor="com.cerr.dao.MyFirstPlugin"></plugin>
    </plugins>

At this point we have defined an interceptor that intercepts StatementHandlerthe parameterizemethod of the object .


Third, the execution order of multiple plugins

Let's write another plug-in class MySecondPlugin, which is the same method of the same object intercepted by the plug-in class above. code show as below:

package com.cerr.dao;

import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.plugin.*;

import java.util.Properties;
@Intercepts({
        @Signature(type = StatementHandler.class,method = "parameterize",
                args = java.sql.Statement.class)
})
public class MySecondPlugin implements Interceptor {
    public Object intercept(Invocation invocation) throws Throwable {
        System.out.println("MySecondPlugin..intercept:"+invocation);
        return invocation.proceed();
    }

    public Object plugin(Object target) {
        System.out.println("MySecondPlugin...plugin:"+target);
        return Plugin.wrap(target,this);
    }

    public void setProperties(Properties properties) {
    }
}

Configure in the global configuration file:

    <plugins>
        <plugin interceptor="com.cerr.dao.MyFirstPlugin"></plugin>
        <plugin interceptor="com.cerr.dao.MySecondPlugin"></plugin>
    </plugins>

After configuration, we randomly find a test method to run, for example:

    @Test
    public void testSimple() throws IOException {
        SqlSessionFactory factory = getSqlSessionFactory();
        SqlSession session = factory.openSession();
        try{
            EmployeeMapper mapper = session.getMapper(EmployeeMapper.class);
            List< Employee > list = mapper.selectByExample(null);
        }finally {
            session.close();
        }
    }

The result is shown in Figure 1:

 

 
13424350-6781df183cd88ff9.png

                                                                          Figure 1: Console results

 

 

As can be seen from the above figure, when generating the proxy object, the proxy object of the first plug-in class is created first, and then the proxy object of the second plug-in class is created; but when the target method is intercepted, the second is executed first Plug-in class, then execute the first plug-in class.

 

Therefore, we can draw the following conclusion: when creating a dynamic proxy, proxy objects are created layer by layer in the order of plug-in configuration. When executing the target method, execute it in reverse order.

We can compare the process to the following model. First create StatementHandler, then create the MyFirstPluginproxy object, and then create the MySecondPluginproxy object. The relationship between the three is created late, including early creation, and naturally when the target method is executed Execute from outside to inside.

 
13424350-5941b5c2e05a3d0b.png
 

 


Fourth, use the PageHelper plug-in

1. Guide package

Two packages are required, which can be downloaded on GitHub: click here to download

 
13424350-0c0575179809008b.png
 

 

2. Register the PageHelperplugin in the global configuration file

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

3. Coding

Can use Pageobjects:

    @Test
    public void test() throws IOException {
        SqlSessionFactory factory = getSqlSessionFactory();
        SqlSession session = factory.openSession();
        try{
            EmployeeMapper mapper = session.getMapper(EmployeeMapper.class);
            //第一个参数是页码,第二个是每页的记录数
            Page <Object> page = PageHelper.startPage(1,5);
            List <Employee> list = mapper.getEmps();
            System.out.println("当前页码:"+page.getPageNum());
            System.out.println("总记录数:"+page.getTotal());
            System.out.println("每页的记录数:"+page.getPageSize());
            System.out.println("总页码:"+page.getPages());
        }finally {
            session.close();
        }
    }

You can use Pageobjects to obtain data about paging, such as the current page number, total number of records, and so on. PageHelper.startPage(1,5)Indicates that the first page is displayed, and there are 5 records per page.

    @Test
    public void test() throws IOException {
        SqlSessionFactory factory = getSqlSessionFactory();
        SqlSession session = factory.openSession();
        try{
            EmployeeMapper mapper = session.getMapper(EmployeeMapper.class);
            //第一个参数是页码,第二个是每页的记录数
            PageHelper.startPage(1,5);
            List <Employee> list = mapper.getEmps();
            PageInfo<Employee> info = new PageInfo <>(list);
            System.out.println("当前页码:"+info.getPageNum());
            System.out.println("总记录数:"+info.getTotal());
            System.out.println("每页的记录数:"+info.getPageSize());
            System.out.println("总页码:"+info.getPages());
            System.out.println("是否第一页:"+info.isIsFirstPage());
            System.out.println("是否最后一页:"+info.isIsLastPage());
        }finally {
            session.close();
        }
    }

You can also use PageInfoobjects to get paged data, similar to the code above.


Five, use BatchExecutor for batch operations

Just pass in a parameter when SqlSessionFactorygetting through SqlSession, namely:

SqlSessionFactory factory = getSqlSessionFactory();
//可以执行批量操作的SqlSession
SqlSession session = factory.openSession(ExecutorType.BATCH);

After setting this parameter, it SqlSessionis now time to batch operations:

    @Test
    public void testBatch() throws IOException {
        SqlSessionFactory factory = getSqlSessionFactory();
        //可以执行批量操作的SqlSession
        SqlSession session = factory.openSession(ExecutorType.BATCH);
        try{
            EmployeeMapper mapper = session.getMapper(EmployeeMapper.class);
            for (int i = 0;i < 10000;++i){
                mapper.addEmps(new Employee(null,"a","a","a"));
            }
            session.commit();
        }finally {
            session.close();
        }
    }

Six, custom TypeHandler to handle enumerated types

We Employeeadd a EmpStatusfield to the class to save the state of the class. This field is an enumeration class:

package com.cerr.mybatis;

public enum EmpStatus {
    LOGIN,LOGOUT,REMOVE;
}

MyBatis has two when dealing with enumerated types TypeHandler, one is EnumTypeHandlerand the other is EnumOrdinalTypeHandler.

  • EnumTypeHandler: When saving an enumeration object, the name of the enumeration object is saved by default
  • EnumOrdinalTypeHandler: When saving an enumeration object, the index of the enumeration object is saved by default.

We now want to handle enumeration types by ourselves, we need to customize TypeHandler to implement, and custom classes need to implement TypeHandlerinterfaces or inheritance BaseTypeHandler.

We first EmpStatusto transform it, because we want to keep the status code stored in the database in time, then the time taken to obtain the information of the object, it is necessary to add two fields msgand codeconstruction methods, and then add a corresponding, getterand setterMethods :

package com.cerr.mybatis;
public enum EmpStatus {
    LOGIN(100,"用户登录"),LOGOUT(200,"用户登出"),REMOVE(300,"用户不存在");
    private Integer code;
    private String msg;
    public static EmpStatus getEmpStatusByCode(Integer code){
        switch (code){
            case 100:
                return LOGIN;
            case 200:
                return LOGOUT;
            case 300:
                return REMOVE;
             default:
                 return LOGOUT;
        }
    }
    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;
    }
}

1. Custom TypeHandler class

package com.cerr.mybatis.dao;
import com.cerr.mybatis.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;
/**
 * 实现TypeHandler接口或者继承BaseTypeHandler
 */
public class MyEnumEmpStatusTypeHandler implements TypeHandler<EmpStatus> {
    //定义数据如何保存到数据库中
    @Override
    public void setParameter(PreparedStatement preparedStatement, int i, EmpStatus empStatus, JdbcType jdbcType) throws SQLException {
        preparedStatement.setString(i,empStatus.getCode().toString());
    }

    @Override
    public EmpStatus getResult(ResultSet resultSet, String s) throws SQLException {
        //根据从数据库中拿到的状态码返回枚举对象
        int code = resultSet.getInt(s);
        return EmpStatus.getEmpStatusByCode(code);
    }

    @Override
    public EmpStatus getResult(ResultSet resultSet, int i) throws SQLException {
        //根据从数据库中拿到的状态码返回枚举对象
        int code = resultSet.getInt(i);
        return EmpStatus.getEmpStatusByCode(code);
    }

    @Override
    public EmpStatus getResult(CallableStatement callableStatement, int i) throws SQLException {
        //根据从数据库中拿到的状态码返回枚举对象
        int code = callableStatement.getInt(i);
        return EmpStatus.getEmpStatusByCode(code);
    }
}

The implemented TypeHandlermethod setParameter()defines how to save the data in the database, and directly uses PreparedStatementthe method of setting parameters to add the data we want to save as parameters. For the remaining three methods, we define how to process the data after getting the data from the database. After the status code saved in the database is taken out, call the EmpStatus.getEmpStatusByCode(code)method we wrote to return the information of the enumerated object.

2. Register the TypeHandler in the global configuration file

The syntax format is as follows:

<typeHandlers>
  <!-- 配置自定义的类型处理器 -->
  <typeHandler handler="自定义的TypeHandler全类名" javaType="指定要处理的类"/>
</typeHandlers>

Here our configuration is as follows:

    <typeHandlers>
        <!-- 配置自定义的类型处理器 -->
        <typeHandler handler="com.cerr.mybatis.dao.MyEnumEmpStatusTypeHandler" javaType="com.cerr.mybatis.EmpStatus"/>
    </typeHandlers>

Of course, in addition to using it in the global configuration file javaTypeto specify which class is processed by our custom type processor, we can also configure it in the sql mapping file:

  • For <insert>tags, we can add typeHandlerattributes directly when passing parameters , for example #{empStatus,typeHandler=com.cerr.mybatis.dao.MyEnumEmpStatusTypeHandler}.
  • For the <select>label, we can <resultMap>add an typeHandlerattribute to the enumeration attribute when defining it and specify it as our custom type processor.
  • However, if this method is used to specify the type of word processor, such as guaranteed <insert>and <select>labels using the same type of processor.

Guess you like

Origin blog.csdn.net/qq_14810195/article/details/103546635