Mybatis的演变和原理
2021-06-14 20:15:35 13 举报
从JDBC演变到Mybatis,实现自定义Mybatis,同时进行源码分析
作者其他创作
大纲/内容
实体类
StaticTextSqlNode
创建 sqlSession 接⼝及实现类
数据库连接配置存在硬编码问题
连接数据库的配置信息
创建SqlSessionFactory对象
TextSqlNode
SqlSession:CRUD操作
public class DefaultSqlSessionFactory implements SqlSessionFactory { private Configuration configuration; public DefaultSqlSessionFactory(Configuration configuration) { this.configuration = configuration; } @Override public SqlSession openSession() { return new DefaultSqlSession(configuration); }}
1. Sql语句在代码中硬编码,实际应⽤中sql变化的可能较⼤2. 使⽤preparedStatement向占有位符号传参数存在硬编码
import java.io.InputStream;public class Resources { // 根据配置文件的路径,将配置文件加载成字节输入流,存储在内存中 public static InputStream getResourceAsSteam(String path){ InputStream resourceAsStream = Resources.class.getClassLoader().getResourceAsStream(path); return resourceAsStream; }}
/*** @ClassName: JDBCDemo* @Author: 99847* @Description: JDBC连接配置* @Date: 2021/6/14 19:27* @Version: 1.0*/public class JDBCDemo { public static void main(String[] args) { Connection connection = null; PreparedStatement preparedStatement = null; ResultSet resultSet = null; try { // 加载数据库驱动 Class.forName(\"com.mysql.cj.jdbc.Driver\"); // 通过驱动管理类获取数据库链接 connection = DriverManager.getConnection(\"jdbc:mysql://localhost:3306/mybatis?\" + \"characterEncoding = utf-8\
SetSqlNode
创建Resources类:getResourceAsSteam(String path)
SqlNode
生产SqlSession对象:openSession()
配置文件
Mybatis执行sql流程
sql语句的解析过程原理
2. 创建sqlSessionFactory
创建 Executor 接⼝及实现类SimpleExexcutor
IfSqlNode
连接池(Pool)
方便运用反射技术,实现实体类的自动封装
解析配置⽂件
// 连接数据库配置<configuration> <!--数据库配置信息--> <dataSource> <property name=\"driverClass\" value=\"com.mysql.jdbc.Driver\"></property> <property name=\"jdbcUrl\" value=\"jdbc:mysql:///zdy_mybatis\"></property> <property name=\"username\" value=\"root\"></property> <property name=\"password\" value=\"root\"></property> </dataSource> <!--存放mapper.xml的全路径--> <mapper resource=\"UserMapper.xml\"></mapper></configuration>
反射、内省技术
可以实现只调用一次getResources方法
插件原理
MixedSqlNode
自定义框架思路
解析配置
1. 创建SqlSessionFactoryBuilder对象2. 调用sqlSessionFactoryBuilder的build(reader)方法;参数reader是mybatis配置文件输入流3. build方法里面创建XMLConfigBuilder对象,调用parse()方法解析配置文件(里面parseConfiguration()会解析各个节点,包括mapper.xml),封装到Configuration对象中4. 创建DefaultSqlSessionFactory对象,里面有Configuration对象5. SqlSessionFactory对象的openSession()创建一个DefaultSqlSession(对象(默认开启二级缓存),里面也有Configuration对象6. sqlSession对象进行CRUD和事务的操作,底层委托executor执行sql语句
TrimSqlNode
创建SqlSessionFactory
Mybatis相关原理
@Datapublic class MappedStatement { //id标识 private String id; //返回值类型 private String resultType; //参数值类型 private String paramterType; //sql语句 private String sql;}
// 单个表sql语句配置<mapper namespace=\"com.lagou.dao.IUserDao\"> <!--sql的唯一标识:namespace.id来组成 : statementId--> <select id=\"findAll\" resultType=\"com.lagou.pojo.User\" > select * from user </select> <select id=\"findByCondition\" resultType=\"com.lagou.pojo.User\" paramterType=\"com.lagou.pojo.User\"> select * from user where id = #{id} and username = #{username} </select></mapper>
对结果集解析存在硬编码(查询列名)系统不易维护
核心:XMLScriptBuilder通过parseDynamicTags()进行递归节点解析,默认实例化一个MinedSqlNode对象,从而得到SqlSource(里面封装了sqlNode)之后,会放到Configuration中,有了SqlSource,就能拿BoundSql了,BoundSql可以得到最终的sql。
dom4j解析配置文件,放到容器对象中
二级缓存原理
分别用不同的配置文件配置,因为数据库连接配置不需要频繁改动,sql语句配置会频繁变动
1. 解析配置文件
二级缓存的生命周期是整个应用,所以必须限制二级缓存的容量,在这里mybatis使用的是溢出淘汰机制。而一级缓存是会话级的生命周期非常短暂是没有必要实现这些功能的。相比较之下,二级缓存机制更加完善。二级缓存在结构设计上采用装饰器+责任链模式二级缓存的使用(命中条件)1)会话提交后2)sql语句、参数相同3)相同的statementID4)RowBounds相同注意:设置为自动提交事务并不会命中二级缓存。二级缓存执行流程1)查询是实时查询缓存区的。2)所有对二级缓存的实时变动都是通过暂存区来实现的。3)暂存区清理完会进行标识,但此时二级缓存中数据并未清理,只有执行commit后才会真正清理二级缓存中的数据。4)查询会实时查询缓存区,若暂存区清理标识为true就算从缓存区中查询到数据也会返回一个null,重新查询数据库(暂存区清理标识为true也会返回null是为了防止脏读,一旦提交清空掉二级缓存中的数据此时读取到的就是脏数据,因此返回null重新查询数据库得到的才是正确数据)。一级缓存(会话级)和二级缓存(全局)执行顺序:先查二级缓存,再查一级缓存,再查数据库;即使在一个sqlSession中,也会先查二级缓存。二级缓存数据来自一级缓存
不同业务表用不同的配置文件唯一标识
创建 SqlSessionFactoryBuilder 类:build(InputSteam in)
SqlSessionFactory 对象
读取配置⽂件
数据库连接创建、释放频繁造成系统资源浪费,从⽽影响系统性能。
启动流程
同一个业务表的不同sql语句唯一标识
自定义框架实现
存在问题
读取配置
Executor:调用JDBC接口
// Mapper配置文件解析public class XMLMapperBuilder { private Configuration configuration; public XMLMapperBuilder(Configuration configuration) { this.configuration =configuration; } public void parse(InputStream inputStream) throws DocumentException { Document document = new SAXReader().read(inputStream); Element rootElement = document.getRootElement(); String namespace = rootElement.attributeValue(\"namespace\"); List<Element> list = rootElement.selectNodes(\"//select\"); for (Element element : list) { String id = element.attributeValue(\"id\"); String resultType = element.attributeValue(\"resultType\"); String paramterType = element.attributeValue(\"paramterType\"); String sqlText = element.getTextTrim(); MappedStatement mappedStatement = new MappedStatement(); mappedStatement.setId(id); mappedStatement.setResultType(resultType); mappedStatement.setParamterType(paramterType); mappedStatement.setSql(sqlText); String key = namespace+\".\
解决思路
ForEachSqlNode
0 条评论
下一页