1、MyBatis逆向工程(Generator)

MyBatis Generator,简称MBG,是一个专门为MyBatis框架使用者定制的代码生成器,可以快速的根据表生成对应的映射文件,接口,以及bean类。支持基本的增删改查,以及QBC风格的条件查询。但是表连接,存储过程等这些复杂sql的定义需要我们手工编写

1、官方文档地址

http://mybatis.org/generator/

2、官方工程地址

https://github.com/mybatis/generator

3、整合项目

1、下载包

https://github.com/mybatis/generator/releases

2、导入进项目

image.png

3、创建配置文件mbg.xml

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <!DOCTYPE generatorConfiguration
  3. PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
  4. "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
  5. <generatorConfiguration>
  6. <context id="DB2Tables" targetRuntime="MyBatis3">
  7. <!-- 注释构建 -->
  8. <commentGenerator>
  9. <!-- 去掉所有的注释 -->
  10. <property name="suppressAllComments" value="true"/>
  11. <property name="suppressDate" value="true"/>
  12. </commentGenerator>
  13. <!--连接指定目标数据库-->
  14. <jdbcConnection driverClass="com.mysql.cj.jdbc.Driver"
  15. connectionURL="jdbc:mysql://localhost:3306/learn"
  16. userId="root"
  17. password="qwerasdf123">
  18. <!-- MySQL 不支持 schema 或者 catalog 所以需要添加这个,官方文档有 -->
  19. <property name="nullCatalogMeansCurrent" value="true"/>
  20. </jdbcConnection>
  21. <!--java类型解析器用默认-->
  22. <javaTypeResolver >
  23. <property name="forceBigDecimals" value="false" />
  24. </javaTypeResolver>
  25. <!--生成bean路径和工程配置-->
  26. <javaModelGenerator targetPackage="com.daijunyi.model" targetProject="./src">
  27. <property name="enableSubPackages" value="true" />
  28. <property name="trimStrings" value="true" />
  29. </javaModelGenerator>
  30. <!--映射文件配置-->
  31. <sqlMapGenerator targetPackage="com.daijunyi.mapper" targetProject="./src">
  32. <property name="enableSubPackages" value="true" />
  33. </sqlMapGenerator>
  34. <!--mapper接口文件-->
  35. <javaClientGenerator type="XMLMAPPER" targetPackage="com.daijunyi.mapper" targetProject="./src">
  36. <property name="enableSubPackages" value="true" />
  37. </javaClientGenerator>
  38. <!--指定生成哪些表 domainObjectName:java类名 tableName:数据库表名-->
  39. <table tableName="%"></table>
  40. </context>
  41. </generatorConfiguration>

4、创建代码生成各个文件

  • 直接测试运行,就可以生成对应的表

      @Test
      public void BGMTest() throws Exception{
          List<String> warnings = new ArrayList<String>();
          File configFile = new File("mbg.xml");
          ConfigurationParser cp = new ConfigurationParser(warnings);
          Configuration config = cp.parseConfiguration(configFile);
          DefaultShellCallback callback = new DefaultShellCallback(true);
          MyBatisGenerator myBatisGenerator = new MyBatisGenerator(config, callback, warnings);
          myBatisGenerator.generate(null);
      }
    

    5、运行完成之后就生成了对应的列

    image.pngimage.png
    Example是用来复杂查询的

    6、写测试代码

     public SqlSessionFactory getSqlSessionFactory() {
          String resource = "mybatis.xml";
          SqlSessionFactory sqlSessionFactory = null;
          try {
              InputStream inputStream = Resources.getResourceAsStream(resource);
              sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
          } catch (IOException e) {
              e.printStackTrace();
          }
          return sqlSessionFactory;
      }
    
      @Test
      public void TestUser(){
          SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
          SqlSession sqlSession = sqlSessionFactory.openSession(true);
          UserMapper mapper = sqlSession.getMapper(UserMapper.class);
          UserExample userExample = new UserExample();
          UserExample.Criteria criteria = userExample.createCriteria();
          criteria.andUserNameIn(Arrays.asList("红领巾"));
          List<User> users = mapper.selectByExample(userExample);
          System.out.println(users);
      }
    

    2、MyBatis工作原理

    1、架构图

    image.png

    2、SqlSessionFactory

    1、获取SqlSessionFactory

    String resource = "mybatis.xml";
    SqlSessionFactory sqlSessionFactory = null;
    try {
      InputStream inputStream = Resources.getResourceAsStream(resource);
      sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
    } catch (IOException e) {
      e.printStackTrace();
    }
    return sqlSessionFactory;
    

    2、解析标签

    private void parseConfiguration(XNode root) {
      try {
        // issue #117 read properties first
        propertiesElement(root.evalNode("properties"));
        Properties settings = settingsAsProperties(root.evalNode("settings"));
        loadCustomVfs(settings);
        loadCustomLogImpl(settings);
        typeAliasesElement(root.evalNode("typeAliases"));
        pluginElement(root.evalNode("plugins"));
        objectFactoryElement(root.evalNode("objectFactory"));
        objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
        reflectorFactoryElement(root.evalNode("reflectorFactory"));
        settingsElement(settings);
        // read it after objectFactory and objectWrapperFactory issue #631
        environmentsElement(root.evalNode("environments"));
        databaseIdProviderElement(root.evalNode("databaseIdProvider"));
        typeHandlerElement(root.evalNode("typeHandlers"));
        mapperElement(root.evalNode("mappers"));
      } catch (Exception e) {
        throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
      }
    }
    
  • 一个MappedStatement代表一个增删改查的片段

  • Configuration保存了所有的信息,包括sql信息等等
  • 每一个Mapper接口文件对应一个MapperProxyFactory类
  • 最终帮我们创建一个DefaultSqlSessionFactory

    public SqlSessionFactory build(Configuration config) {
      return new DefaultSqlSessionFactory(config);
    }
    

    3、时序图

    image.png

    3、SqlSession

    1、创建SqlSession

    private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
      Transaction tx = null;
      try {
        final Environment environment = configuration.getEnvironment();
        final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
        tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
        final Executor executor = configuration.newExecutor(tx, execType);
        return new DefaultSqlSession(configuration, executor, autoCommit);
      } catch (Exception e) {
        closeTransaction(tx); // may have fetched a connection so lets call close()
        throw ExceptionFactory.wrapException("Error opening session.  Cause: " + e, e);
      } finally {
        ErrorContext.instance().reset();
      }
    }
    

    2、创建Executor

    public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
      executorType = executorType == null ? defaultExecutorType : executorType;
      executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
      Executor executor;
      if (ExecutorType.BATCH == executorType) {
        executor = new BatchExecutor(this, transaction);
      } else if (ExecutorType.REUSE == executorType) {
        executor = new ReuseExecutor(this, transaction);
      } else {
        executor = new SimpleExecutor(this, transaction);
      }
      if (cacheEnabled) {
        executor = new CachingExecutor(executor);
      }
      executor = (Executor) interceptorChain.pluginAll(executor);
      return executor;
    }
    
  • CachingExecutor(二级缓存)

  • interceptorChain.pluginAll(Executor)

    • 使用每一个拦截器,重新包装再返回
      public Object pluginAll(Object target) {
      for (Interceptor interceptor : interceptors) {
       target = interceptor.plugin(target);
      }
      return target;
      }
      

      3、DefaultSqlSession

  • 再把executor传递给新创建DefaultSqlSession然后返回

    new DefaultSqlSession(configuration, executor, autoCommit);
    

    4、时序图

    image.png

    4、SqlSession.GetMapper

  • 从Configuration中的MapperRegistry方法来创建

    @SuppressWarnings("unchecked")
    public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
      final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
      if (mapperProxyFactory == null) {
        throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
      }
      try {
        return mapperProxyFactory.newInstance(sqlSession);
      } catch (Exception e) {
        throw new BindingException("Error getting mapper instance. Cause: " + e, e);
      }
    }
    
  • mapperProxyFactory.newInstance(sqlSeesion)

    • 其实就是通过Proxy动态代理生成MapperProxy代理对象, ```java @SuppressWarnings(“unchecked”) protected T newInstance(MapperProxy mapperProxy) { return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy); }

    public T newInstance(SqlSession sqlSession) { final MapperProxy mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache); return newInstance(mapperProxy); }

    <a name="owdNZ"></a>
    ### 1、时序图
    ![image.png](https://cdn.nlark.com/yuque/0/2021/png/12971636/1628756027810-d07cdaf3-1602-4878-9319-26142e9dd58a.png#height=707&id=kuGzg&margin=%5Bobject%20Object%5D&name=image.png&originHeight=707&originWidth=1235&originalType=binary&ratio=1&size=382090&status=done&style=none&width=1235)
    <a name="nOK9k"></a>
    ## 5、执行具体接口获取结果
    <a name="P3uGN"></a>
    ### 1、直接进入MapperProxy代理对象中调用
    ```java
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
      try {
        if (Object.class.equals(method.getDeclaringClass())) {
          return method.invoke(this, args);
        } else {
          return cachedInvoker(method).invoke(proxy, method, args, sqlSession);
        }
      } catch (Throwable t) {
        throw ExceptionUtil.unwrapThrowable(t);
      }
    }
    
  • 判断如果是object类的方法就直接放行

    Object.class.equals(method.getDeclaringClass())
    

    2、Execute

    public Object execute(SqlSession sqlSession, Object[] args) {
      Object result;
      switch (command.getType()) {
        case INSERT: {
          Object param = method.convertArgsToSqlCommandParam(args);
          result = rowCountResult(sqlSession.insert(command.getName(), param));
          break;
        }
        case UPDATE: {
          Object param = method.convertArgsToSqlCommandParam(args);
          result = rowCountResult(sqlSession.update(command.getName(), param));
          break;
        }
        case DELETE: {
          Object param = method.convertArgsToSqlCommandParam(args);
          result = rowCountResult(sqlSession.delete(command.getName(), param));
          break;
        }
        case SELECT:
          if (method.returnsVoid() && method.hasResultHandler()) {
            executeWithResultHandler(sqlSession, args);
            result = null;
          } else if (method.returnsMany()) {
            result = executeForMany(sqlSession, args);
          } else if (method.returnsMap()) {
            result = executeForMap(sqlSession, args);
          } else if (method.returnsCursor()) {
            result = executeForCursor(sqlSession, args);
          } else {
            Object param = method.convertArgsToSqlCommandParam(args);
            result = sqlSession.selectOne(command.getName(), param);
            if (method.returnsOptional()
                && (result == null || !method.getReturnType().equals(result.getClass()))) {
              result = Optional.ofNullable(result);
            }
          }
          break;
        case FLUSH:
          result = sqlSession.flushStatements();
          break;
        default:
          throw new BindingException("Unknown execution method for: " + command.getName());
      }
      if (result == null && method.getReturnType().isPrimitive() && !method.returnsVoid()) {
        throw new BindingException("Mapper method '" + command.getName()
            + " attempted to return null from a method with a primitive return type (" + method.getReturnType() + ").");
      }
      return result;
    }
    
  • 加入我们现在查询单个对象返回的

  • 调用result = sqlSession.selectOne(command.getName(), param);
  • 最终调用DefaultSqlSession中的selectList

    private <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds, ResultHandler handler) {
      try {
        MappedStatement ms = configuration.getMappedStatement(statement);
        return executor.query(ms, wrapCollection(parameter), rowBounds, handler);
      } catch (Exception e) {
        throw ExceptionFactory.wrapException("Error querying database.  Cause: " + e, e);
      } finally {
        ErrorContext.instance().reset();
      }
    }
    
  • executor调用query

    • 获取到BoundSql是具体的sql信息
    • 创建出缓存key的值特别复杂如下
      • 374417829:4136890107:com.daijunyi.mapper.UserMapper.getUserList:0:2147483647:select * from user:development
        @Override
        public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
        BoundSql boundSql = ms.getBoundSql(parameterObject);
        CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql);
        return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
        }
        
  • BoundSql ```java public class BoundSql {

    private final String sql; private final List parameterMappings; private final Object parameterObject; private final Map additionalParameters; private final MetaObject metaParameters;


- 最终query调用查询 
   - 这里List<E> list = (List<E>) tcm.getObject(cache, key); 是调用二级缓存查询是否存在,如果存在就直接返回
   - delegate就是我们全局配置的另外一个SimpleExecutor
```java
  @Override
  public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)
      throws SQLException {
    Cache cache = ms.getCache();
    if (cache != null) {
      flushCacheIfRequired(ms);
      if (ms.isUseCache() && resultHandler == null) {
        ensureNoOutParams(ms, boundSql);
        @SuppressWarnings("unchecked")
        List<E> list = (List<E>) tcm.getObject(cache, key);
        if (list == null) {
          list = delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
          tcm.putObject(cache, key, list); // issue #578 and #116
        }
        return list;
      }
    }
    return delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
  }
  • SimpleExecutor.query

    • list = resultHandler == null ? (List) localCache.getObject(key) : null; 查看本地缓存(一级缓存)是否存在
    • 不存在就调用queryFromDatabase调用数据库
      public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
      ErrorContext.instance().resource(ms.getResource()).activity("executing a query").object(ms.getId());
      if (closed) {
       throw new ExecutorException("Executor was closed.");
      }
      if (queryStack == 0 && ms.isFlushCacheRequired()) {
       clearLocalCache();
      }
      List<E> list;
      try {
       queryStack++;
       list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;
       if (list != null) {
         handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
       } else {
         list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
       }
      } finally {
       queryStack--;
      }
      if (queryStack == 0) {
       for (DeferredLoad deferredLoad : deferredLoads) {
         deferredLoad.load();
       }
       // issue #601
       deferredLoads.clear();
       if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
         // issue #482
         clearLocalCache();
       }
      }
      return list;
      }
      
  • queryFromDatabase查询数据库

    • ms 当前sql片段的详细信息包括接口文件 映射文件等信息
    • parameter参数信息
    • boundSql Sql语句信息
    • rowBounds 逻辑分页的
      private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
      List<E> list;
      localCache.putObject(key, EXECUTION_PLACEHOLDER);
      try {
       list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql);
      } finally {
       localCache.removeObject(key);
      }
      localCache.putObject(key, list);
      if (ms.getStatementType() == StatementType.CALLABLE) {
       localOutputParameterCache.putObject(key, parameter);
      }
      return list;
      }
      
  • doQuery

    • StatementHandler 可以用来创建Statement
      @Override
      public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
      Statement stmt = null;
      try {
       Configuration configuration = ms.getConfiguration();
       StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
       stmt = prepareStatement(handler, ms.getStatementLog());
       return handler.query(stmt, resultHandler);
      } finally {
       closeStatement(stmt);
      }
      }
      
  • configuration.newStatementHandler

    • statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
      • 拦截器进一步包装
        public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
        StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);
        statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
        return statementHandler;
        }
        
  • ResultSetHandler和StatementHandler创建

  • StatementHandler参数预编译
  • ResultSetHandler返回结果处理器

    • 都通过了interceptorChain的包装 ```java public ResultSetHandler newResultSetHandler(Executor executor, MappedStatement mappedStatement, RowBounds rowBounds, ParameterHandler parameterHandler, ResultHandler resultHandler, BoundSql boundSql) { ResultSetHandler resultSetHandler = new DefaultResultSetHandler(executor, mappedStatement, parameterHandler, resultHandler, boundSql, rowBounds); resultSetHandler = (ResultSetHandler) interceptorChain.pluginAll(resultSetHandler); return resultSetHandler; }

    public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) { StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql); statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler); return statementHandler; } ```

    3、流程图

image.png

6、总结:

(1)根据配置文件(全局,sql映射)初始化出Configuration对象
(2)创建一个DefaultSqlSession对象,他里面包含Configuration以及Executor(根据全局配置文件中的defaultExecutorType创建出对应的Executor)
(3)DefaultSqlSession.getMapper():拿到Mapper接口对应的MapperProxy动态代理对象
(4)MapperProxy里面有(DefaultSqlSession)
(5)执行增删改查方法:

  • (1)调用DefaultSqlSession的增删改查(Executor)
  • (2)创建一个Statement对象。(同时也会创建出ParameterHandler和ResultSetHandler)
  • (3)调用StatementHandler预编译参数以及设置参数值;使用ParameterHandler给sql设置参数
  • (4)调用StatementHandler的增删改查方法
  • (5)ResultSetHandler封装结果

注意:

  • 四大对象Executor、ParameterHandler、ResultSetHandler、StatementHandler都会有一个拦截器包装的过程
    • statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
      public Object pluginAll(Object target) {
      for (Interceptor interceptor : interceptors) {
       target = interceptor.plugin(target);
      }
      return target;
      }
      


3、插件开发

1、简介

  • MyBatis在四大对象的创建过程中,都会有插件进行介入。插件可以利用动态代理机制-层层的包装目标对象,而实现在目标对象执行目标方法之前进行拦截的效果
  • MyBatis允许在已映射语句执行过程中的某一点进行拦截调用。
  • 默认情况下,MyBatis允许使用插件来拦截的方法调用包括
    • image.png

      2、插件开发步骤

      (1)编写实现Interceptor接口
      (2)使用@Intercepts注解配置实现Interceptor的实现类
      (3)在全局配置文件配置当前插件
package com.daijunyi.interceptor;

import org.apache.ibatis.executor.resultset.ResultSetHandler;
import org.apache.ibatis.plugin.*;

import java.sql.Statement;
import java.util.Properties;

@Intercepts(value = {@Signature(type = ResultSetHandler.class,method = "handleResultSets",args = {Statement.class})})
public class MyMybatisPlugin implements Interceptor {
    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        System.out.println("MyMybatisPlugin...intercept");
        Object proceed = invocation.proceed();
        return proceed;
    }

    @Override
    public Object plugin(Object target) {
        System.out.println("包装对象对:"+target.getClass().getName());
        Object wrap = Plugin.wrap(target, this);
        System.out.println("包装对象后:"+wrap.getClass().getName());
        return wrap;
    }

    @Override
    public void setProperties(Properties properties) {
        System.out.println("MyMybatisPlugin:"+properties.toString());
    }
}
<plugins>
  <plugin interceptor="com.daijunyi.interceptor.MyMybatisPlugin">
    <property name="tx" value="11"/>
  </plugin>
</plugins>

运行结果:

[DEBUG][2021-08-12 22:07:12 771][org.apache.ibatis.io.ResolverUtil]-[Checking to see if class com.daijunyi.interceptor.MyMybatisPlugin matches criteria [is assignable to Object]]
[DEBUG][2021-08-12 22:07:12 771][org.apache.ibatis.io.ResolverUtil]-[Checking to see if class com.daijunyi.mapper.UserMapper matches criteria [is assignable to Object]]
[DEBUG][2021-08-12 22:07:12 772][org.apache.ibatis.io.ResolverUtil]-[Checking to see if class com.daijunyi.model.User matches criteria [is assignable to Object]]
[DEBUG][2021-08-12 22:07:12 772][org.apache.ibatis.io.ResolverUtil]-[Checking to see if class com.daijunyi.test.BGMTest matches criteria [is assignable to Object]]
MyMybatisPlugin:{tx=11}
[DEBUG][2021-08-12 22:07:12 802][org.apache.ibatis.datasource.pooled.PooledDataSource]-[PooledDataSource forcefully closed/removed all connections.]
[DEBUG][2021-08-12 22:07:12 802][org.apache.ibatis.datasource.pooled.PooledDataSource]-[PooledDataSource forcefully closed/removed all connections.]
[DEBUG][2021-08-12 22:07:12 802][org.apache.ibatis.datasource.pooled.PooledDataSource]-[PooledDataSource forcefully closed/removed all connections.]
[DEBUG][2021-08-12 22:07:12 803][org.apache.ibatis.datasource.pooled.PooledDataSource]-[PooledDataSource forcefully closed/removed all connections.]
[DEBUG][2021-08-12 22:07:13 233][org.apache.ibatis.datasource.pooled.PooledDataSource]-[Created connection 1927963027.]
[DEBUG][2021-08-12 22:07:13 236][org.apache.ibatis.datasource.pooled.PooledDataSource]-[Returned connection 1927963027 to pool.]
包装对象对:org.apache.ibatis.executor.CachingExecutor
包装对象后:org.apache.ibatis.executor.CachingExecutor
包装对象对:org.apache.ibatis.scripting.defaults.DefaultParameterHandler
包装对象后:org.apache.ibatis.scripting.defaults.DefaultParameterHandler
包装对象对:org.apache.ibatis.executor.resultset.DefaultResultSetHandler
包装对象后:com.sun.proxy.$Proxy10
包装对象对:org.apache.ibatis.executor.statement.RoutingStatementHandler
包装对象后:org.apache.ibatis.executor.statement.RoutingStatementHandler
[DEBUG][2021-08-12 22:10:03 820][org.apache.ibatis.transaction.jdbc.JdbcTransaction]-[Opening JDBC Connection]
[DEBUG][2021-08-12 22:10:03 823][org.apache.ibatis.datasource.pooled.PooledDataSource]-[Checked out connection 1927963027 from pool.]
[DEBUG][2021-08-12 22:10:07 233][com.daijunyi.mapper.UserMapper.getUserList]-[==>  Preparing: select * from `user`]
[DEBUG][2021-08-12 22:10:08 002][com.daijunyi.mapper.UserMapper.getUserList]-[==> Parameters: ]
MyMybatisPlugin...intercept
[DEBUG][2021-08-12 22:11:19 949][com.daijunyi.mapper.UserMapper.getUserList]-[<==      Total: 15]
[User{userId=2, userName='红领巾', uStatus=2}, User{userId=111, userName='白毛女', uStatus=2}, User{userId=112, userName='红领巾3', uStatus=2}, User{userId=113, userName='红领巾3', uStatus=2}, User{userId=114, userName='红领巾3', uStatus=2}, User{userId=115, userName='红领巾3', uStatus=2}, User{userId=116, userName='红领巾3', uStatus=2}, User{userId=117, userName='111', uStatus=1}, User{userId=118, userName='红林俊杰', uStatus=2}, User{userId=119, userName='11111', uStatus=1}, User{userId=120, userName='红林俊杰11', uStatus=2}, User{userId=121, userName='11111', uStatus=1}, User{userId=122, userName='红林俊杰11', uStatus=2}, User{userId=123, userName='11111', uStatus=1}, User{userId=124, userName='红林俊杰11', uStatus=2}]
Disconnected from the target VM, address: '127.0.0.1:49288', transport: 'socket'

Process finished with exit code 0

3、比如我们修改一个查询语句拦截参考

  • parameterHandler.mappedStatement是拿出了PreparedStatementHandler中的parameterHandler(DefaultParameterHandler)对象中的mappedStatement
  • SystemMetaObject就是个反射工具

    @Intercepts(@Signature(type = StatementHandler.class, method = "parameterize", args = {Statement.class}))
    public class MyStatementHandlerPlugin implements Interceptor {
      @Override
      public Object intercept(Invocation invocation) throws Throwable {
          Object target = invocation.getTarget();
          MetaObject metaObject = SystemMetaObject.forObject(target);
          Object obj = metaObject.getValue("parameterHandler.mappedStatement");
          if (obj instanceof MappedStatement){
              MappedStatement mappedStatement = (MappedStatement) obj;
              String id = mappedStatement.getId();
              if (id !=null && "com.daijunyi.mapper.UserMapper.getUserList".equals(id)){
                  Object value = metaObject.getValue("parameterHandler.parameterObject");
                  if (value != null && value instanceof Integer){
                      metaObject.setValue("parameterHandler.parameterObject",124);
                  }
              }
          }
          Object proceed = invocation.proceed();
          return proceed;
      }
    
      @Override
      public Object plugin(Object target) {
          Object wrap = Plugin.wrap(target, this);
          return wrap;
      }
    
      @Override
      public void setProperties(Properties properties) {
    
      }
    }
    

    4、批量操作

    1、开启一个批处理sqlSession

    sqlSessionFactory.openSession(ExecutorType.BATCH);

    public SqlSessionFactory getSqlSessionFactory() {
          String resource = "mybatis.xml";
          SqlSessionFactory sqlSessionFactory = null;
          try {
              InputStream inputStream = Resources.getResourceAsStream(resource);
              sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
          } catch (IOException e) {
              e.printStackTrace();
          }
          return sqlSessionFactory;
      }
    @Test
    public void addUser(){
      SqlSession sqlSession = null;
      long beforeTime = 0;
      try {
          SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
          sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH);
          UserMapper mapper = sqlSession.getMapper(UserMapper.class);
          Random random = new Random();
          beforeTime = System.currentTimeMillis();
          for (int i=0;i<10000;i++){
              int t = mapper.addUser(new User(UUID.randomUUID().toString().substring(0, 10), random.nextInt(2)));
          }
          sqlSession.commit();
      } catch (Exception e) {
          e.printStackTrace();
      } finally {
          if (sqlSession != null){
              sqlSession.close();
          }
      }
      long time = System.currentTimeMillis() - beforeTime;
      System.out.println(time);
    }
    

    2、SpringMVC中配置批处理

    在Spring的配置文件 applicationContext.xml文件中

      <!--整合mybatis spring 管理所有组件
          1、使用
      -->
      <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
          <property name="dataSource" ref="dataSource"/>
          <!--指定配置文件-->
          <property name="configLocation" value="classpath:mybatis-config.xml"/>
          <!--指定映射文件-->
          <property name="mapperLocations" value="classpath:mapper/*.xml"/>
      </bean>
    
      <!--配置批处理sqlSession-->
      <bean id="sqlSessionTemplate" class="org.mybatis.spring.SqlSessionTemplate">
          <constructor-arg name="sqlSessionFactory" ref="sqlSessionFactory"/>
          <constructor-arg name="executorType" value="BATCH"/>
      </bean>
    

    5、存储过程

    image.png
    调用
    image.png

    6、自定义类型处理器

    1、默认枚举处理器

  • EnumOrdinalTypeHandler:处理是把枚举类型转换为index的处理器ordinal()方法

  • EnumTypeHandler:处理是把枚举转换为枚举名称的处理name()方法

(1)创建枚举类

public enum UStatus {
    Live,Die
}

(2)修改User

package com.daijunyi.model;

public class User {

    private Integer userId;

    private String userName;

    private UStatus uStatus;

    public User() {
    }

    public User(String userName, UStatus uStatus) {
        this.userName = userName;
        this.uStatus = uStatus;
    }

    public Integer getUserId() {
        return userId;
    }

    public void setUserId(Integer userId) {
        this.userId = userId;
    }

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName == null ? null : userName.trim();
    }

    public UStatus getuStatus() {
        return uStatus;
    }

    public void setuStatus(UStatus uStatus) {
        this.uStatus = uStatus;
    }

    @Override
    public String toString() {
        return "User{" +
                "userId=" + userId +
                ", userName='" + userName + '\'' +
                ", uStatus=" + uStatus +
                '}';
    }
}

(3)数据库类型
image.png
(4)配置MyBatis类型处理器

  • 指定java类型
  • 指定jdbc类型INTEGER 是在(java.sql.JDBCType)中查看
      <typeHandlers>
          <typeHandler handler="org.apache.ibatis.type.EnumOrdinalTypeHandler" javaType="com.daijunyi.model.UStatus" jdbcType="INTEGER"/>
      </typeHandlers>
    

    2、自定义处理器类型

    假如我们的枚举类型如下
    我们想把0 1 10存进数据库
    而返回给前端的时候是msg 活着 死了 未知这样 就需要我们自定义类型了 ```java package com.daijunyi.model;

public enum UStatus { Live(0,”活着”),Die(1,”死了”),UnKnow(10,”未知”);

private String msg;
private Integer code;
UStatus(Integer code,String msg) {
    this.msg = msg;
    this.code = code;
}

public String getMsg() {
    return msg;
}

public void setMsg(String msg) {
    this.msg = msg;
}

public Integer getCode() {
    return code;
}

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

public static UStatus getUStatusByCode(Integer code){
    switch (code){
        case 0:
            return UStatus.Live;
        case 1:
            return UStatus.Die;
        default:
            return UStatus.UnKnow;
    }
}

}


- 自定义类型处理器
```java
package com.daijunyi.typehandler;

import com.daijunyi.model.UStatus;
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;

public class UStatueTypeHandler implements TypeHandler<UStatus> {
    //保存到数据库的时候调用的方法
    @Override
    public void setParameter(PreparedStatement ps, int i, UStatus parameter, JdbcType jdbcType) throws SQLException {
        ps.setInt(i,parameter.getCode());
    }

    //查询
    @Override
    public UStatus getResult(ResultSet rs, String columnName) throws SQLException {
        int code = rs.getInt(columnName);
        UStatus uStatusByCode = UStatus.getUStatusByCode(code);
        return uStatusByCode;
    }
    //根据列查询
    @Override
    public UStatus getResult(ResultSet rs, int columnIndex) throws SQLException {
        int code = rs.getInt(columnIndex);
        UStatus uStatusByCode = UStatus.getUStatusByCode(code);
        return uStatusByCode;
    }

    //调用存储过程封装结果
    @Override
    public UStatus getResult(CallableStatement cs, int columnIndex) throws SQLException {
        int code = cs.getInt(columnIndex);
        UStatus uStatusByCode = UStatus.getUStatusByCode(code);
        return uStatusByCode;
    }
}
  • 配置类型处理器

    <typeHandlers>
    <typeHandler handler="com.daijunyi.typehandler.UStatueTypeHandler" javaType="com.daijunyi.model.UStatus" jdbcType="INTEGER"/>
    </typeHandlers>
    
  • 存储进数据库的数据

image.png