一,orm介绍
o <===> object r <===> relational m <===> mapping
MyBatis:是一个持久层框架,使用简单,学习成本较低。可以执行自己手写的sql语句,比较灵活,但是mybatis的自动程度画不高,移植性也不高,从一个数据库迁移到另外一个数据库的时候需要修改配置,所以被称为半自动化ORM框架
Hibernate:底层实现了很多add方式,所以被称为全自动ORM框架
2,JDBC
2.1四大核心对象
1,加载驱动
Class.forName("com.mysql.jdbc.Driver")
2,创建连接
COnnection con = DriverManager.getConnection(...)
3,获取sql执行者,执行操作
con.prepareStament(sql)
4,执行查询
execute();
5,关闭数据库,关闭连接
.close()
2.1 JDBC出现一些的问题
1,数据库配置,sql语句在代码中出现硬编码的问题,mybatis通过xml解决
2,jdbc频繁的创建和关闭数据库链接,对数据库是一个很大的消耗,mybatis通过数据库连接池解决。
3,参数设置不方便,mybatis 通过动态sql查询
4,处理结果集不方便,mybatis 可以自定义 resultMap 进行映射
三,mybatis功能架构
API接口层:提供给外部使用的接口api,可以通过这些api来操纵数据库
数据处理层:负责具体的sql查找、解析、执行和映射结果处理等
基础支撑层:负责最基础功能的支撑,如一些连接管理,事务管理,配置加载,缓存等。
可以通过这份笔记来对这个mybatis有一个基本的了解:https://note.youdao.com/ynoteshare/index.html?id=5d41fd41d970f1af9185ea2ec0647b64&type=notebook&_time=1662564167792
四,mybatis源码解析
1,构造对象流程
String resource = "mybatis-config.xml"; Reader reader; //将XML配置文件构建为Configuration配置类 reader = Resources.getResourceAsReader(resource); // 通过加载配置文件流构建一个SqlSessionFactory DefaultSqlSessionFactory SqlSessionFactory sqlMapper = new SqlSessionFactoryBuilder().build(reader); // 数据源 执行器 DefaultSqlSession SqlSession session = sqlMapper.openSession(); //解析配置类 XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties); //其底层就是专门用来解析xml的,xml里面的任何一个配置文件,都是通过node存储的 //可以通过node来获取里面的属性和里面的值,可以获取配置文件下面的所有的值 Node node = (Node)new XPathParser(); //调用parse方法解析 这个全局配置文件 parser.parse() //解析mybatis-config.xml的node节点 parseConfiguration(parser.evalNode("/configuration"))
2,加载流程源码分析
1,先查看这个SqlSessionFactoryBuilder看看底层的具体流程,其主要就是加载配置文件,将这些文件加载一个configuration的一个对象里面。
SqlSessionFactory sqlMapper = new SqlSessionFactoryBuilder().build(reader);
2,这个Configuration会包含很多东西,如一些环境,缓存等等等
public class Configuration { //环境 protected Environment environment; protected boolean safeRowBoundsEnabled; protected boolean safeResultHandlerEnabled = true; protected boolean mapUnderscoreToCamelCase; protected boolean aggressiveLazyLoading; protected boolean multipleResultSetsEnabled = true; protected boolean useGeneratedKeys; protected boolean useColumnLabel = true; //缓存 protected boolean cacheEnabled = true; protected boolean callSettersOnNulls; protected boolean useActualParamName = true; protected boolean returnInstanceForEmptyRow; ... }
3,其build方法如下,主要是通过这个责任链模式,单参调双参,双参调三参。
public SqlSessionFactory build(Reader reader, String environment, Properties properties) { try { //获取配置文件 XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties); //解析配置文件 return build(parser.parse()); }finally{ reader.close(); } }
在这个解析配置文件的方法里面,会有一个parseConfiguration的类
public Configuration parse() { parseConfiguration(parser.evalNode("/configuration")); return configuration; }
接下来主要查看这个parseConfiguration类,就是对应xml里面的configuration标签里面的一些标签
private void parseConfiguration(XNode root) { //解析properties标签 propertiesElement(root.evalNode("properties")); //解析settings标签 Properties settings = settingsAsProperties(root.evalNode("settings")); //解析environments环境标签 environmentsElement(root.evalNode("environments")); //解析typeAliases别名标签 typeAliasesElement(root.evalNode("typeAliases")); //解析typeHandlers标签 typeHandlerElement(root.evalNode("typeHandlers")); //解析mappers标签 mapperElement(root.evalNode("mappers")); }
4,解析这个配置文件的propertiesElement方法如下,里面的parse解析方法采用的是一个责任链模式
private void propertiesElement(XNode context) throws Exception { if (context != null) { //获取子结点 Properties defaults = context.getChildrenAsProperties(); //获取资源 String resource = context.getStringAttribute("resource"); //最后将这个Properties对象加入到这个configuration的配置文件里面 configuration.setVariables(defaults); } }
其他的标签也是会通过这个责任链模式,对这些标签进行解析,并最终会加入到这个configuration的配置里面
5,一直到最后一个,会去解析这个mapper里面的mappers这个配置文件。会通过这个mapper的这个类,比如说UserMapper,然后找到对应的xml文件。
<mappers> <package name="com.tuling.mapper"/> </mappers>
其源码实现如下,在解析这个config.xml的配置文件之后,会解析这个mapper.xml的映射文件
private void mapperElement(XNode parent) throws Exception { //创建读取XmlMapper构建器对象 XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments()); //开始真正的解析mapper的xml文件 mapperParser.parse(); }
进入这个解析映射文件的方法
public void parse() { //获取mapper映射文件的所有内容,并对映射文件进行解析 configurationElement(parser.evalNode("/mapper")); }
在进入这个configurationElement的这个方法
private void configurationElement(XNode context) { //会先解析mapper标签里面的这个namespace的这个标签 String namespace = context.getStringAttribute("namespace"); //设置到这个build的助手里面 builderAssistant.setCurrentNamespace(namespace); //解析当前的缓存的引用 cacheRefElement(context.evalNode("cache-ref")); //解析这个二级缓存 cacheElement(context.evalNode("cache")); //解析resultMapper文件 resultMapElements(context.evalNodes("/mapper/resultMap")); //解析sql片段 sqlElement(context.evalNodes("/mapper/sql")); //解析数据的增删改查 buildStatementFromContext(context.evalNodes("select|insert|update|delete")); }
6,再查看解析mapper里面的二级缓存,cacheElement方法如下,缓存的过期策略为一个LRU
private void cacheElement(XNode context) { //解析cache节点的type属性 String type = context.getStringAttribute("type", "PERPETUAL"); //根据type的String获取class类型 Class<? extends Cache> typeClass = typeAliasRegistry.resolveAlias(type); //获取缓存过期策略:默认是LRU String eviction = context.getStringAttribute("eviction", "LRU"); Class<? extends Cache> evictionClass = typeAliasRegistry.resolveAlias(eviction); //flushInterval(刷新间隔)属性可以被设置为任意的正整数,设置的值应该是一个以毫秒为单位的合理时间量。 默认情况是不设置,也就是没有刷新间隔,缓存仅仅会在调用语句时刷新。 Long flushInterval = context.getLongAttribute("flushInterval"); //size(引用数目)属性可以被设置为任意正整数,要注意欲缓存对象的大小和运行环境中可用的内存资源。默认值是 1024。 Integer size = context.getIntAttribute("size"); //只读)属性可以被设置为 true 或 false。只读的缓存会给所有调用者返回缓存对象的相同实例。 因此这些对象不能被修改。这就提供了可观的性能提升。而可读写的缓存会(通过序列化)返回缓存对象的拷贝。 速度上会慢一些,但是更安全,因此默认值是 false boolean readWrite = !context.getBooleanAttribute("readOnly", false); boolean blocking = context.getBooleanAttribute("blocking", false); Properties props = context.getChildrenAsProperties(); //把缓存节点加入到Configuration中 builderAssistant.useNewCache(typeClass, evictionClass, flushInterval, size, readWrite, blocking, props); }
7,再查看这个解析数据的增删改查,会对文件里面的增删改查语句进行解析
private void buildStatementFromContext(List<XNode> list, String requiredDatabaseId) { for (XNode context : list) { //解析增删改查结点 final XMLStatementBuilder statementParser = new XMLStatementBuilder(configuration, builderAssistant, context, requiredDatabaseId); try { statementParser.parseStatementNode(); } catch (IncompleteElementException e) { configuration.addIncompleteStatement(statementParser); } } }
五,总结
1,首先会创建一个SqlSessionFactoryBuilder 的对象,用于解析配置文件
2,会创建一个解析xml配置类的一个构造器,会解析xmlconfig里面的全部标签,并保存在configuration里面
3,会创建一个解析xml映射类的一个构造器,专门解析对应mapper.xml里面的全部标签
4,在解析mapper.xml的时候会将里面增删改查的元素全部解析出来,存在一个MapperStatement里面
5,所有的信息解析完之后,都会生成在一个configuration对象,所有的信息都会加入到这个configuration里面
6,最后会创建一个SqlSessionFactory,会将所有的配置信息,都加入到这个SqlSessionFactory里面