MyBatis-04-資料庫配置

YangDanMua發表於2024-04-16

XML 解析時對 environment 節點的處理

TransactionFactory txFactory = transactionManagerElement(child.evalNode("transactionManager"));
DataSourceFactory dsFactory = dataSourceElement(child.evalNode("dataSource"));
DataSource dataSource = dsFactory.getDataSource();
Environment.Builder environmentBuilder = new Environment.Builder(id).transactionFactory(txFactory)
        .dataSource(dataSource);
configuration.setEnvironment(environmentBuilder.build());

transactionManager

型別實際是TransactionFactory
Configuration 預設註冊了兩個可使用別名的

  • JDBC: JdbcTransactionFactory
  • MANAGED: ManagedTransactionFactory

TransactionFactory: 事務/Transaction工廠 ,透過資料庫連線池或連線獲取 Transaction 物件

public interface TransactionFactory {
  default void setProperties(Properties props) {
    // NOP
  }
  Transaction newTransaction(Connection conn);
  Transaction newTransaction(DataSource dataSource, TransactionIsolationLevel level, boolean autoCommit);
}

Transaction:事務,管理事務/連線的提交等

public interface Transaction {
  Connection getConnection() throws SQLException;
  void commit() throws SQLException;
  void rollback() throws SQLException;
  void close() throws SQLException;
  Integer getTimeout() throws SQLException;
}

JdbcTransactionFactory
對應 JdbcTransaction 的工廠,普普通通沒什麼

public class JdbcTransactionFactory implements TransactionFactory {

  private boolean skipSetAutoCommitOnClose = false;

  @Override
  public void setProperties(Properties props) {
    // 處理 Properties 中的 skipSetAutoCommitOnClose 配置的
  }

  @Override
  public Transaction newTransaction(Connection conn) {
    return new JdbcTransaction(conn);
  }

  @Override
  public Transaction newTransaction(DataSource ds, TransactionIsolationLevel level, boolean autoCommit) {
    return new JdbcTransaction(ds, level, autoCommit, skipSetAutoCommitOnClose);
  }
}

ManagedTransactionFactory:ManagedTransaction 的工廠,事務是外部控制的,對於自動提交引數、隔離級別等不會去主動設定

public class ManagedTransactionFactory implements TransactionFactory {

  private boolean closeConnection = true;

  @Override
  public void setProperties(Properties props) {
    // closeConnection 引數
  }

  @Override
  public Transaction newTransaction(Connection conn) {
    return new ManagedTransaction(conn, closeConnection);
  }

  @Override
  public Transaction newTransaction(DataSource ds, TransactionIsolationLevel level, boolean autoCommit) {
	// 忽略自動提交引數	
    return new ManagedTransaction(ds, level, closeConnection);
  }
}

Transaction

Transaction:事務,管理事務/連線的提交等

public interface Transaction {
  Connection getConnection() throws SQLException;
  void commit() throws SQLException;
  void rollback() throws SQLException;
  void close() throws SQLException;
  Integer getTimeout() throws SQLException;
}

預設兩個實現 JdbcTransaction 和 ManagedTransaction,都有其獨立的工廠
JdbcTransaction
主要就是對 Connection 的 commit、rollback、close 進行了一定程度的封裝,支援直接傳入 Connection 和透過 DataSource 獲取 Connection

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;
  protected boolean skipSetAutoCommitOnClose;

  public JdbcTransaction(DataSource ds, TransactionIsolationLevel desiredLevel, boolean desiredAutoCommit) {
    this(ds, desiredLevel, desiredAutoCommit, false);
  }

  public JdbcTransaction(DataSource ds, TransactionIsolationLevel desiredLevel, boolean desiredAutoCommit,
      boolean skipSetAutoCommitOnClose) {
    dataSource = ds;
    level = desiredLevel;
    autoCommit = desiredAutoCommit;
    this.skipSetAutoCommitOnClose = skipSetAutoCommitOnClose;
  }

  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()) {
      connection.commit();
    }
  }

  @Override
  public void rollback() throws SQLException {
    if (connection != null && !connection.getAutoCommit()) {
      connection.rollback();
    }
  }

  @Override
  public void close() throws SQLException {
    if (connection != null) {
      resetAutoCommit();
      connection.close();
    }
  }

  protected void setDesiredAutoCommit(boolean desiredAutoCommit) {
    if (connection.getAutoCommit() != desiredAutoCommit) {
      connection.setAutoCommit(desiredAutoCommit);
    }
  }

  protected void resetAutoCommit() {
    connection.setAutoCommit(true);
  }

  protected void openConnection() throws SQLException {
    connection = dataSource.getConnection();
    if (level != null) {
      connection.setTransactionIsolation(level.getLevel());
    }
    setDesiredAutoCommit(autoCommit);
  }

  @Override
  public Integer getTimeout() throws SQLException {
    return null;
  }
}

ManagedTransaction:相對於 JdbcTransaction 主要是忽略了 commit 和 rollback 呼叫

public class ManagedTransaction implements Transaction {
  public ManagedTransaction(Connection connection, boolean closeConnection) {
    this.connection = connection;
    this.closeConnection = closeConnection;
  }

  public ManagedTransaction(DataSource ds, TransactionIsolationLevel level, boolean closeConnection) {
    this.dataSource = ds;
    this.level = level;
    this.closeConnection = closeConnection;
  }

  @Override
  public Connection getConnection() throws SQLException {
    if (this.connection == null) {
      openConnection();
    }
    return this.connection;
  }

  @Override
  public void commit() throws SQLException {
    // Does nothing
  }

  @Override
  public void rollback() throws SQLException {
    // Does nothing
  }

  @Override
  public void close() throws SQLException {
    if (this.closeConnection && this.connection != null) {
      this.connection.close();
    }
  }

  protected void openConnection() throws SQLException {
    this.connection = this.dataSource.getConnection();
    if (this.level != null) {
      this.connection.setTransactionIsolation(this.level.getLevel());
    }
  }

  @Override
  public Integer getTimeout() throws SQLException {
    return null;
  }
}

DataSource

typeAliasRegistry.registerAlias("JNDI", JndiDataSourceFactory.class);
typeAliasRegistry.registerAlias("POOLED", PooledDataSourceFactory.class);
typeAliasRegistry.registerAlias("UNPOOLED", UnpooledDataSourceFactory.class);

DataSourceFactory: 生產 DataSource

public interface DataSourceFactory {
  void setProperties(Properties props);
  DataSource getDataSource();
}

JNDI: JndiDataSourceFactory
暫略,這種一般需要在容器內配置的檔案,而非類似 SpringBoot 配置檔案配置的

UNPOOLED: UnpooledDataSourceFactory

public class UnpooledDataSourceFactory implements DataSourceFactory {

  private static final String DRIVER_PROPERTY_PREFIX = "driver.";
  private static final int DRIVER_PROPERTY_PREFIX_LENGTH = DRIVER_PROPERTY_PREFIX.length();

  protected DataSource dataSource;

  public UnpooledDataSourceFactory() {
    // DataSource 物件直接建立了
    this.dataSource = new UnpooledDataSource();
  }

  @Override
  public void setProperties(Properties properties) {
    Properties driverProperties = new Properties();
    MetaObject metaDataSource = SystemMetaObject.forObject(dataSource);
    for (Object key : properties.keySet()) {
      String propertyName = (String) key;
	  // 如果有資料庫配置
      if (propertyName.startsWith(DRIVER_PROPERTY_PREFIX)) {
        String value = properties.getProperty(propertyName);
		// Properties 拆分儲存資料庫相關配置
        driverProperties.setProperty(propertyName.substring(DRIVER_PROPERTY_PREFIX_LENGTH), value);
      // 如果是直接配置的屬性值配置
	  } else if (metaDataSource.hasSetter(propertyName)) {
        String value = (String) properties.get(propertyName);
		// 根據 setter 的入參型別轉換值
        Object convertedValue = convertValue(metaDataSource, propertyName, value);
        metaDataSource.setValue(propertyName, convertedValue);
      } else {
        throw new DataSourceException("Unknown DataSource property: " + propertyName);
      }
    }
	// 有資料庫相關配置, 設定給資料來源
    if (driverProperties.size() > 0) {
      metaDataSource.setValue("driverProperties", driverProperties);
    }
  }

  @Override
  public DataSource getDataSource() {
    return dataSource;
  }

  private Object convertValue(MetaObject metaDataSource, String propertyName, String value) {
    Object convertedValue = value;
	// setter 引數型別
    Class<?> targetType = metaDataSource.getSetterType(propertyName);
	// 僅支援 int、long、boolean 及其包裝型別還有 String 型別
    if (targetType == Integer.class || targetType == int.class) {
      convertedValue = Integer.valueOf(value);
    } else if (targetType == Long.class || targetType == long.class) {
      convertedValue = Long.valueOf(value);
    } else if (targetType == Boolean.class || targetType == boolean.class) {
      convertedValue = Boolean.valueOf(value);
    }
    return convertedValue;
  }

}

POOLED: PooledDataSourceFactory,父類是 UnpooledDataSourceFactory,UnpooledDataSourceFactory 的功能基本上是處理資料庫配置的,兩者差別就是 DataSource 型別不一樣

public class PooledDataSourceFactory extends UnpooledDataSourceFactory {

  public PooledDataSourceFactory() {
    this.dataSource = new PooledDataSource();
  }

}

DataSource

UnpooledDataSource: 資料庫連線沒有被池管理,直接 DriverManager 獲取連線

public class UnpooledDataSource implements DataSource {

  private ClassLoader driverClassLoader;
  // 用於配置資料庫的引數資訊
  private Properties driverProperties;
  // SPI 能查詢出來的所有資料庫驅動
  private static final Map<String, Driver> registeredDrivers = new ConcurrentHashMap<>();

  private String driver;
  private String url;
  private String username;
  private String password;

  private Boolean autoCommit;
  private Integer defaultTransactionIsolationLevel;
  private Integer defaultNetworkTimeout;

  // 獲取所有驅動
  static {
    Enumeration<Driver> drivers = DriverManager.getDrivers();
    while (drivers.hasMoreElements()) {
      Driver driver = drivers.nextElement();
      registeredDrivers.put(driver.getClass().getName(), driver);
    }
  }

  public UnpooledDataSource() {
  }

  // 其他建構函式略

  @Override
  public Connection getConnection() throws SQLException {
    return doGetConnection(username, password);
  }

  @Override
  public Connection getConnection(String username, String password) throws SQLException {
    return doGetConnection(username, password);
  }

  @Override
  public void setLoginTimeout(int loginTimeout) {
    DriverManager.setLoginTimeout(loginTimeout);
  }

  @Override
  public int getLoginTimeout() {
    return DriverManager.getLoginTimeout();
  }

  @Override
  public void setLogWriter(PrintWriter logWriter) {
    DriverManager.setLogWriter(logWriter);
  }

  @Override
  public PrintWriter getLogWriter() {
    return DriverManager.getLogWriter();
  }

  // driver、url、username、password、autoCommit、defaultTransactionIsolationLevel
  //  driverProperties、defaultNetworkTimeout、driverClassLoader 的 getter 和 setter

  private Connection doGetConnection(String username, String password) throws SQLException {
  // 新的 Properties, 傳入的名稱、密碼 + 其他預設配置引數組成新的
	Properties props = new Properties();
    if (driverProperties != null) {
      props.putAll(driverProperties);
    }
    if (username != null) {
      props.setProperty("user", username);
    }
    if (password != null) {
      props.setProperty("password", password);
    }
    return doGetConnection(props);
  }

  private Connection doGetConnection(Properties properties) throws SQLException {
    // 初始化驅動
	initializeDriver();
	// 直接獲取資料庫連線
    Connection connection = DriverManager.getConnection(url, properties);
    // 配置連線資訊
	configureConnection(connection);
    return connection;
  }

  private void initializeDriver() throws SQLException {
    try {
	  // 沒有透過 SPI 機制自動發現的才會走到這裡
      MapUtil.computeIfAbsent(registeredDrivers, driver, x -> {
        Class<?> driverType;
        try {
          if (driverClassLoader != null) {
            driverType = Class.forName(x, true, driverClassLoader);
          } else {
            driverType = Resources.classForName(x);
          }
          Driver driverInstance = (Driver) driverType.getDeclaredConstructor().newInstance();
          // 為啥這裡要加上一個代理
		  DriverManager.registerDriver(new DriverProxy(driverInstance));
          return driverInstance;
        } catch (Exception e) {
          throw new RuntimeException("Error setting driver on UnpooledDataSource.", e);
        }
      });
    } catch (RuntimeException re) {
      throw new SQLException("Error setting driver on UnpooledDataSource.", re.getCause());
    }
  }

  private void configureConnection(Connection conn) throws SQLException {
    if (defaultNetworkTimeout != null) {
      conn.setNetworkTimeout(Executors.newSingleThreadExecutor(), defaultNetworkTimeout);
    }
    if (autoCommit != null && autoCommit != conn.getAutoCommit()) {
      conn.setAutoCommit(autoCommit);
    }
    if (defaultTransactionIsolationLevel != null) {
      conn.setTransactionIsolation(defaultTransactionIsolationLevel);
    }
  }

  private static class DriverProxy implements Driver {
    private final Driver driver;

    DriverProxy(Driver d) {
      this.driver = d;
    }

    // 其他略, 都是呼叫 Driver 的

    @Override
    public Logger getParentLogger() {
      return Logger.getLogger(Logger.GLOBAL_LOGGER_NAME);
    }
  }

  @Override
  public <T> T unwrap(Class<T> iface) throws SQLException {
    throw new SQLException(getClass().getName() + " is not a wrapper.");
  }

  @Override
  public boolean isWrapperFor(Class<?> iface) throws SQLException {
    return false;
  }

  @Override
  public Logger getParentLogger() {
    // requires JDK version 1.6
    return Logger.getLogger(Logger.GLOBAL_LOGGER_NAME);
  }

}

PooledDataSource: 使用池管理,自己僅維護池,使用 UnpooledDataSource 來獲取資料庫連線

相關文章