Mybatis原理及源码分析
什么是Mybatis?
Mybatis是一个半自动化的持久层框架。
Mybatis可以将向PreparedStatement中的输入参数自动进行映射(输入映射),将结果集映射成Java对象(输出映射)
为什么使用Mybatis?
JDBC:
SQL夹杂在Java代码块中,耦合度高导致硬编码
维护不易且实际开发需求中SQL有变化,频繁修改的情况多见
Hibernate和JPA:
长难复杂SQL,对于Hibernate而言处理也不容易
内部自动生成的SQL,不容易做特殊优化
基于全映射的全自动框架,大量字段的POJO进行部分映射时比较苦难,导致数据库性能下降
而实际开发中,对开发人员而言,核心SQL还是需要自己优化,而Mybatis中SQL和Java代码分开,功能边界清晰,一个专注业务,一个专注数据
配置文件,mybatis-config.xml
<?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 resource=""></properties><!--加载配置文件-->
<settings>
<!--开启二级缓存,默认开启-->
<setting name="cacheEnabled" value="true"/>
</settings>
<typeAliases> <!--设置单个pojo别名-->
<!--<typeAlias alias="Employee" type="com.yang.domain.Employee"/>-->
<!--对整个包下的pojo设置别名,别名为类名,如果类上使用了@Alias("")注解指定了别名则用注解设置的-->
<package name="com.yang.domain"/>
</typeAliases>
<!--与Spring整合后,environment配置将废除-->
<environments default="development">
<environment id="development">
<!--使用jdbc事务管理,由mybatis自己管理-->
<transactionManager type="JDBC"></transactionManager>
<!--数据库连接池,由mybatis自己管理-->
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/test"/>
<property name="username" value="yang"/>
<property name="password" value="yang"/>
</dataSource>
</environment>
</environments>
<!--我们写的sql映射文件-->
<mappers>
<mapper resource="mybatis/xxxMapper.xml"/>
</mappers>
</configuration>
logback.xml,打印sql
<?xml version="1.0" encoding="UTF-8"?>
<configuration debug="false">
<!--定义日志文件的存储地址 勿在 LogBack 的配置中使用相对路径-->
<property name="LOG_HOME" value="/logback/LogFile"/>
<!--控制台输出-->
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%date{yyyy-MM-dd HH:mm:ss.SSS}|%thread|%-5level|%r|%X{threadId}|%C|%msg%n</pattern>
</encoder>
</appender>
<!--sql相关-->
<logger name="java.sql">
<level value="debug" />
</logger>
<logger name="org.apache.ibatis">
<level value="info" />
</logger>
<root level="INFO">
<appender-ref ref="STDOUT"/>
</root>
</configuration>
简单的Mybatis操作数据库步骤:
1:创建Mybatis全局配置文件,包含了影响Mybatis行为的设置(setting)和属性(properties)信息、如数据库连接池信息等
2:创建SQL映射文件,映射文件的作用就相当于是定义Dao接口的实现类如何工作
3:将sql映射文件注册到全局配置中
4:持久化代码
1):根据全局配置文件得到SqlSessionFactory
2):使用SqlSessionFactory,获取到SqlSession对象使用它来执行增删改查,一个SqlSession就是代表和数据库的一次会话,用完则关闭
3):使用sql的唯一标志,namespace+id,执行sql
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
// 1、获取sqlSessionFactory对象
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
// 2、获取sqlSession对象
SqlSession openSession = sqlSessionFactory.openSession();
try {
// 3、获取接口的实现类对象
//会为接口自动的创建一个代理对象,代理对象去执行增删改查方法
Employee employee = (Employee) openSession.selectOne(
"com.atguigu.mybatis.EmployeeMapper.selectEmp", 1);
} finally {
openSession.close();
}
第二种方式,接口式编程
xxxMapper.xml中的namespace设置为xxxMapper接口的全路径名,Mybatis会为Mapper接口创建一个代理对象
使用接口式编程会有更强的类型检查,参数控制等
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
// 1、获取sqlSessionFactory对象
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
// 2、获取sqlSession对象
SqlSession openSession = sqlSessionFactory.openSession();
try {
// 3、获取接口的实现类对象
//会为接口自动的创建一个代理对象,代理对象去执行增删改查方法
EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class);
Employee employee = mapper.getEmpById(1);
} finally {
openSession.close();
}
SqlSession,需要注意:
1、SqlSession的实例不是线程安全的,因此是不能被共享的
2、SqlSession每次使用完成后需要正确关闭,这个关闭操作是必须的
3、SqlSession可以直接调用方法的id进行数据库操作,不过一般推荐使用SqlSession获取到Dao接口的代理类,执行代理对象的方法,可以更安全的进行类型检查操作
代理Mapper执行方法的源码:
1、JDK动态代理创建Mapper的代理类
public static <T> T newMapperProxy(Class<T> mapperInterface, SqlSession sqlSession) {
ClassLoader classLoader = mapperInterface.getClassLoader();
Class<?>[] interfaces = new Class[]{mapperInterface};
MapperProxy proxy = new MapperProxy(sqlSession);
return Proxy.newProxyInstance(classLoader, interfaces, proxy);
}
2、代理类执行方法
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (method.getDeclaringClass() == Object.class) {
return method.invoke(this, args);
} else {
Class<?> declaringInterface = this.findDeclaringInterface(proxy, method);
MapperMethod mapperMethod = new MapperMethod(declaringInterface, method, this.sqlSession);
Object result = mapperMethod.execute(args);
if (result == null && method.getReturnType().isPrimitive() && !method.getReturnType().equals(Void.TYPE)) {
throw new BindingException("Mapper method '" + method.getName() + "' (" + method.getDeclaringClass() + ") attempted to return null from a method with a primitive return type (" + method.getReturnType() + ").");
} else {
return result;
}
}
}
execute:
public Object execute(Object[] args) {
Object result = null;
Object param;
// 判断执行sql类型,insert,update、delete或select,然后封装参数,调用的还是sqlSession的增删改查方法
if (SqlCommandType.INSERT == this.type) {
param = this.getParam(args);
result = this.sqlSession.insert(this.commandName, param);
} else if (SqlCommandType.UPDATE == this.type) {
param = this.getParam(args);
result = this.sqlSession.update(this.commandName, param);
} else if (SqlCommandType.DELETE == this.type) {
param = this.getParam(args);
result = this.sqlSession.delete(this.commandName, param);
} else {
if (SqlCommandType.SELECT != this.type) {
throw new BindingException("Unknown execution method for: " + this.commandName);
} if (this.returnsVoid && this.resultHandlerIndex != null) {
this.executeWithResultHandler(args);
} else if (this.returnsList) {
result = this.executeForList(args);
} else if (this.returnsMap) {
result = this.executeForMap(args);
} else {
param = this.getParam(args);
result = this.sqlSession.selectOne(this.commandName, param);
}
} return result;
}
getParam方法:
Mybatis的缓存:
Mybatis提供查询缓存,用于减轻数据库压力,提高数据库性能,Mybatis提供一级缓存,二级缓存
一级缓存(粒度小):SqlSession级别的缓存,在操作数据库时需要构造SqlSession对象,在对象中有一个数据结构(HashMap)用于存储缓存数据,不同的SqlSession之间的缓
存数据区域是互相不影响的。
当第一次发起查询请求时,先去缓存中查找有没有符合的信息,如果没有,就从数据库中去查,然后将结果信息存储到一级缓存中
如果SqlSession执行了Commit操作(插入、删除、更新)等,将清空SqlSession中的一级缓存,为了让缓存中的信息是最新信息,避免脏读,Mybatis默认是支持一级缓存的,
关掉一级缓存的话需要在配置文件中配置。
SqlSession关闭,一级缓存就清空
应用:将Mybatis和Spring整合开发,事务控制是在service中,开始执行时,开启事务,创建SqlSession对象。
在service方法内第一次调用,第二次调用将从一级缓存中取数据,方法结束,SqlSession关闭
如果执行了两次service方法调用查询相同的信息,不走一级缓存,因为service方法结束,SqlSession就关闭了,一级缓存也随之清空
二级缓存(粒度大):Mapper级别的缓存,多个SqlSession去操作同一个mapper sql语句,多个SqlSession可以共用二级缓存,二级缓存是跨SqlSession的。
多个SqlSession共享一个Mapper的二级缓存区域,按照namespace区分,每个mapper有都按自己的namespace区分的缓存区域。二级缓存默认是也是开启的
开启二级缓存:
1):在mybatis-config.xml配置文件的setting标签中设置二级缓存的开关
2):在每个具体的mapper.xml文件中开启二级缓存
3):调用pojo类实现序列化接口,因为为了将缓存数据取出执行反序列操作,因为二级缓存存储介质多种多样,不一定在内存
禁用缓存:
在每个select标签中设置useCache="false",如果想要针对每次查询都需要最新的数据,则需要设置禁用二级缓存
Mybatis和Spring整合:
1)、需要Spring通过单例方式管理SqlSessionFactory,注入org.mybatis.spring.SqlSessionFactoryBean指定mybatis配置文件地址、dataSource、mapper.xml、别名等
2)、Spring和Mybatis整合生成代理对象,使用SqlSessionFactory创建Session(整合自动完成),提供SqlSessionTemplate
3)、持久层的mapper都需要由Spring进行管理
Mybatis原理及源码分析的更多相关文章
- OpenCV学习笔记(27)KAZE 算法原理与源码分析(一)非线性扩散滤波
http://blog.csdn.net/chenyusiyuan/article/details/8710462 OpenCV学习笔记(27)KAZE 算法原理与源码分析(一)非线性扩散滤波 201 ...
- ConcurrentHashMap实现原理及源码分析
ConcurrentHashMap实现原理 ConcurrentHashMap源码分析 总结 ConcurrentHashMap是Java并发包中提供的一个线程安全且高效的HashMap实现(若对Ha ...
- HashMap和ConcurrentHashMap实现原理及源码分析
HashMap实现原理及源码分析 哈希表(hash table)也叫散列表,是一种非常重要的数据结构,应用场景及其丰富,许多缓存技术(比如memcached)的核心其实就是在内存中维护一张大的哈希表, ...
- (转)ReentrantLock实现原理及源码分析
背景:ReetrantLock底层是基于AQS实现的(CAS+CHL),有公平和非公平两种区别. 这种底层机制,很有必要通过跟踪源码来进行分析. 参考 ReentrantLock实现原理及源码分析 源 ...
- 【转】HashMap实现原理及源码分析
哈希表(hash table)也叫散列表,是一种非常重要的数据结构,应用场景极其丰富,许多缓存技术(比如memcached)的核心其实就是在内存中维护一张大的哈希表,而HashMap的实现原理也常常出 ...
- 【OpenCV】SIFT原理与源码分析:DoG尺度空间构造
原文地址:http://blog.csdn.net/xiaowei_cqu/article/details/8067881 尺度空间理论 自然界中的物体随着观测尺度不同有不同的表现形态.例如我们形 ...
- 《深入探索Netty原理及源码分析》文集小结
<深入探索Netty原理及源码分析>文集小结 https://www.jianshu.com/p/239a196152de
- HashMap实现原理及源码分析之JDK8
继续上回HashMap的学习 HashMap实现原理及源码分析之JDK7 转载 Java8源码-HashMap 基于JDK8的HashMap源码解析 [jdk1.8]HashMap源码分析 一.H ...
- 【OpenCV】SIFT原理与源码分析:关键点描述
<SIFT原理与源码分析>系列文章索引:http://www.cnblogs.com/tianyalu/p/5467813.html 由前一篇<方向赋值>,为找到的关键点即SI ...
随机推荐
- Spring源码解读--(一)源码下载
走在Java程序员这条路上,网上Java各种工具满天飞,写个简单的CRUD,相信是个开发都能写出来,于是在思考如何可以在同行业中更有竞争力(其实就是如何赚更多钱).那么,老大给我推荐了Spring源码 ...
- python爬虫学习之路-遇错笔记-1
当在运行爬虫时同时开启了Fidder解析工具时(此爬虫并不是用于爬取手机端那内容,而是爬去电脑访问的网页时),访问目标站点会遇到以下错误: File "C:\Users\litao\AppD ...
- EL表达式(二)运算符
运算符"."和"[]": "."能做的"[]"也能做,"[]"能做的"."不一定 ...
- 让Debian以root登录
Debian默认不允许root登录,所以修改之. (1)让Debian以root登录 修改gdm3的登录pam文件 #vi /etc/pam.d/gdm3 将auth required pam_suc ...
- Linux安装redis服务器和部署
Linux安装redis和部署 第一步:下载安装包 wget http://download.redis.io/releases/redis-5.0.5.tar.gz 访问https://redis. ...
- Python3数据科学入门与实践学习教程
整个课程都看完了,这个课程的分享可以往下看,下面有链接,之前做java开发也做了一些年头,也分享下自己看这个视频的感受,单论单个知识点课程本身没问题,大家看的时候可以关注下面几点: 1.为了追求精 ...
- MyBatis-Spring的sqlSessionTemplate
转自:http://www.cnblogs.com/yhtboke/p/5611375.html SqlSessionTemplate SqlSessionTemplate是MyBatis-Sprin ...
- js实现千位符分隔
前几天面试做保险项目的公司,被问到了一道实现千位符分割方法的题,乍一看挺简单,但做起来最后却没给出来一个合适的解决方法.回来自己琢磨了一个还行的答案. var num = 3899000001, ar ...
- jvm性能监控(5)-jdk自带工具 VisualVM
一.在服务器的jdk的bin目录下添加配置文件 jstatd.all.policy [root@localhost /]# cd /usr/local/src/jdk1.8.0_131/bin/ [r ...
- k8s nginx ingress配置TLS
在没有配置任何nginx下,k8s的nginx默认只支持TLS1.2,不支持TLS1.0和TLS1.1 默认的 nginx-config(部分可能叫 nginx-configuration)的配置如下 ...