mybais启动 - 969251639/study GitHub Wiki
最简单的一个mybatis的用法如下
public static void main(String[] args) throws IOException {
String resource = "mybatis-config.xml";
//读取mybatis配置
InputStream inputStream = Resources.getResourceAsStream(resource);
//从 XML 中构建 SqlSessionFactory
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
//打开会话
SqlSession session = sqlSessionFactory.openSession();
try {
//通过会话操作sql
List<Blog> blogs = session.selectList("test.Blog.findById", 67);
System.out.println(blogs);
blogs = session.selectList("test.Blog.findById", 2);
System.out.println(blogs);
//提交事务
session.commit();
} finally {
//关闭会话
session.close();
}
}
可以看到mybatis的启动时需要读取mybatis的一个xml配置,一个简单mybatis配置如下:
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!-- 配置环境,制定数据库连接信息 -->
<environments default="development">
<environment id="development">
<transactionManager type="JDBC" />
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://localhost:3306/test?characterEncoding=utf-8" />
<property name="username" value="root" />
<property name="password" value="root" />
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="blog.xml"/>
</mappers>
</configuration>
其中environments节点可以定义多个子节点,用于多环境切换,default表示默认环境
environment下面有一个dataSource,type可以指定如下几种类型
- JNDI
- POOLED
- UNPOOLED
这个xml的载入点在SqlSessionFactoryBuilder类的build方法中
public SqlSessionFactory build(InputStream inputStream) {
return build(inputStream, null, null);
}
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
try {
XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
return build(parser.parse());
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error building SqlSession.", e);
} finally {
ErrorContext.instance().reset();
try {
inputStream.close();
} catch (IOException e) {
// Intentionally ignore. Prefer previous error.
}
}
}
上面最重要的一个方法是XMLConfigBuilder.parse,里面会真正进行对配置的解析处理
public Configuration parse() {
if (parsed) {
throw new BuilderException("Each XMLConfigBuilder can only be used once.");
}
parsed = true;
parseConfiguration(parser.evalNode("/configuration"));
return configuration;
}
private void parseConfiguration(XNode root) {
try {
//issue #117 read properties first
propertiesElement(root.evalNode("properties"));
Properties settings = settingsAsProperties(root.evalNode("settings"));
loadCustomVfs(settings);
typeAliasesElement(root.evalNode("typeAliases"));
pluginElement(root.evalNode("plugins"));
objectFactoryElement(root.evalNode("objectFactory"));
objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
reflectorFactoryElement(root.evalNode("reflectorFactory"));
settingsElement(settings);
// read it after objectFactory and objectWrapperFactory issue #631
environmentsElement(root.evalNode("environments"));
databaseIdProviderElement(root.evalNode("databaseIdProvider"));
typeHandlerElement(root.evalNode("typeHandlers"));
mapperElement(root.evalNode("mappers"));
} catch (Exception e) {
throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
}
}
可以看到处理的根结点就是configuration,下面简单分析下environments的解析
private void environmentsElement(XNode context) throws Exception {
if (context != null) {
if (environment == null) {//environment为空则使用default的配置
environment = context.getStringAttribute("default");
}
for (XNode child : context.getChildren()) {//逐个解析下面的environment节点
String id = child.getStringAttribute("id");
if (isSpecifiedEnvironment(id)) {//根据environment找到对应environment节点,也就是找到对应的环境配置项
TransactionFactory txFactory = transactionManagerElement(child.evalNode("transactionManager"));//解析transactionManager类
DataSourceFactory dsFactory = dataSourceElement(child.evalNode("dataSource"));//解析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;
}
private TransactionFactory transactionManagerElement(XNode context) throws Exception {
if (context != null) {
String type = context.getStringAttribute("type");
Properties props = context.getChildrenAsProperties();
TransactionFactory factory = (TransactionFactory) resolveClass(type).newInstance();
factory.setProperties(props);
return factory;
}
throw new BuilderException("Environment declaration requires a TransactionFactory.");
}
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.");
}
在transactionManagerElement方法和dataSourceElement方法中都可以根据type属性找到相应的实现类,如上面的一个xml配置
<environment id="development">
<transactionManager type="JDBC" />
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://localhost:3306/test?characterEncoding=utf-8" />
<property name="username" value="root" />
<property name="password" value="root" />
</dataSource>
</environment>
transactionManager的type是JDBC
dataSource的type是POOLED
两者共同调用resolveClass方法进行实例化
protected Class<?> resolveClass(String alias) {
if (alias == null) {
return null;
}
try {
return resolveAlias(alias);
} catch (Exception e) {
throw new BuilderException("Error resolving class. Cause: " + e, e);
}
}
protected Class<?> resolveAlias(String alias) {
return typeAliasRegistry.resolveAlias(alias);
}
其中typeAliasRegistry是BaseBuilder类中初始化
public BaseBuilder(Configuration configuration) {
this.configuration = configuration;
this.typeAliasRegistry = this.configuration.getTypeAliasRegistry();
this.typeHandlerRegistry = this.configuration.getTypeHandlerRegistry();
}
所以这里的typeAliasRegistry也就是Configuration类中的typeAliasRegistry
在Configuration的构造方法中会初始化typeAliasRegistry各种映射关系
public class ResultMap {
...
protected final TypeAliasRegistry typeAliasRegistry = new TypeAliasRegistry();
public Configuration(Environment environment) {
this();
this.environment = environment;
}
public Configuration() {
typeAliasRegistry.registerAlias("JDBC", JdbcTransactionFactory.class);
typeAliasRegistry.registerAlias("MANAGED", ManagedTransactionFactory.class);
typeAliasRegistry.registerAlias("JNDI", JndiDataSourceFactory.class);
typeAliasRegistry.registerAlias("POOLED", PooledDataSourceFactory.class);
typeAliasRegistry.registerAlias("UNPOOLED", UnpooledDataSourceFactory.class);
typeAliasRegistry.registerAlias("PERPETUAL", PerpetualCache.class);
typeAliasRegistry.registerAlias("FIFO", FifoCache.class);
typeAliasRegistry.registerAlias("LRU", LruCache.class);
typeAliasRegistry.registerAlias("SOFT", SoftCache.class);
typeAliasRegistry.registerAlias("WEAK", WeakCache.class);
typeAliasRegistry.registerAlias("DB_VENDOR", VendorDatabaseIdProvider.class);
typeAliasRegistry.registerAlias("XML", XMLLanguageDriver.class);
typeAliasRegistry.registerAlias("RAW", RawLanguageDriver.class);
typeAliasRegistry.registerAlias("SLF4J", Slf4jImpl.class);
typeAliasRegistry.registerAlias("COMMONS_LOGGING", JakartaCommonsLoggingImpl.class);
typeAliasRegistry.registerAlias("LOG4J", Log4jImpl.class);
typeAliasRegistry.registerAlias("LOG4J2", Log4j2Impl.class);
typeAliasRegistry.registerAlias("JDK_LOGGING", Jdk14LoggingImpl.class);
typeAliasRegistry.registerAlias("STDOUT_LOGGING", StdOutImpl.class);
typeAliasRegistry.registerAlias("NO_LOGGING", NoLoggingImpl.class);
typeAliasRegistry.registerAlias("CGLIB", CglibProxyFactory.class);
typeAliasRegistry.registerAlias("JAVASSIST", JavassistProxyFactory.class);
languageRegistry.setDefaultDriverClass(XMLLanguageDriver.class);
languageRegistry.register(RawLanguageDriver.class);
}
...
}
所以上面transactionManager的type所对应的实现类是JdbcTransactionFactory,而dataSource的type所对应的实现类是PooledDataSourceFactory,其他的节点解析也都是很类似的。最后解析完得到一个SqlSessionFactory。
如果想扩展mybatis的话也可以通过typeAliasRegistry.registerAlias注册自己的别名和实现类的映射,比如创建自己的数据源或者第三方的数据源(c3p0,druid等),然后在datasource的type属性中填写自己的别名即可,当然还有其他方式扩展,比如通过属性方式注入
<dataSource type="xxx">
加载完配置文件后就可以通过创建会话进行各种sql操作了