MyBatis 源代码阅读笔记 2 基于"注解"方式的代码编写
源代码工程
https://github.com/Jason-Chen-2017/source-code-reading
代码详解:
package com.light.sword;
import org.apache.ibatis.datasource.DataSourceFactory;
import org.apache.ibatis.datasource.pooled.PooledDataSourceFactory;
import org.apache.ibatis.mapping.Environment;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.apache.ibatis.transaction.TransactionFactory;
import org.apache.ibatis.transaction.jdbc.JdbcTransactionFactory;
import javax.sql.DataSource;
import java.util.List;
import java.util.Properties;
public class MyBatisDemoAnno {
public static void main(String[] args) {
// 获取数据源
DataSourceFactory dataSourceFactory = new PooledDataSourceFactory();
Properties props = new Properties();
props.setProperty("driver", "com.mysql.cj.jdbc.Driver");
props.setProperty("url", "jdbc:mysql://127.0.0.1:3306/test");
props.setProperty("username", "root");
props.setProperty("password", "root");
dataSourceFactory.setProperties(props);
DataSource dataSource = dataSourceFactory.getDataSource();
// 事务管理
TransactionFactory transactionFactory = new JdbcTransactionFactory();
Environment environment = new Environment("dev", transactionFactory, dataSource);
// 配置对象
Configuration configuration = new Configuration(environment);
// add Mapper 接口类
configuration.addMapper(ProductMapper.class);
// open SqlSession
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(configuration);
SqlSession sqlSession = sqlSessionFactory.openSession();
// 从SQL会话中获取 ProductMapper
ProductMapper productMapper = sqlSession.getMapper(ProductMapper.class);
List<Product> productList = productMapper.findAll();
System.out.println(productList);
}
}
其中, ProductMapper 如下:
package com.light.sword;
import org.apache.ibatis.annotations.Select;
import java.util.List;
public interface ProductMapper {
@Select("select * from product")
List<Product> findAll();
}
运行输出:
[Product{id=1, name='Product1'}, Product{id=2, name='Product2'}]
对应到XML的配置则是:
<?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>
<typeAliases>
<typeAlias alias="Product" type="com.light.sword.Product"></typeAlias>
</typeAliases>
<environments default="dev">
<environment id="dev">
<transactionManager type="JDBC"></transactionManager>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://127.0.0.1:3306/test"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="mapper/ProductMapper.xml"></mapper>
</mappers>
</configuration>
mapper/ProductMapper.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!-- namespace命名空间,做SQL隔离 -->
<mapper namespace="productMapper">
<select id="listAll" resultType="Product">
select *
from product
</select>
</mapper>
源码运行解析
DataSource
===CONFINGURATION==============================================
jdbcDriver com.mysql.cj.jdbc.Driver
jdbcUrl jdbc:mysql://127.0.0.1:3306/test
jdbcUsername root
jdbcPassword ************
poolMaxActiveConnections 10
poolMaxIdleConnections 5
poolMaxCheckoutTime 20000
poolTimeToWait 20000
poolPingEnabled false
poolPingQuery NO PING QUERY SET
poolPingConnectionsNotUsedFor 0
---STATUS-----------------------------------------------------
activeConnections 0
idleConnections 0
requestCount 0
averageRequestTime 0
averageCheckoutTime 0
claimedOverdue 0
averageOverdueCheckoutTime 0
hadToWait 0
averageWaitTime 0
badConnectionCount 0
===============================================================
我们使用的是
DataSourceFactory dataSourceFactory = new PooledDataSourceFactory();


  public PooledDataSource() {
    dataSource = new UnpooledDataSource();
  }// 获取数据源
DataSourceFactory dataSourceFactory = new PooledDataSourceFactory();
Properties props = new Properties();
props.setProperty("driver", "com.mysql.cj.jdbc.Driver");
props.setProperty("url", "jdbc:mysql://127.0.0.1:3306/test");
props.setProperty("username", "root");
props.setProperty("password", "root");
dataSourceFactory.setProperties(props);
DataSource dataSource = dataSourceFactory.getDataSource();

然后 事务管理器:
public class JdbcTransaction implements Transaction {
  private static final Log log = LogFactory.getLog(JdbcTransaction.class);
  protected Connection connection;
  protected DataSource dataSource;
  protected TransactionIsolationLevel level;
  protected boolean autoCommit;
  public JdbcTransaction(DataSource ds, TransactionIsolationLevel desiredLevel, boolean desiredAutoCommit) {
    dataSource = ds;
    level = desiredLevel;
    autoCommit = desiredAutoCommit;
  }
  public JdbcTransaction(Connection connection) {
    this.connection = connection;
  }
  @Override
  public Connection getConnection() throws SQLException {
    if (connection == null) {
      openConnection();
    }
    return connection;
  }
  @Override
  public void commit() throws SQLException {
    if (connection != null && !connection.getAutoCommit()) {
      if (log.isDebugEnabled()) {
        log.debug("Committing JDBC Connection [" + connection + "]");
      }
      connection.commit();
    }
  }
  @Override
  public void rollback() throws SQLException {
    if (connection != null && !connection.getAutoCommit()) {
      if (log.isDebugEnabled()) {
        log.debug("Rolling back JDBC Connection [" + connection + "]");
      }
      connection.rollback();
    }
  }
  @Override
  public void close() throws SQLException {
    if (connection != null) {
      resetAutoCommit();
      if (log.isDebugEnabled()) {
        log.debug("Closing JDBC Connection [" + connection + "]");
      }
      connection.close();
    }
  }
  protected void setDesiredAutoCommit(boolean desiredAutoCommit) {
    try {
      if (connection.getAutoCommit() != desiredAutoCommit) {
        if (log.isDebugEnabled()) {
          log.debug("Setting autocommit to " + desiredAutoCommit + " on JDBC Connection [" + connection + "]");
        }
        connection.setAutoCommit(desiredAutoCommit);
      }
    } catch (SQLException e) {
      // Only a very poorly implemented driver would fail here,
      // and there's not much we can do about that.
      throw new TransactionException("Error configuring AutoCommit.  "
          + "Your driver may not support getAutoCommit() or setAutoCommit(). "
          + "Requested setting: " + desiredAutoCommit + ".  Cause: " + e, e);
    }
  }
  protected void resetAutoCommit() {
    try {
      if (!connection.getAutoCommit()) {
        // MyBatis does not call commit/rollback on a connection if just selects were performed.
        // Some databases start transactions with select statements
        // and they mandate a commit/rollback before closing the connection.
        // A workaround is setting the autocommit to true before closing the connection.
        // Sybase throws an exception here.
        if (log.isDebugEnabled()) {
          log.debug("Resetting autocommit to true on JDBC Connection [" + connection + "]");
        }
        connection.setAutoCommit(true);
      }
    } catch (SQLException e) {
      if (log.isDebugEnabled()) {
        log.debug("Error resetting autocommit to true "
            + "before closing the connection.  Cause: " + e);
      }
    }
  }
  protected void openConnection() throws SQLException {
    if (log.isDebugEnabled()) {
      log.debug("Opening JDBC Connection");
    }
    connection = dataSource.getConnection();
    if (level != null) {
      connection.setTransactionIsolation(level.getLevel());
    }
    setDesiredAutoCommit(autoCommit);
  }
  @Override
  public Integer getTimeout() throws SQLException {
    return null;
  }
}openSession
根据 configuration 构建 SqlSessionFactory, 然后使用 SqlSessionFactory 打开session :
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(configuration);
SqlSession sqlSession = sqlSessionFactory.openSession();
// 从SQL会话中获取 ProductMapper
ProductMapper productMapper = sqlSession.getMapper(ProductMapper.class);
List<Product> productList = productMapper.findAll();
通过 接口类型的 ProductMapper 创建出实例对象的代码:
  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
package org.apache.ibatis.binding;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.apache.ibatis.session.SqlSession;
/**
* @author Lasse Voss
*/
public class MapperProxyFactory<T> {
private final Class<T> mapperInterface;
private final Map<Method, MapperMethod> methodCache = new ConcurrentHashMap<>();
public MapperProxyFactory(Class<T> mapperInterface) {
this.mapperInterface = mapperInterface;
}
public Class<T> getMapperInterface() {
return mapperInterface;
}
public Map<Method, MapperMethod> getMethodCache() {
return methodCache;
}
@SuppressWarnings("unchecked")
protected T newInstance(MapperProxy<T> mapperProxy) {
return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
}
public T newInstance(SqlSession sqlSession) {
final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache);
return newInstance(mapperProxy);
}
}










