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设置是否自动提交以及隔离级别