1.常用核心原理
1.1ORM框架:
-
也就是把表字段映射为对象的属性;包含hibreate功能,可以直接写sql语句,进行表到字段的映射;
1.2ORM框架之前:
- 1、导入JDBC依赖包
- 2、通过DriverManager注册驱动
- 3、创建连接
- 4、创建Statement
- 5、执行CRUD
- 6、操作结果集(需要手动设置到对象)
- 7、关闭连接
//jdbc使用预编译语句
String sqlString ="insert into user(id, password, name, email, address) values(?, ?, ?, ?, ?)";
PreparedStatement pstmt = connection.PreparedStatement(sqlString);
pstmt.setString(1, user.id);
pstmt.setString(2, user.password);
pstmt.setString(3, user.name);
pstmt.setString(4, user.email);
pstmt.setString(5, user.address);
1.3接口+注解 / xml文件:
- sql解析 / 解析抽象语法树都是一堆if/else判断解析出一个个节点 或者 使用监听者模式利用回调来进行实现
1.4架构设计
- 配置中心(SqlSessionFactoryBuilder包含多种注册中心进行缓存)
- SqlSession(包含执行器)
- 多种执行器(获取代理类执行invoke方法)
- 字段映射(ParameterHandler)
- 结果集映射(ResultSetHandler)
1.5读源码
- 架构设计-----打开官网找入口点
//1.从配置文件(通常是xml配置文件中)得到sessionFactory。
String resources = "mybatis.xml";
//获取InputStreamReadIo
//mybatis对我们操作流做了一个封装,对用户来说比较简单使用
Reader reader = Resources.getResourceAsReader(resources);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
try (SqlSession session = sqlSessionFactory.openSession()) {
BlogMapper mapper = session.getMapper(BlogMapper.class);
Blog blog = mapper.selectBlog(101);
}
2.Mapper
2.1注册Mapper
- 原本使用hashmap+单线程注册(缓存起来)
- 改进:针对大数据量可以改为使用ConcurrentHashMap(尽量避免扩容带来额外开销)+多线程并发注册
2.2解析XML
- XML解析(DOM4j)---使用Xpath查找节点(生成XNode节点对应标签)
- @一种是基于DOM解析为树形结构对象(比如Xpath解析),加载整个结构到内存,比如JDOM,DOM4j;
- @一种是基于SAX的事件驱动解析(比如Digester解析),加载部分结构比较节省内存;
- 将mapper.class接口 和 对应的代理类进行映射,注册到mapperRegistry
- 将解析出的所有xml信息,将接口的一个method 和 具体对应的元信息进行绑定形成MappedStatement,并进行注册到mappedStatements
- mappedStatements和mapperRegistry都存在Configuration类里,所有的缓存配置都在此配置中心;
3.执行流程
3.1Mapper接口实例生成以及执行
- 从sqlSession.getMapper获取mapper接口类,是通过MapperProxyFactory创建出动态代理的实例,
- invoke方法会先从缓存中获取方法,然后会进行执行,
- 通过sqlsession进行执行,sqlsession包含executor,所以实际还是由执行器executor进行执行;
ps:实际执行之前,先要判断一级缓存才会进行实际的query(该缓存需要参数、sql语句等全部一致才可以),在进行更新的时候会清除所有缓存,防止脏数据,二级缓存需要进行设置才可以使用;
3.2核心执行器
- Executor(接口)---BaseExecutor(抽象类)----实现类
- 模板方法(模式)--接口定义方法,抽象类将不变的写成算法实现、将变化的写成抽象方法,最后扔给实现类进行实现
- 装饰者(模式)----针对原有exector的方法进行装饰
三种执行器:
- ExecutorType.SIMPLE 它只为每个语句执行的时候都会创建一个PreparedStatement(默认的执行器)。
- ExecutorType.REUSE 只对预编译好的sql语句进行缓存,参数值可以不同的,可以重复使用PreparedStatements。
- ExecutorType.BATCH 可以批量进行更新,但不支持select。
- CachingExecutor 利用了二级缓存,mapper表级别的缓存,使用cacheEnabled配置,会先判断二级缓存然后才会判断一级缓存。
3.3执行器执行
- 优先需要判断一级缓存,如果没有才会执行真正的查询
- 编译sql语句,设置参数值
- 进行实际的查询并且返回结果
- ResultSetHandler进行结果集映射
- 完成之后,逐层关闭i/o流直到回溯到执行器Executor(关闭resultSet--statement--connection)
- 扩展:一级缓存;
- 在同个SqlSession中,查询语句相同的sql会被缓存,但是一旦执行新增或更新或删除操作,缓存就会被清除;
- 只需要在一个事务中,不需要commit,两次相同的查询实际只会查一次数据库;
- 扩展:二级缓存;
- SqlSession commit或close之后,二级缓存才会生效;
- 因为在commit的时候会从entriesToAddOnCommit这个map结构获取元素然后放入实际的缓存map中,核心就是先用了一个map进行了一个临时的存储;
4.插件原理
4.1责任链模式
- 两种形式:一种带拦截,一种不带拦截(Mybatis)
4.2拦截器原理
- 原理介绍:
- 责任链模式---通过拦截器对StatementHandler、ParameterHandler和ResultSetHandler这三种类型的handel处理器进行拦截来实现不同功能;
- 调用体系:
- Configuration#newParameterHandler/newResultSetHandler/newStatementHandler
- InterceptorChain#pluginAll(使用了责任链模式增强)
- Interceptor(接口)#plugin 生成对应handler处理器的代理类;
- 通过对原handler接口的方法通过动态代理进行增强;
- 扩展:可以不使用原生提供的动态代理去做,可以直接在plugin 方法里通过类型判断进行实现;
- 扩展:分页机制如何实现;
- 实现Interceptor(接口),核心就是对sql进行改写(https://blog.csdn.net/feinifi/article/details/88769101);
- 查出来全部数据,只返回部分数据即可;
- 直接头通过limit进行控制;
- 扩展:超大分页如何优化;
- 可以使用覆盖索引优化;
- 如果id是连续的,可以直接索引id大于1w的;
- 可以使用redis存储每页的数据;
5.Mybatis与Spring整合
5.1整合
- 扫描所有mapper接口,在xxxServiceImpl里进行依赖注入@Autowired
- 将dataSource、configLocation、mapperLocations(扫描所有xml文件)配置到sqlSessionFactory中
- 最终,配置transactionManager以及dataSource以便被其他bean注入
5.2SpringBean实例创建过程
- AbstractApplicationContext的refresh方法代表了bean的生命周期(从注册到实例化)
- 进行实例化
- 初始化实例:xxxAware进行设置属性properties---初始化之前的后置处理器---初始化bean(InitializingBean的
afterPropertiesSet方法)---用户定义的初始化方法---初始化之后的后置处理器 - 将初始化好的单实例会缓存起来 :如果要获取FactoryBean实例就要在beanname前加&,如果获取FactoryBean创建出来的对象不要加&即可
- 生命结束先执行DisposableBean的destory接口,在执行用户自定义的destory-method方法
- 至此整个生命周期结束
5.3Spring的类型转换器
- TypeConverter接口----TypeConverterSupper进行实现--(装饰者模式)--委派给TypeConverterDelegate进行实现---先使用PropertyEditor,如果没有可以使用ConversionService(可以自定义converter可扩展性强)进行实现
- 要增加一个类型转换器就可以增加一个自定义的converter
5.4获取mapper接口的两种方式
- SpringBoot注解配置:使用@MapperScan可以指定要扫描的Mapper类的包的路径,底层通过MapperScannerRegistrar类进行实现ImportBeanDefinitionRegistrar接口注册BD;
- 先注册beanDefinitions,在进行设置beanDefinitions,
- xml配置:配置MapperScannerConfigurer类即可,底层通过ClassPathBeanDefinitionScanner类实现,
- 先设置beanDefinitions,在进行注册beanDefinitions