MyBatis source code analysis

Running process MyBatis

Code and traditional JDBC programming process to query the database summary.

  1. Load the driver.
  2. Create a connection, Connection object.
  3. Connection to execute sql statements in accordance with Statement or create PreparedStatement.
  4. To return a result set ResultSet.
  5. ResultSet manually mapped to a JavaBean.
        public static void main(String[] args) {
            //声明Connection对象
            Connection con = null;
            //遍历查询结果集
            try {
                //加载驱动程序
                Class.forName("com.mysql.jdbc.Driver");
                //创建 connection 对象
                con = DriverManager.getConnection("jdbc:mysql://localhost:3306/db","username","password");

                //使用 connection 对象创建statement 或者 PreparedStatement 类对象,用来执行SQL语句
                Statement statement = con.createStatement();
                //要执行的SQL语句
                String sql = "select * from emp";
                //3.ResultSet类,用来存放获取的结果集!!
                ResultSet rs = statement.executeQuery(sql);
   
                String job = "";
                String id = "";
                while(rs.next()){
                    //获取stuname这列数据
                    job = rs.getString("job");
                    //获取stuid这列数据
                    id = rs.getString("ename");

                    //输出结果
                    System.out.println(id + "\t" + job);
                }
            } catch(ClassNotFoundException e) {
                e.printStackTrace();
            } catch(SQLException e) {
                //数据库连接失败异常处理
                e.printStackTrace();
            }catch (Exception e) {
                e.printStackTrace();
            }finally{
                rs.close();
                con.close();
            }
        }

Coding to achieve MyBatis query the database, to facilitate understanding, do not use SpringMybatis, the whole process will be much more complicated after the addition of Spring. The original post can use traditional MyBatis JDBC programming series so simple. Specific processes are summarized.

  1. Use profiling SqlSessionFactory.
  2. Use SqlSessionFactory get SqlSession, SqlSession equivalent of the traditional JDBC Conection.
  3. Use SqlSession get Mapper.
  4. Mapper performed with the sql statement, and returns the result directly encapsulated into a JavaBean.
//获取 sqlSession,sqlSession 相当于传统 JDBC 的 Conection
public static SqlSession getSqlSession(){
        InputStream configFile = new FileInputStream(filePath);
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder.build(configFile);
    return sqlSessionFactory.openSession();
}

//使用 sqlSession 获得对应的 mapper,mapper 用来执行 sql 语句。
public static User get(SqlSession sqlSession, int id){
    UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
    return userMapper.selectByPrimaryKey(id);
}

to sum up

MyBatis source code analysis

The following specific analysis procedure performed MyBatis code **

Overall structure

Source code analysis

Let me talk about the code that processes most of the framework:

Look at our profile.

<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/db"/>
          <property name="username" value="root"/>
          <property name="password" value="123456"/>
        </dataSource>
      </transactionManager>
    </environment>
  </environments>
</configuration>
<mappers>
  <mapper resource="xml/UserMapper.xml"/>
</mappers>
public static SqlSession getSqlSession(){
    //读取上面的配置文件
        InputStream configFile = new FileInputStream(filePath);
    //根据上面配置的 dataSource 配置 SqlSessionFactory,并且建立 Mapper 接口和 xml 之间的关系。
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder.build(configFile);
    //工厂方法返回一个 sqlSession
    return sqlSessionFactory.openSession();
}

//我们来重点看看 openSession 做了什么操作, DefaultSqlSessionFactory.java
@Override
public SqlSession openSession() {
  return this.openSessionFromDataSource(this.configuration.getDefaultExecutorType(), (TransactionIsolationLevel)null, false);
}

public Configuration getConfiguration() {
  return this.configuration;
}
//这个函数里面有着事务控制相关的代码。
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
  Transaction tx = null;

  DefaultSqlSession var8;
  try {
    Environment environment = this.configuration.getEnvironment();
    TransactionFactory transactionFactory = this.getTransactionFactoryFromEnvironment(environment);
    //根据上面的参数得到 TransactionFactory,通过 TransactionFactory 生成一个 Transaction,可以理解为这个 SqlSession 的事务控制器
    tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
    // 将这个事务控制器封装在 Executor 里
    Executor executor = this.configuration.newExecutor(tx, execType);
    // 使用 configuration 配置类,Executor,和 configuration(是否自动提交) 来构建一个 DefaultSqlSession。
    var8 = new DefaultSqlSession(this.configuration, executor, autoCommit);
  } catch (Exception var12) {
    this.closeTransaction(tx);
    throw ExceptionFactory.wrapException("Error opening session.  Cause: " + var12, var12);
  } finally {
    ErrorContext.instance().reset();
  }

  return var8;
}

SqlSession implementation process.

SqlSession interface definition: which defines the CRUD rollback and commit the like.

public interface SqlSession extends Closeable {
    <T> T selectOne(String var1);

    <T> T selectOne(String var1, Object var2);

    <E> List<E> selectList(String var1);

    <E> List<E> selectList(String var1, Object var2);

    <E> List<E> selectList(String var1, Object var2, RowBounds var3);

    <K, V> Map<K, V> selectMap(String var1, String var2);

    <K, V> Map<K, V> selectMap(String var1, Object var2, String var3);

    <K, V> Map<K, V> selectMap(String var1, Object var2, String var3, RowBounds var4);

    <T> Cursor<T> selectCursor(String var1);

    <T> Cursor<T> selectCursor(String var1, Object var2);

    <T> Cursor<T> selectCursor(String var1, Object var2, RowBounds var3);

    void select(String var1, Object var2, ResultHandler var3);

    void select(String var1, ResultHandler var2);

    void select(String var1, Object var2, RowBounds var3, ResultHandler var4);

    int insert(String var1);

    int insert(String var1, Object var2);

    int update(String var1);

    int update(String var1, Object var2);

    int delete(String var1);

    int delete(String var1, Object var2);

    void commit();

    void commit(boolean var1);

    void rollback();

    void rollback(boolean var1);

    List<BatchResult> flushStatements();

    void close();

    void clearCache();

    Configuration getConfiguration();

    <T> T getMapper(Class<T> var1);

    Connection getConnection();
}

Next, by acquiring a corresponding Mapper sqlSession:

//使用 sqlSession 获得对应的 mapper,mapper 用来执行 sql 语句。
public static User get(SqlSession sqlSession, int id){
    UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
    return userMapper.selectByPrimaryKey(id);
}

DefaultSqlSession of getMapper achieve:

public <T> T getMapper(Class<T> type) {
  return this.configuration.getMapper(type, this);
}

//从 configuration 里面 getMapper,Mapper 就在 Configuration 里
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
  return this.mapperRegistry.getMapper(type, sqlSession);
}

MapperRegistry in getMapper final realization:

Here we must explain our side of the interface only defines the abstract CRUD, and this does not have any class that implements the interface, then in the end it is how the xml associated with the interface together and generate implementation classes that?

public class MapperRegistry {
    private final Configuration config;
    // 用一个 Map 来存储接口和 xml 文件之间的映射关系,key 应该是接口,但是 value 是 MapperProxyFactory
    private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap();

    public MapperRegistry(Configuration config) {
        this.config = config;
    }

    public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
      //获取到这个接口对应的 MapperProxyFactory。
        MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory)this.knownMappers.get(type);
        if (mapperProxyFactory == null) {
            throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
        } else {
            try {
                //用上一步获取的 MapperProxyFactory 和 sqlSession 构建对应的 Class
                return mapperProxyFactory.newInstance(sqlSession);
            } catch (Exception var5) {
                throw new BindingException("Error getting mapper instance. Cause: " + var5, var5);
            }
        }
    }
}

Next we look at the specific realization of newInstance:

public T newInstance(SqlSession sqlSession) {
  // mapperInterface 就是接口
  MapperProxy<T> mapperProxy = new MapperProxy(sqlSession, this.mapperInterface, this.methodCache);
  return this.newInstance(mapperProxy);
}

protected T newInstance(MapperProxy<T> mapperProxy) {
  //动态代理,这里的动态代理有一些不一样
  return Proxy.newProxyInstance(this.mapperInterface.getClassLoader(), new Class[]{this.mapperInterface}, mapperProxy);
}

The normal process of dynamic proxies:

Compared with the traditional dynamic proxy, MyBatis class interface is not implemented, then it is how to achieve a dynamic proxy that?

We look MapperProxy source:

public class MapperProxy<T> implements InvocationHandler, Serializable {
    private static final long serialVersionUID = -6424540398559729838L;
    private final SqlSession sqlSession;
    private final Class<T> mapperInterface;
    private final Map<Method, MapperMethod> methodCache;

    public MapperProxy(SqlSession sqlSession, Class<T> mapperInterface, Map<Method, MapperMethod> methodCache) {
        this.sqlSession = sqlSession;
        this.mapperInterface = mapperInterface;
        this.methodCache = methodCache;
    }
        // 正常的动态代理中 Object proxy 这个参数应该是接口的实现类
    // com.paul.pkg.UserMapper@5a123uf
    // 现在里面是 org.apache.ibatis.binding.MapperProxy@6y213kn, 这俩面
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        try {
            if (Object.class.equals(method.getDeclaringClass())) {
                return method.invoke(this, args);
            }

            if (this.isDefaultMethod(method)) {
                return this.invokeDefaultMethod(proxy, method, args);
            }
        } catch (Throwable var5) {
            throw ExceptionUtil.unwrapThrowable(var5);
        }
        // Mapper 走这个流程,先尝试在缓存里获取 method
        MapperMethod mapperMethod = this.cachedMapperMethod(method);
        return mapperMethod.execute(this.sqlSession, args);
    }

    private MapperMethod cachedMapperMethod(Method method) {
        MapperMethod mapperMethod = (MapperMethod)this.methodCache.get(method);
        if (mapperMethod == null) {
            // mapperMethod 的构建,通过接口名,方法,和 xml 配置(通过 sqlSession 的 Configuration 获得)
            mapperMethod = new MapperMethod(this.mapperInterface, method, this.sqlSession.getConfiguration());
          //通过 execute 执行方法,因为 sqlSession 封装了 Executor,所以还要传进来,execute 方法使用
          //sqlSession 里面的方法。
            this.methodCache.put(method, mapperMethod);
        }

        return mapperMethod;
    }

}

Look MapperMethod definition:

// command 里面包含了方法名,比如 com.paul.pkg.selectByPrimaryKey
// type, 表示是 SELECT,UPDATE,INSERT,或者 DELETE
// method 是方法的签名
public class MapperMethod {
    private final MapperMethod.SqlCommand command;
    private final MapperMethod.MethodSignature method;

    public MapperMethod(Class<?> mapperInterface, Method method, Configuration config) {
        this.command = new MapperMethod.SqlCommand(config, mapperInterface, method);
        this.method = new MapperMethod.MethodSignature(config, mapperInterface, method);
    }
}

DefaultSqlSession into the corresponding implementation of sql statement:

public <T> T selectOne(String statement, Object parameter) {
  List<T> list = this.selectList(statement, parameter);
  if (list.size() == 1) {
    return list.get(0);
  } else if (list.size() > 1) {
    throw new TooManyResultsException("Expected one result (or null) to be returned by selectOne(), but found: " + list.size());
  } else {
    return null;
  }
}

public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
  List var5;
  try {
    // 这里又需要 configuration 来获取对应的 statement
    // MappedStatement 里面有 xml 文件,和要执行的方法,就是 xml 里面的 id,statementType,以及 sql 语句。
    MappedStatement ms = this.configuration.getMappedStatement(statement);
    // 用 executor 执行 query,executor 里面应该是包装了 JDBC。
    var5 = this.executor.query(ms, this.wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
  } catch (Exception var9) {
    throw ExceptionFactory.wrapException("Error querying database.  Cause: " + var9, var9);
  } finally {
    ErrorContext.instance().reset();
  }

  return var5;
}

Executor implementation class which query method performed:

public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
  BoundSql boundSql = ms.getBoundSql(parameterObject);
  CacheKey key = this.createCacheKey(ms, parameterObject, rowBounds, boundSql);
  return this.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}
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) {
    this.flushCacheIfRequired(ms);
    if (ms.isUseCache() && resultHandler == null) {
      this.ensureNoOutParams(ms, boundSql);
      List<E> list = (List)this.tcm.getObject(cache, key);
      if (list == null) {
        list = this.delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
        this.tcm.putObject(cache, key, list);
      }

      return list;
    }
  }
  // 使用 delegate 去 query,delegate 是 SimpleExecutor。里面使用 JDBC 进行数据库操作。
  return this.delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}

Manually implement a simple MyBatis

  1. Creating SqlSessionFactory instance.
  2. Instantiation, load profile creation Configuration object.
  3. SqlSession created by the factory.
  4. Mapper interface to obtain dynamic proxy by SqlSession.
  5. SqlSession in a proxy callback dynamic query method.
  6. SqlSession query methods will be forwarded to the Executor.
  7. Executor JDBC-based access to the database to obtain data.
  8. Executor by converting the data into POJO reflected and returned to SqlSession.
  9. The data is returned to the caller.

The overall project built with Maven, mybatis-demo is an example of using the Spring MyBatis detachment. paul-mybatis is mybatis framework of our own implementation.

  • First, in accordance with the flow when using the code mybatis our previous, create mapper interfaces, xml files, and POJO and set some configuration files.

    Interface: TUserMapper

      package com.paul.mybatis.mapper;
    
      import com.paul.mybatis.entity.TUser;
    
      import java.util.List;
    
      public interface TUserMapper {
    
          TUser selectByPrimaryKey(Integer id);
    
          List<TUser> selectAll();
      }

    xml file

      <?xml version="1.0" encoding="UTF-8" ?>
      <!DOCTYPE mapper
              PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
              "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
      <mapper namespace="com.paul.mybatis.mapper.TUserMapper">
    
          <select id="selectByPrimaryKey" resultType="TUser">
              select *
              from t_user
              where id = #{id,jdbcType=INTEGER}
          </select>
    
          <select id="selectAll" resultType="TUser">
              select *
              from t_user
          </select>
    
      </mapper>

    Entity classes, properties should want to match the database

      package com.paul.mybatis.entity;
    
      public class TUser {
    
      private Integer id;
    
      private String userName;
    
      private String realName;
    
      private Byte sex;
    
      private String mobile;
    
      public Integer getId() {
          return id;
      }
    
      public void setId(Integer id) {
          this.id = id;
      }
    
      public String getUserName() {
          return userName;
      }
    
      public void setUserName(String userName) {
          this.userName = userName;
      }
    
      public String getRealName() {
          return realName;
      }
    
      public void setRealName(String realName) {
          this.realName = realName;
      }
    
      public Byte getSex() {
          return sex;
      }
    
      public void setSex(Byte sex) {
          this.sex = sex;
      }
    
      public String getMobile() {
          return mobile;
      }
    
      public void setMobile(String mobile) {
          this.mobile = mobile;
      }
      }
    

    Database connection profile, db.properties

      jdbc.driver=com.mysql.jdbc.Driver
      jdbc.url=jdbc:mysql://localhost:3306/mybatis?useUnicode=true&characterEncoding=utf8
      jdbc.username=root
      jdbc.password=root
  • Let's focus on xml files, mapper file in the namespace, id, resultType and sql statement to be stored, we define a POJO to store this information.

      package com.paul.mybatis.confiuration;
    
    
      /**
      *
      * XML 中的 sql 配置信息加载到这个类中
      *
      */
      public class MappedStatement {
    
      private String namespace;
    
      private String id;
    
      private String resultType;
    
      private String sql;
    
      public String getNamespace() {
          return namespace;
      }
    
      public void setNamespace(String namespace) {
          this.namespace = namespace;
      }
    
      public String getId() {
          return id;
      }
    
      public void setId(String id) {
          this.id = id;
      }
    
      public String getResultType() {
          return resultType;
      }
    
      public void setResultType(String resultType) {
          this.resultType = resultType;
      }
    
      public String getSql() {
          return sql;
      }
    
      public void setSql(String sql) {
          this.sql = sql;
      }
      }
    
  • Let's create a Configuration class, to save all configuration files and xml file information.

      package com.paul.mybatis.confiuration;
    
      import java.util.HashMap;
      import java.util.List;
      import java.util.Map;
    
      /**
      *
      * 所有的配置信息
      *
      */
      public class Configuration {
    
      private String jdbcDriver;
    
      private String jdbcUrl;
    
      private String jdbcPassword;
    
      private String jdbcUsername;
    
      private Map<String,MappedStatement> mappedStatement = new HashMap<>();
    
      public Map<String, MappedStatement> getMappedStatement() {
          return mappedStatement;
      }
    
      public void setMappedStatement(Map<String, MappedStatement> mappedStatement) {
          this.mappedStatement = mappedStatement;
      }
    
      public String getJdbcDriver() {
          return jdbcDriver;
      }
    
      public void setJdbcDriver(String jdbcDriver) {
          this.jdbcDriver = jdbcDriver;
      }
    
      public String getJdbcUrl() {
          return jdbcUrl;
      }
    
      public void setJdbcUrl(String jdbcUrl) {
          this.jdbcUrl = jdbcUrl;
      }
    
      public String getJdbcPassword() {
          return jdbcPassword;
      }
    
      public void setJdbcPassword(String jdbcPassword) {
          this.jdbcPassword = jdbcPassword;
      }
    
      public String getJdbcUsername() {
          return jdbcUsername;
      }
    
      public void setJdbcUsername(String jdbcUsername) {
          this.jdbcUsername = jdbcUsername;
      }
      }
    
  • Once you have configured class, we can build a SqlSessionFactory through this configuration class.
    SqlSessionFactory abstract template

      package com.paul.mybatis.factory;
    
      import com.paul.mybatis.sqlsession.SqlSession;
    
      public interface SqlSessionFactory {
    
          SqlSession openSession();
      }
    

    Default implementation class completed two major functions, load configuration information into the Configuration object, the implementation creates SqlSession function.

      package com.paul.mybatis.factory;
    
      import com.paul.mybatis.confiuration.Configuration;
      import com.paul.mybatis.confiuration.MappedStatement;
      import com.paul.mybatis.sqlsession.DefaultSqlSession;
      import com.paul.mybatis.sqlsession.SqlSession;
      import org.dom4j.Document;
      import org.dom4j.DocumentException;
      import org.dom4j.Element;
      import org.dom4j.io.SAXReader;
    
      import java.io.File;
      import java.io.IOException;
      import java.io.InputStream;
      import java.net.URL;
      import java.util.ArrayList;
      import java.util.List;
      import java.util.Properties;
    
      /**
       *
       * 1.初始化时就完成了 configuration 的实例化
       * 2.工厂类,生成 sqlSession
       *
       */
      public class DefaultSqlSessionFactory implements SqlSessionFactory{
    
          //希望Configuration 是单例子并且唯一的
          private final Configuration configuration = new Configuration();
    
          // xml 文件存放的位置
          private static final String MAPPER_CONFIG_LOCATION = "mappers";
    
          // 数据库信息存放的位置
          private static final String DB_CONFIG_FILE = "db.properties";
    
    
          public DefaultSqlSessionFactory() {
              loadDBInfo();
              loadMapperInfo();
          }
    
          private void loadDBInfo() {
              InputStream db = this.getClass().getClassLoader().getResourceAsStream(DB_CONFIG_FILE);
              Properties p = new Properties();
    
              try {
                  p.load(db);
              } catch (IOException e) {
                  e.printStackTrace();
              }
              //将配置信息写入Configuration 对象
              configuration.setJdbcDriver(p.get("jdbc.driver").toString());
              configuration.setJdbcUrl(p.get("jdbc.url").toString());
              configuration.setJdbcUsername(p.get("jdbc.username").toString());
              configuration.setJdbcPassword(p.get("jdbc.password").toString());
    
          }
    
          //解析并加载xml文件
          private void loadMapperInfo(){
              URL resources = null;
              resources = this.getClass().getClassLoader().getResource(MAPPER_CONFIG_LOCATION);
              File mappers = new File(resources.getFile());
              //读取文件夹下面的文件信息
              if(mappers.isDirectory()){
                  File[] files = mappers.listFiles();
                  for(File file:files){
                      loadMapperInfo(file);
                  }
              }
          }
    
          private void loadMapperInfo(File file){
              SAXReader reader = new SAXReader();
              //通过read方法读取一个文件转换成Document 对象
              Document document = null;
              try {
                  document = reader.read(file);
              } catch (DocumentException e) {
                  e.printStackTrace();
              }
              //获取根结点元素对象<mapper>
              Element e = document.getRootElement();
              //获取命名空间namespace
              String namespace = e.attribute("namespace").getData().toString();
              //获取select,insert,update,delete子节点列表
              List<Element> selects = e.elements("select");
              List<Element> inserts = e.elements("select");
              List<Element> updates = e.elements("select");
              List<Element> deletes = e.elements("select");
    
              List<Element> all = new ArrayList<>();
              all.addAll(selects);
              all.addAll(inserts);
              all.addAll(updates);
              all.addAll(deletes);
    
              //遍历节点,组装成 MappedStatement 然后放入到configuration 对象中
              for(Element ele:all){
                  MappedStatement mappedStatement = new MappedStatement();
                  String id = ele.attribute("id").getData().toString();
                  String resultType = ele.attribute("resultType").getData().toString();
                  String sql = ele.getData().toString();
    
                  mappedStatement.setId(namespace+"."+id);
                  mappedStatement.setResultType(resultType);
                  mappedStatement.setNamespace(namespace);
                  mappedStatement.setSql(sql);
    
                  configuration.getMappedStatement().put(namespace+"."+id,mappedStatement);
              }
          }
    
          @Override
          public SqlSession openSession() {
              return new DefaultSqlSession(configuration);
          }
      }
    
  • In SqlSessionFactory was created DefaultSqlSession, we look at its implementation. SqlSession specific operation which should encapsulate all databases and methods of obtaining mapper implementation class. Using dynamic proxy class generated a strengthened. There is eventually transferred to the relevant database operations SqlSession, using the mapper can make programming more elegant.
    SqlSession interface methods defined template

      package com.paul.mybatis.sqlsession;
    
      import java.util.List;
    
      /**
       *
       * 封装了所有数据库的操作
       * 所有功能都是基于 Excutor 来实现的,Executor 封装了 JDBC 操作
       *
       *
       */
      public interface SqlSession {
    
          /**
           * 根据传入的条件查询单一结果
           * @param statement  方法对应 sql 语句,namespace+id
           * @param parameter  要传入 sql 语句中的查询参数
           * @param <T> 返回指定的结果对象
           * @return
           */
          <T> T selectOne(String statement, Object parameter);
    
          <T> List<T> selectList(String statement, Object parameter);
    
          <T> T getMapper(Class<T> type);
      }
    

    Default of SqlSession implementation class. Which we need to pass Executor, which encapsulates the Executor flow JDBC database operation. We focus on getMapper method.

      package com.paul.mybatis.sqlsession;
    
      import com.paul.mybatis.bind.MapperProxy;
      import com.paul.mybatis.confiuration.Configuration;
      import com.paul.mybatis.confiuration.MappedStatement;
      import com.paul.mybatis.executor.Executor;
      import com.paul.mybatis.executor.SimpleExecutor;
    
      import java.lang.reflect.Proxy;
      import java.util.List;
    
      public class DefaultSqlSession implements  SqlSession {
    
          private final Configuration configuration;
    
          private Executor executor;
    
          public DefaultSqlSession(Configuration configuration) {
              super();
              this.configuration = configuration;
              executor = new SimpleExecutor(configuration);
          }
    
          @Override
          public <T> T selectOne(String statement, Object parameter) {
              List<T> selectList = this.selectList(statement,parameter);
              if(selectList == null || selectList.size() == 0){
                  return null;
              }
              if(selectList.size() == 1){
                  return (T) selectList.get(0);
              }else{
                  throw new RuntimeException("too many result");
              }
          }
    
          @Override
          public <T> List<T> selectList(String statement, Object parameter) {
              MappedStatement ms = configuration.getMappedStatement().get(statement);
              return executor.query(ms,parameter);
          }
    
          @Override
          public <T> T getMapper(Class<T> type) {
              MapperProxy mp = new MapperProxy(this);
              //给我一个接口,还你一个实现类
              return (T)Proxy.newProxyInstance(type.getClassLoader(),new Class[]{type},mp);
          }
      }
    
  • Dynamic proxy InvocationHandler.

      package com.paul.mybatis.bind;
    
      import com.paul.mybatis.sqlsession.SqlSession;
    
      import java.lang.reflect.InvocationHandler;
      import java.lang.reflect.Method;
      import java.util.Collection;
      import java.util.Collections;
    
      /**
       *
       * 将请求转发给 sqlSession
       *
       */
      public class MapperProxy implements InvocationHandler {
    
          private SqlSession sqlSession;
    
          public MapperProxy(SqlSession sqlSession) {
              this.sqlSession = sqlSession;
          }
    
          @Override
          public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
              System.out.println(method.getDeclaringClass().getName()+"."+method.getName());
              if(Collection.class.isAssignableFrom(method.getReturnType())){
                  return sqlSession.selectList(method.getDeclaringClass().getName()+"."+method.getName(),args==null?null:args[0]);
              }else{
                  return sqlSession.selectOne(method.getDeclaringClass().getName()+"."+method.getName(),args==null?null:args[0]);
              }
          }
      }
    
  • Finally, look at our test classes

     package com.paul.mybatis;
    
     import com.paul.mybatis.entity.TUser;
     import com.paul.mybatis.factory.DefaultSqlSessionFactory;
     import com.paul.mybatis.factory.SqlSessionFactory;
     import com.paul.mybatis.mapper.TUserMapper;
     import com.paul.mybatis.sqlsession.SqlSession;
    
     public class TestDemo {
    
         public static void main(String[] args) {
             SqlSessionFactory sqlSessionFactory = new DefaultSqlSessionFactory();
    
             SqlSession sqlSession = sqlSessionFactory.openSession();
    
             TUserMapper mapper = sqlSession.getMapper(TUserMapper.class);
    
             TUser user = mapper.selectByPrimaryKey(1);
    
             System.out.println(user.toString());
         }
     }
    

Source entire project in the project source code , I hope you mark it, together with the improvements.

Guess you like

Origin www.cnblogs.com/paulwang92115/p/11028715.html