MyBatis是什么?
MyBatis是一个ORM框架,为什么需要一个ORM框架呢?
传统的JDBC编程,一般需要6步
注册数据库驱动,指定数据库地址账号密码
DriverManager打开数据库连接
通过数据库连接创建Statement对象
Statement对象执行SQL语句,得到ResultSet对象
ResultSet对象得到的数据转换为JavaBean
关闭ResultSet以及各种数据库连接资源
其中1、2、3、6都是重复性代码,也业务没有关系的,所以会封装成工具类DBUtils,但是还有个问题,就是步骤5,封装是不够通用和彻底的。为了解决这个问题,就有了ORM框架-Object Relational Mapping对象关系映射。
就是select xx_xx xxx_xxx from table_xx 字段自动就映射成JavaBean中的属性了。
除此之外,既然是数据库和业务之间的中间件,当然会顺便解决其他问题。例如数据库连接比较珍贵,所以用连接池,例如给数据库中的数据顺便做个缓存。
ORM框架有很多,来一个友商对比。
Hibernate:曾经最屌的,毕竟SSH三大框架之一。映射得很彻底,可以完全脱离SQL,就像理解对象之间的关系来理解表的关系。脱离了SQL,优点没有侵入性,MySQL切换到oracle也可以。缺点是代码职责不单一了,代码干了SQL该干的事,索引、存储过程、函数、优化啊什么的,就拜拜了。所以后来没那么流行了。
SpringJDBC:非常轻量级的ORM,开发起来只需要关注SQL和SQL返回的数据如何处理就可以。很原生。没有映射文件、缓存等概念。
MyBatis:功能丰富如hibernate,但非常灵活,主要是可以用SQL,反正互联网应用99%都是MySQL吧,不会在多种数据库中来回切换。
HelloWorld一下
这里用了lombok,不仅需要jar包还需要IDEA插件支持,不然会报错。lombok原理就是利用一个Java的规范,定义编译期注解把一些简单的get set等代码织入。用了之后会觉得pojo都很干净=。=
mybatis有多种用法,简单的可以用注解搞定,复杂点的SQL还是用XML可读性更好。
原理浅析
mybatis是个框架,有三层功能架构。
基础支持层:反射、类型转换、日志、资源加载等
核心处理层:配置解析、参数映射、SQL解析、SQL执行、结果集映射、插件
接口层:SqlSession接口中的事务和增删改查
回到这个helloworld,点进去看一下源码,分为4步。
new SqlSessionFactoryBuilder().build(inputStream) 这一步把所有配置文件信息解析并保存在Configuration对象中。这是一个很大很复杂的对象,所以用了builder模式。各种配置信息包括Mapper配置信息都保存了。
sqlSessionFactory.openSession() 这一步返回了SqlSession对象,主要做了事务初始化和Executor初始化。
getMapper 这一步返回接口的代理对象。毕竟接口无法执行。返回代理对象,也是因为还有很多的配置的插件啊事务啊等着通过动态代理织入。
具体的执行增删改查。这一步会通过创建的四大对象来搞事情。
Executor:具体来执行的
StatementHandler:处理SQL预编译
ParameterHandler:设置预编译参数
ResultHandler:处理结果集
还有个TypeHandler,在JavaBean和数据库字段类型之间做映射转换
常用扩展之插件和TypeHandler
先说一下插件的原理,看代码
public Object pluginAll(Object target){
for(Interceptor interceptor:interceptors){
target = interceptor.plugin(target);
}
return target;
}
是不是和上篇说的拦截链一毛一样...
可以这么理解,通过动态代理层层包装。
所以框架给使用者暴露的,只是一个Interceptor接口。实现接口,覆写方法,指定拦截的对象和方法,注册一下插件就可以。
mybatis插件界最著名的肯定就是分页插件了。核心原理是通过插件拦截每个执行的sql语句,举例是select * from xxx ,拦截下来之后,再搞点事情,select count(*) from (select * from xxx) temp ,就拿到了条数了,根据pageSize pageNo就可以分页了。
具体怎么用,大神有专门的文档:https://github.com/pagehelper/Mybatis-PageHelper/blob/master/wikis/zh/HowToUse.md
插件还有很多广泛的应用。例如,支持存储过程的插件、批处理的插件、乐观锁的插件。其实都是在写切面逻辑,通过动态代理来实现给指定的一大片代码加上这个逻辑。
说一个用插件非常巧妙的解决的事情。可能在看这篇文章的你也用得到。
我们知道,在写SQL的时候,可能翻一个错,“update xx set xx=xx where xx=xx ”,这个where后面的条件,可能由于代码判断的问题,丢失了,那就成了update xx set xx=xx 整个表都改了。这属于代码逻辑问题,属于非常隐藏的bug。怎么解决呢?怎么杜绝呢?MySQL数据库层面,有个安全更新模式设置,set SQL_SAFE_UPDATE=1,但是太死板了,开启之后,执行更新或者删除必须添加where或者limit,而且where字段必须是主键或者有索引字段。
有没有更灵活的方式呢?嗯,用mybatis的插件。
思路是,先自定义一个注解叫做@AllowEmptyWhere,有这个注解的mapper方法,就意味着开发者经过思考,这个sql语句可以如此使用,不会出现update整个库的问题,否则默认没有这个注解,拦截器拦截所有执行的SQL,对这个具体的sql字符串分析一下where后面条件为空,则抛出自定义异常。
官方对于这种常用的插件功能文档非常友好,例如实现Interceptor接口后,在intercept方法中,文档会告诉你,
invocation.getTarget()->被代理对象
invocation.getMethod()->被代理方法
invocation.getArgs()->方法参数
invocation.proceed()->真实的方法执行,在这个之前和之后可以写相应的逻辑。
再说TypeHandler
第一个常见的扩展是枚举。项目中会用到大量枚举,大部分都是和数据库相关的,也就是要从Javabean映射到数据库字段,默认情况下,映射到数据库中的内容是枚举值,打个比方,性别是FEMALE,在数据库中存的也是这个字符串,这就比较浪费空间了。因为可以用tinyint(1)来搞定的。所以这里要改改默认的枚举映射。继承BaseTypeHandler,覆写几个方法就可以了。就是在Javabean和数据库打交道的“来去之间”指定一下转换规则。那就两全其美了,数据库的数据存得很节省,代码中的枚举可读性很强,FEMLE(1,"女性")。
另一个类型扩展,和枚举差不多一个意思。我们知道金钱为了避免小数以及对账对不上的尴尬问题,在数据库中是以int类型单位为分存储的,一块钱,就是100分,没有小数点。Java对象中呢,的确可以存储为分,最终JSON的形式通过接口给到客户端,还是分,没安全问题,但是可以做的更好,例如,Java对象中对于金额是一个Amount对象,这个对象中有三个字段,fen\yuan\formatYuan,也就是说默认给到前端就是个对象。有这个想法,实现起来当然也很简单。截个图。
mybatis这个框架是非常优秀的,毕竟是在hibernate的阴影下,大家用代码投票选择了mybatis。“历史的进程”~
领取专属 10元无门槛券
私享最新 技术干货