Mybatis DataSource

  • Author: HuiFer
  • Description: 该文介绍 mybatis DataSource 源码
  • 源码阅读工程: SourceHot-Mybatis

  • org.apache.ibatis.datasource.DataSourceFactory

  1. /**
  2. * 数据源工厂
  3. * @author Clinton Begin
  4. */
  5. public interface DataSourceFactory {
  6. /**
  7. * 设置 dataSource 属性
  8. * @param props
  9. */
  10. void setProperties(Properties props);
  11. /**
  12. * 获取 dataSource
  13. * @return {@link DataSource}
  14. */
  15. DataSource getDataSource();
  16. }

类图如下

image-20191223081023730

  • setProperties会将下列标签放入datasource
  1. <dataSource type="POOLED">
  2. <property name="driver" value="com.mysql.jdbc.Driver"/>
  3. <property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=false"/>
  4. <property name="username" value="mybatis"/>
  5. <property name="password" value="mybatis"/>
  6. </dataSource>
  • org.apache.ibatis.session.Configuration中有配置下面三个信息
  1. typeAliasRegistry.registerAlias("JNDI", JndiDataSourceFactory.class);
  2. typeAliasRegistry.registerAlias("POOLED", PooledDataSourceFactory.class);
  3. typeAliasRegistry.registerAlias("UNPOOLED", UnpooledDataSourceFactory.class);

JndiDataSourceFactory

  1. /**
  2. * @author Clinton Begin
  3. */
  4. public class JndiDataSourceFactory implements DataSourceFactory {
  5. public static final String INITIAL_CONTEXT = "initial_context";
  6. public static final String DATA_SOURCE = "data_source";
  7. public static final String ENV_PREFIX = "env.";
  8. /**
  9. * 直接 java 数据源
  10. */
  11. private DataSource dataSource;
  12. /**
  13. * 获取数据源的配置信息
  14. * @param allProps
  15. * @return
  16. */
  17. private static Properties getEnvProperties(Properties allProps) {
  18. final String PREFIX = ENV_PREFIX;
  19. Properties contextProperties = null;
  20. for (Entry<Object, Object> entry : allProps.entrySet()) {
  21. String key = (String) entry.getKey();
  22. String value = (String) entry.getValue();
  23. // 只获取前缀`env`
  24. if (key.startsWith(PREFIX)) {
  25. if (contextProperties == null) {
  26. contextProperties = new Properties();
  27. }
  28. // 放入数据
  29. contextProperties.put(key.substring(PREFIX.length()), value);
  30. }
  31. }
  32. return contextProperties;
  33. }
  34. /**
  35. * 设置数据源属性
  36. * @param properties
  37. */
  38. @Override
  39. public void setProperties(Properties properties) {
  40. try {
  41. InitialContext initCtx;
  42. Properties env = getEnvProperties(properties);
  43. if (env == null) {
  44. initCtx = new InitialContext();
  45. } else {
  46. initCtx = new InitialContext(env);
  47. }
  48. if (properties.containsKey(INITIAL_CONTEXT)
  49. && properties.containsKey(DATA_SOURCE)) {
  50. // 如果包含`initial_context`和`data_source`
  51. Context ctx = (Context) initCtx.lookup(properties.getProperty(INITIAL_CONTEXT));
  52. dataSource = (DataSource) ctx.lookup(properties.getProperty(DATA_SOURCE));
  53. } else if (properties.containsKey(DATA_SOURCE)) {
  54. dataSource = (DataSource) initCtx.lookup(properties.getProperty(DATA_SOURCE));
  55. }
  56. } catch (NamingException e) {
  57. throw new DataSourceException("There was an error configuring JndiDataSourceTransactionPool. Cause: " + e, e);
  58. }
  59. }
  60. @Override
  61. public DataSource getDataSource() {
  62. return dataSource;
  63. }
  64. }

PooledDataSource

  1. protected int poolMaximumActiveConnections = 10;
  2. protected int poolMaximumIdleConnections = 5;
  3. protected int poolMaximumCheckoutTime = 20000;
  4. protected int poolTimeToWait = 20000;
  5. protected int poolMaximumLocalBadConnectionTolerance = 3;
  6. protected String poolPingQuery = "NO PING QUERY SET";
  7. protected boolean poolPingEnabled;
  8. protected int poolPingConnectionsNotUsedFor;

PooledDataSourceFactory

  1. public class PooledDataSourceFactory extends UnpooledDataSourceFactory {
  2. public PooledDataSourceFactory() {
  3. this.dataSource = new PooledDataSource();
  4. }
  5. }
  6. // 初始化
  7. public PooledDataSource() {
  8. dataSource = new UnpooledDataSource();
  9. }

UnpooledDataSourceFactory

  1. @Override
  2. public void setProperties(Properties properties) {
  3. Properties driverProperties = new Properties();
  4. //metaDataSource 现在是一个dataSource
  5. MetaObject metaDataSource = SystemMetaObject.forObject(dataSource);
  6. for (Object key : properties.keySet()) {
  7. String propertyName = (String) key;
  8. if (propertyName.startsWith(DRIVER_PROPERTY_PREFIX)) {
  9. // 如果是 driver. 前缀开头
  10. String value = properties.getProperty(propertyName);
  11. driverProperties.setProperty(propertyName.substring(DRIVER_PROPERTY_PREFIX_LENGTH), value);
  12. } else if (metaDataSource.hasSetter(propertyName)) {
  13. String value = (String) properties.get(propertyName);
  14. Object convertedValue = convertValue(metaDataSource, propertyName, value);
  15. // 通过 metaDataSource 来对 dataSource 进行设置属性
  16. metaDataSource.setValue(propertyName, convertedValue);
  17. } else {
  18. throw new DataSourceException("Unknown DataSource property: " + propertyName);
  19. }
  20. }
  21. if (driverProperties.size() > 0) {
  22. metaDataSource.setValue("driverProperties", driverProperties);
  23. }
  24. }

UnpooledDataSource

  • org.apache.ibatis.datasource.unpooled.UnpooledDataSource主要定义数据库连接相关的一些属性,以及与数据库的链接对象创建

    1. // 一些配置信息
    2. private ClassLoader driverClassLoader;
    3. private Properties driverProperties;
    4. private String driver;
    5. private String url;
    6. private String username;
    7. private String password;
    8. private Boolean autoCommit;
    9. private Integer defaultTransactionIsolationLevel;
    10. private Integer defaultNetworkTimeout;
  • 初始化连接对象

    1. /**
    2. * 加载链接驱动 如 mysql 链接驱动
    3. * @throws SQLException
    4. */
    5. private synchronized void initializeDriver() throws SQLException {
    6. if (!registeredDrivers.containsKey(driver)) {
    7. Class<?> driverType;
    8. try {
    9. if (driverClassLoader != null) {
    10. driverType = Class.forName(driver, true, driverClassLoader);
    11. } else {
    12. driverType = Resources.classForName(driver);
    13. }
    14. // DriverManager requires the driver to be loaded via the system ClassLoader.
    15. // http://www.kfu.com/~nsayer/Java/dyn-jdbc.html
    16. Driver driverInstance = (Driver) driverType.getDeclaredConstructor().newInstance();
    17. DriverManager.registerDriver(new DriverProxy(driverInstance));
    18. registeredDrivers.put(driver, driverInstance);
    19. } catch (Exception e) {
    20. throw new SQLException("Error setting driver on UnpooledDataSource. Cause: " + e);
    21. }
    22. }
    23. }
  • 设置连接对象的属性

    1. /**
    2. * 设置连接对象 , 超时时间,是否自动提交事物
    3. * @param conn
    4. * @throws SQLException
    5. */
    6. private void configureConnection(Connection conn) throws SQLException {
    7. if (defaultNetworkTimeout != null) {
    8. conn.setNetworkTimeout(Executors.newSingleThreadExecutor(), defaultNetworkTimeout);
    9. }
    10. if (autoCommit != null && autoCommit != conn.getAutoCommit()) {
    11. conn.setAutoCommit(autoCommit);
    12. }
    13. if (defaultTransactionIsolationLevel != null) {
    14. conn.setTransactionIsolation(defaultTransactionIsolationLevel);
    15. }
    16. }
  • 获取连接对象

    1. /**
    2. * 获取链接对象
    3. * @param username
    4. * @param password
    5. * @return
    6. * @throws SQLException
    7. */
    8. private Connection doGetConnection(String username, String password) throws SQLException {
    9. Properties props = new Properties();
    10. if (driverProperties != null) {
    11. props.putAll(driverProperties);
    12. }
    13. if (username != null) {
    14. props.setProperty("user", username);
    15. }
    16. if (password != null) {
    17. props.setProperty("password", password);
    18. }
    19. return doGetConnection(props);
    20. }

解析流程

  • 在 xml 解析的过程中会执行DataSourceFactory相关内容
  1. /**
  2. * 解析 dataSourceElement 标签
  3. * <dataSource type="POOLED">
  4. * <property name="driver" value="com.mysql.jdbc.Driver"/>
  5. * <property name="url" value="jdbc:mysql://localhost:3306/mybatis"/>
  6. * <property name="username" value="root"/>
  7. * <property name="password" value="root"/>
  8. * </dataSource>
  9. *
  10. * @param context
  11. * @return
  12. * @throws Exception
  13. */
  14. private DataSourceFactory dataSourceElement(XNode context) throws Exception {
  15. if (context != null) {
  16. String type = context.getStringAttribute("type");
  17. Properties props = context.getChildrenAsProperties();
  18. //org.apache.ibatis.session.Configuration.Configuration()
  19. DataSourceFactory factory = (DataSourceFactory) resolveClass(type).getDeclaredConstructor().newInstance();
  20. // PooledDataSourceFactory -> UnpooledDataSourceFactory
  21. factory.setProperties(props);
  22. return factory;
  23. }
  24. throw new BuilderException("Environment declaration requires a DataSourceFactory.");
  25. }

从类图上或者代码中我们可以发现PooledDataSourceFactory是继承UnpooledDataSourceFactory那么方法应该也是UnpooledDataSourceFactory的。看看设置属性方法

image-20191223083610214

方法直接走完

image-20191223083732972