- 文章内容主要是个人学习梳理,大佬略过
- 参考mybatis – MyBatis 3 | 简介
1、基本简介
上图是mybatis的整体架构,对比JDBC来看
public static void main(String[] args) throws Exception {
//1 - 注册驱动
Class.forName("com.mysql.jdbc.Driver");
//2 - 获取数据库连接
Connection con = DriverManager.getConnection("jdbc:mysql://localhost:3306/springdemo", "root", "123");
//3 - 获取语句执行平台
PreparedStatement statement =
con.prepareStatement("select * from employee where id = ?");
//4 - 设置参数
statement.setString(1,"8");//给?添加参数
//5 - 执行,并返回结果
ResultSet resultSet = statement.executeQuery();
//6 - 处理结果
while (resultSet.next()) {
System.out.println(resultSet.getString("id"));
}
//7 - 关闭执行平台与数据库连接
statement.close();
con.close();
}
- 框架主要优化了以下方面的事情
- 为sql设置参数
- 处理数据库返回的结果,简化了处理过程
- 数据库的连接由框架进行管理
- 提供了更强大的动态sql能力
接下来根据下面的Demo来解析框架的大体流程
阅读框架要抓大放小,梳理清楚整体脉络即可,框架的大部分代码都是针对细节进行处理
public static void main(String[] args) throws Exception {
//加载配置文件
SqlSessionFactory sqlSessionFactory =
new SqlSessionFactoryBuilder().build(Resources.getResourceAsReader("MapperConfig.xml"));
//创建sqlSession
SqlSession sqlSession = sqlSessionFactory.openSession();
List<StudentPOJO> studentPOJOS = null;
//1.通过动态代理对象执行
Demo2 demo2 = sqlSession.getMapper(Demo2.class);
studentPOJOS = demo2.selectAll(new HashMap());
//2.根据statement执行
studentPOJOS = sqlSession.selectList("org.apache.ibatis.zDemo.Demo2.selectAllByStu", new Object());
System.out.println(JSONObject.toJSONString(studentPOJOS));
System.out.println("end");
}
其他准备工作
- 配置文件
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<properties>
<property name="lizhi" value="lizhi"/>
</properties>
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
<setting name="localCacheScope" value="STATEMENT"/>
</settings>
<plugins>
<plugin interceptor="org.apache.ibatis.zDemo.ExamplePlugin"></plugin>
</plugins>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="org.apache.ibatis.zDemo.myDataSource">
<property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://********:22206/study_demo"/>
<property name="username" value="root"/>
<property name="password" value="88888"/>
<property name="initialSize" value="10"/>
<property name="maxActive" value="20"/>
</dataSource>
</environment>
</environments>
<mappers>
<!-- <package name="org.apache.ibatis.zDemo"/>-->
<mapper resource="mapper/DemoMapper.xml"/>
<mapper resource="mapper/DemoMapper2.xml"/>
</mappers>
</configuration>
2、加载配置文件
-
首先框架也是读取读取xml配置文件
-
读取configuration标签中的内容
private void parseConfiguration(XNode root) {
try {
//1.处理properties标签
propertiesElement(root.evalNode("properties"));
//2.加载settings标签(没有其他处理)
Properties settings = settingsAsProperties(root.evalNode("settings"));
//2.1.处理vfs实现类
loadCustomVfs(settings);
//2.2.处理log实现类
loadCustomLogImpl(settings);
//2.3 处理类型别名
typeAliasesElement(root.evalNode("typeAliases"));
//3.处理插件
pluginElement(root.evalNode("plugins"));
//4.是否指定了三个工厂类的实现
objectFactoryElement(root.evalNode("objectFactory"));//用来创建对象
objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));//创建对象映射
reflectorFactoryElement(root.evalNode("reflectorFactory"));
//5.处理其他setting属性
settingsElement(settings);
//6.处理环境信息(处理数据源,事务管理)
environmentsElement(root.evalNode("environments"));
//7.
databaseIdProviderElement(root.evalNode("databaseIdProvider"));
//8.处理类型处理器
typeHandlerElement(root.evalNode("typeHandlers"));
//9.处理映射文件mapper
mapperElement(root.evalNode("mappers"));
} catch (Exception e) {
throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
}
}
处理properties标签
- 主要是将properties属性读取,添加到配置文件对象中
- 三种配置方式
<properties resource="db.properties"></properties>
<properties url="http://www.baidu.com/db.properties"></properties>
<properties>
<property name="lizhi" value="lizhi"/>
</properties>
处理settings标签
- 示例
<settings>
<setting name="cacheEnabled" value="false"/>
<setting name="logImpl" value="STDOUT_LOGGING"/>
<setting name="localCacheScope" value="STATEMENT"/>
</settings>
- 读取setting标签内容
- 通过反射获取Configuration.class类的set方法,对比标签内容是否正确
- 缓存属性值
处理vfs实现类
- 基本没用过
处理日志实现类
- 从setting中找到logImpl属性值
- 找到对应的calss类
- 赋值给configuration
处理typeAliases标签
- 为类名指定别名
<!--为类名指定别名-->
<typeAliases>
<typeAlias alias="Author" type="domain.blog.Author"/>
<typeAlias alias="Blog" type="domain.blog.Blog"/>
<typeAlias alias="Comment" type="domain.blog.Comment"/>
<typeAlias alias="Post" type="domain.blog.Post"/>
<typeAlias alias="Section" type="domain.blog.Section"/>
<typeAlias alias="Tag" type="domain.blog.Tag"/>
</typeAliases>
<!--为包名指定别名-->
<typeAliases>
<package name="domain.blog"/>
</typeAliases>
- 将包名或类型的别名与全程名称注册到TypeAliasRegistry对象中
处理插件
<plugins>
<plugin interceptor="org.mybatis.example.ExamplePlugin">
<property name="someProperty" value="100"/>
</plugin>
</plugins>
- plugins标签标注的插件,加入configuration的插件链中
三个工厂实现类
objectFactoryElement(root.evalNode("objectFactory"));//用来创建对象
objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));//创建对象映射
reflectorFactoryElement(root.evalNode("reflectorFactory"));
设置configuration的默认值
- 优先使用xml中设置的属性值
- 如果没有设置,则使用默认值
configuration.setAutoMappingBehavior(AutoMappingBehavior.valueOf(props.getProperty("autoMappingBehavior", "PARTIAL")));
configuration.setAutoMappingUnknownColumnBehavior(AutoMappingUnknownColumnBehavior.valueOf(props.getProperty("autoMappingUnknownColumnBehavior", "NONE")));
configuration.setCacheEnabled(booleanValueOf(props.getProperty("cacheEnabled"), true));
configuration.setProxyFactory((ProxyFactory) createInstance(props.getProperty("proxyFactory")));
configuration.setLazyLoadingEnabled(booleanValueOf(props.getProperty("lazyLoadingEnabled"), false));
configuration.setAggressiveLazyLoading(booleanValueOf(props.getProperty("aggressiveLazyLoading"), false));
configuration.setMultipleResultSetsEnabled(booleanValueOf(props.getProperty("multipleResultSetsEnabled"), true));
configuration.setUseColumnLabel(booleanValueOf(props.getProperty("useColumnLabel"), true));
configuration.setUseGeneratedKeys(booleanValueOf(props.getProperty("useGeneratedKeys"), false));
configuration.setDefaultExecutorType(ExecutorType.valueOf(props.getProperty("defaultExecutorType", "SIMPLE")));
configuration.setDefaultStatementTimeout(integerValueOf(props.getProperty("defaultStatementTimeout"), null));
configuration.setDefaultFetchSize(integerValueOf(props.getProperty("defaultFetchSize"), null));
configuration.setDefaultResultSetType(resolveResultSetType(props.getProperty("defaultResultSetType")));
configuration.setMapUnderscoreToCamelCase(booleanValueOf(props.getProperty("mapUnderscoreToCamelCase"), false));
configuration.setSafeRowBoundsEnabled(booleanValueOf(props.getProperty("safeRowBoundsEnabled"), false));
configuration.setLocalCacheScope(LocalCacheScope.valueOf(props.getProperty("localCacheScope", "SESSION")));
configuration.setJdbcTypeForNull(JdbcType.valueOf(props.getProperty("jdbcTypeForNull", "OTHER")));
configuration.setLazyLoadTriggerMethods(stringSetValueOf(props.getProperty("lazyLoadTriggerMethods"), "equals,clone,hashCode,toString"));
configuration.setSafeResultHandlerEnabled(booleanValueOf(props.getProperty("safeResultHandlerEnabled"), true));
configuration.setDefaultScriptingLanguage(resolveClass(props.getProperty("defaultScriptingLanguage")));
configuration.setDefaultEnumTypeHandler(resolveClass(props.getProperty("defaultEnumTypeHandler")));
configuration.setCallSettersOnNulls(booleanValueOf(props.getProperty("callSettersOnNulls"), false));
configuration.setUseActualParamName(booleanValueOf(props.getProperty("useActualParamName"), true));
configuration.setReturnInstanceForEmptyRow(booleanValueOf(props.getProperty("returnInstanceForEmptyRow"), false));
configuration.setLogPrefix(props.getProperty("logPrefix"));
configuration.setConfigurationFactory(resolveClass(props.getProperty("configurationFactory")));
configuration.setShrinkWhitespacesInSql(booleanValueOf(props.getProperty("shrinkWhitespacesInSql"), false));
configuration.setDefaultSqlProviderType(resolveClass(props.getProperty("defaultSqlProviderType")));
处理环境信息
private void environmentsElement(XNode context) throws Exception {
if (context != null) {
if (environment == null) {
environment = context.getStringAttribute("default");
}
for (XNode child : context.getChildren()) {
String id = child.getStringAttribute("id");
//只激活默认的配置环境
if (isSpecifiedEnvironment(id)) {
//创建事务工厂,JdbcTransactionFactory
TransactionFactory txFactory = transactionManagerElement(child.evalNode("transactionManager"));
//创建数据源工厂,默认UnpooledDataSourceFactory
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());
break;
}
}
}
}
这里跟着配置文件和配置详解来看
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="org.apache.ibatis.zDemo.myDataSource">
<property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://********:22206/study_demo"/>
<property name="username" value="root"/>
<property name="password" value="88888"/>
<property name="initialSize" value="10"/>
<property name="maxActive" value="20"/>
</dataSource>
</environment>
</environments>
1. default - 指定使用哪个环境信息
2. transactionManager - 事务的管理方式
1. type
1. JDBC - 这个配置直接使用了 JDBC 的提交和回滚功能,它依赖从数据源获得的连接来管理事务作用
2. MANAGED - 它从不提交或回滚一个连接,而是让容器来管理事务的整个生命周期
3. dataSource - 数据库连接相关信息
1. type
1. UNPOOLED - 直连数据库
1. POOLED - 使用数据库连接池
1. JNDI -
- 找到环境信息列表environments,标签内会指定默认的环境
- 为默认的环境信息创建TransactionFactory, DataSourceFactory
- 初始化TransactionFactory对象
- 初始化DataSourceFactory
- 将数据库各种信息写入对象中
- DataSourceFactory中获取DataSource
- 创建Environment对象,持有DataSourceFactory,DataSource
- 将环境信息加入configuration中
环境信息对象详解
#### 事务相关对象
- TransactionFactory - 事务管理器工厂
| 实现类 | 解释 |
|---|---|
| JdbcTransactionFactory | 直接使用了 JDBC 的提交和回滚功能,它依赖从数据源获得的连接来管理事务作用 |
| ManagedTransactionFactory | 它从不提交或回滚一个连接,而是让容器来管理事务的整个生命周期 |
- Transaction - 由TransactionFactory事务工厂产生
| 实现类 | 解释 |
|---|---|
| JdbcTransaction | 事务对象 - 包含数据源,隔离级别,事务是否自动提交 |
| ManagedTransaction |
数据库连接相关对象
- DataSourceFactory - 数据源接口
| 实现类 | 解释 | 其他 |
|---|---|---|
| JndiDataSourceFactory | ||
| UnpooledDataSourceFactory | 直连数据库 | |
| PooledDataSourceFactory | 使用连接池技术 | 继承UnpooledDataSourceFactory |
| 自定义数据源工厂 | 使用德鲁伊数据库连接池 | 继承PooledDataSourceFactory,构造函数new DruidDataSource数据源赋值给dataSource |
- DataSource - DataSourceFactory 数据源工厂产生
| 实现类 | 解释 | 其他 |
|---|---|---|
| PooledDataSource | 连接池数据源 | |
| UnpooledDataSource | 非连接池数据源 | |
| DruidDataSource | 德鲁伊数据源 |
处理数据库厂商标识
- 解析所有
databaseIdProvider节点的属性,然后通过上面配置的数据源,创建一个数据库连接来获取数据库产品的名称(使用完就关掉),然后于节点的name属性匹配,将value值设置到configuration中
类型处理器
- 主要是为后面的sql出参入参的类型处理使用
<typeHandlers>
<typeHandler handler="org.mybatis.example.ExampleTypeHandler"/>
</typeHandlers>
<typeHandlers>
<package name="org.mybatis.example"/>
</typeHandlers>
- 将配置的类型处理器加入到TypeHandlerRegistry中
下一章主要介绍重要的mapper映射文件的处理过程