Mapper映射器的配置
就是 標籤了,用來進行 sql 文件映射。也就是說我們需要告訴 MyBatis 到哪裏去找到這些語句。 Java 在自動查找這方面沒有提供一個很好的方法,所以最佳的方式是告訴 MyBatis 到哪裏去找映射文件。
<!-- 使用相對於類路徑的資源引用 -->
<mappers>
<mapper resource="org/mybatis/builder/AuthorMapper.xml"/>
<mapper resource="org/mybatis/builder/BlogMapper.xml"/>
<mapper resource="org/mybatis/builder/PostMapper.xml"/>
</mappers>
<!-- 使用完全限定資源定位符(URL) -->
<mappers>
<mapper url="file:///var/mappers/AuthorMapper.xml"/>
<mapper url="file:///var/mappers/BlogMapper.xml"/>
<mapper url="file:///var/mappers/PostMapper.xml"/>
</mappers>
<!-- 使用映射器接口實現類的完全限定類名 -->
<mappers>
<mapper class="org.mybatis.builder.AuthorMapper"/>
<mapper class="org.mybatis.builder.BlogMapper"/>
<mapper class="org.mybatis.builder.PostMapper"/>
</mappers>
<!-- 將包內的映射器接口實現全部註冊爲映射器 -->
<mappers>
<package name="org.mybatis.builder"/>
</mappers>
private void mapperElement(XNode parent) throws Exception {
if (parent != null) {
for (XNode child : parent.getChildren()) {
if ("package".equals(child.getName())) {
String mapperPackage = child.getStringAttribute("name");
configuration.addMappers(mapperPackage);
} else {
String resource = child.getStringAttribute("resource");
String url = child.getStringAttribute("url");
String mapperClass = child.getStringAttribute("class");
if (resource != null && url == null && mapperClass == null) {
ErrorContext.instance().resource(resource);
InputStream inputStream = Resources.getResourceAsStream(resource);
XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
mapperParser.parse();
} else if (resource == null && url != null && mapperClass == null) {
ErrorContext.instance().resource(url);
InputStream inputStream = Resources.getUrlAsStream(url);
XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, url, configuration.getSqlFragments());
mapperParser.parse();
} else if (resource == null && url == null && mapperClass != null) {
Class<?> mapperInterface = Resources.classForName(mapperClass);
configuration.addMapper(mapperInterface);
} else {
throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one.");
}
}
}
}
}
顯然我們從源碼裏面發現了,這四種映射文件獲取的方式顯然是有優先級的,package方式最爲優先
Element : mappers
Content Model : (mapper*, package*)
根據dtd約束: mapper子標籤必須在package標籤前面。實際應用中,package標籤使用的比較少,這裏就不貼源碼對package進行分析了(需要注意的是,如果兩個子標籤同時存在,前面解析完mapper標籤後,存在相同的接口名,會拋出異常)
-
第一個 if 表示 resource 值不爲 null,且url 值和 mapperClass 值都爲null。
-
第二個else if 表示 url 值不爲 null,且 resource 值和 mapperClass 值都爲null。
-
第三個 else if 表示 mapperClass 值不爲 null,且 resource 值和 url 值都爲null。
-
第四個 else 表示如果三個都爲null或者都不爲null,或者有兩個不爲null,都會拋出異常。
數據源的配置
實質上,Mybatis-config.xml這個主配置文件就代表我們如何連接數據庫源,那麼哪個負責讀取xml就是哪個源碼負責連接數據庫源
顯然這個方法是讀取environments標籤用的
private void environmentsElement(XNode context) throws Exception {
//如果<environments>標籤不爲null
if (context != null) {
//如果 environment 值爲 null
if (environment == null) {
//獲取<environments default="屬性值">中的default屬性值
environment = context.getStringAttribute("default");
}
//遍歷<environments />標籤中的子標籤<environment />
for (XNode child : context.getChildren()) {
//獲取<environment id="屬性值">中的id屬性值
String id = child.getStringAttribute("id");
//遍歷所有<environment>的時候一次判斷相應的id是否是default設置的值
if (isSpecifiedEnvironment(id)) {
//獲取配置的事務管理器
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());
}
}
}
}
private boolean isSpecifiedEnvironment(String id) {
if (environment == null) {
throw new BuilderException("No environment specified.");
} else if (id == null) {
throw new BuilderException("Environment requires an id attribute.");
} else if (environment.equals(id)) {
return true;
}
return false;
}
-
第 3 行代碼:if (context != null) 也就是說我們可以不在 mybatis-configuration.xml 文件中配置標籤,這是爲了和spring整合時,在spring容器中進行配置。
-
第 5 行——第 8 行代碼:獲取中的default屬性值,注意第 5 行 首先判斷 environment == null 。因爲我們可以配置多個環境,也就是連接多個數據庫。
Mybatis內部的數據源包含在這些jar包裏
public interface DataSourceFactory {
void setProperties(Properties props);
DataSource getDataSource();
}
這就是數據源工廠的接口,幾種不同的數據源工廠都實現了它
1 public class JndiDataSourceFactory implements DataSourceFactory
2 public class UnpooledDataSourceFactory implements DataSourceFactory
3 public class PooledDataSourceFactory extends UnpooledDataSourceFactory
而在mybatis源碼內也是需要通過configxml文件來生產相應的數據源工廠的其實就是我們配置的type屬性
private DataSourceFactory dataSourceElement(XNode context) throws Exception {
if (context != null) {
String type = context.getStringAttribute("type");
Properties props = context.getChildrenAsProperties();
DataSourceFactory factory = (DataSourceFactory) resolveClass(type).newInstance();
factory.setProperties(props);
return factory;
}
throw new BuilderException("Environment declaration requires a DataSourceFactory.");
}
那麼我們看看unpooled的數據源是怎麼獲取連接的
public Connection getConnection() throws SQLException {
return doGetConnection(username, password);
}
public Connection getConnection(String username, String password) throws SQLException {
return doGetConnection(username, password);
}
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 {
//1、初始化驅動
initializeDriver();
//2、從DriverManager中獲取連接,獲取新的Connection對象
Connection connection = DriverManager.getConnection(url, properties);
//3、配置connection屬性
configureConnection(connection);
return connection;
}
顯然其實實際上還是和我們用的時候一樣,只是分了多個重載的方法去傳參而已,本質還是一樣
- 加載數據庫驅動
- 通過url 用戶密碼獲取connection
- 配置connection設置是否自動提交以及隔離級別