Mybatis相关知识
1 Mybatis介绍
Mybatis 是一个==半 ORM(对象关系映射)==框架,它内部封装了 JDBC,开发时只需要关注 SQL 语句本身,不需要花费精力去处理加载驱动、创建连接、创建statement 等繁杂的过程。程序员直接编写原生态 sql,可以严格控制 sql 执行性能,灵活度高。
上面提到了半ORM框架,那么和全自动ORM框架区别在哪
答: 全自动ORM框架以Hibernate为例,Hibernate是全自动的ORM映射工具,使用Hibernate查询的关联对象或者关联集合对象时,根据对象关系模型直接获取。
而Mybatis在查询关联对象或者关联集合对象时,需要手动编写SQL来完成,因此是半自动ORM的框架。
Mybatis和Hibernate的适用场景:
Hibernate 是重量级框架,适合于需求相对稳定,中小型的项目
MyBatis 是轻量级框架,适合于需求变化频繁,大型的项目
2 Mybatis优势和劣势
优势:
基于SQL语句编程,灵活,不会对应用程序或者数据库的现有设计造成任何影响,解除sql与程序代码的耦合,便于统一管理;提供XML标签,支持编写动态SQL语句,并可重用。
与JDBC相比,消除了JDBC大量冗余的代码,不需要手动开关连接很好的与各种数据库兼容。
提供映射标签,支持对象与数据库的ORM字段关系映射;提供对象关系映射标签,支持对象关系组件维护
能够与Spring和其他框架很好的集成
缺点
SQL语句的编写工作量较大,尤其当字段多、关联表多时,对开发人员编写SQL语句的功底有一定要求。
SQL语句依赖于数据库,导致数据库移植性差,不能随意更换数据库。
后来,有更为强大的mybatis-plus,在此基础上做了增强,在开发中也得到了使用。
3 适用mybatis的步骤:
(1) 创建SqlSessionFactory
(2)通过SqlSessionFactory创建SqlSession
(3) sqlsession执行数据库操作
(4) session.commit()提交事务
(5) session.close()关闭会话
4 Mybatis的工作原理
第一步: 读取mybatis配置文件,mybatis-config.xml文件
第二步: mybatis-config.xml文件加载了项目程序的mapper文件,对应的xml文件。
第三步: 创建SqlSessionFactory
第四步: 通过SqlSessionFactory创建SqlSession
第五步: 执行器Executor
第六步: MapperedStatement对象
第七步: 输入参数类型,比如实体对象,基本数据类型等。
第八步: MapperedStatement对象将参数,执行数据库持久化操作,输出结果映射。
下面将用一个案例配套步骤进行讲解:
首先,得建立一个数据库,我这里数据库是babytun,里面有几张表商品表,商品种类表等。对应的java实体类是Goods和Category。
好来看mybatis-config.xml文件里面配置的内容
DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<settings>
<setting name="mapUnderscoreToCamelCase" value="true"/>
settings>
<plugins>
<plugin interceptor="com.github.pagehelper.PageInterceptor">
<property name="helperDialect" value="mysql"/>
<property name="reasonable" value="true"/>
plugin>
plugins>
<environments default="dev">
<environment id="dev">
<transactionManager type="JDBC">transactionManager>
<dataSource type="com.mybatis.datasource.C3p0DataSourceFactory">
<property name="driverClass" value="com.mysql.jdbc.Driver"/>
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/babytun?"/>
<property name="user" value="root"/>
<property name="password" value="root"/>
<property name="initialPoolSize" value="5"/>
<property name="maxPoolSize" value="20"/>
<property name="minPoolSize" value="5"/>
dataSource>
environment>
environments>
<mappers>
<mapper resource="mappers/goods.xml"/>
mappers>
configuration>
主要是配置实体类和数据库表驼峰转换,分页插件和数据源信息,mappers节点配置了对应Goods实体的xml文件即goods.xml。当然goods.xml就是操作和Goods相关的SQL。
来到第三步: 因为涉及创建会话,关闭会话,我们将这些方法封装到一个文件,方便调用。即文件MybatisUils.java。内容如下:
public class MybatisUils {
//利用static保证SqlSessionFactory全局唯一;
private static SqlSessionFactory sqlSessionFactory = null;
//利用static块在初始化类时实例化sqlSessionFactory;
static {
Reader reader = null;
try {
reader = Resources.getResourceAsReader("mybatis-config.xml");
sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
} catch (IOException e) {
e.printStackTrace();
//初始化错误时,通过抛出异常通知调用者;
throw new ExceptionInInitializerError(e);
}
}
public static SqlSession openSession(){ //创建新的SqlSession对象;
return sqlSessionFactory.openSession();
}
public static void CloseSession(SqlSession session){ //关闭SqlSession;
if(session!=null){
session.close();
}
}
}
此外,我们这里用的是c3p0数据源,这里创建了一个数据源工厂类C3p0DataSourceFactory
/*
c3p0和mybatis兼容使用的数据源工厂类;
*/
public class C3p0DataSourceFactory extends UnpooledDataSourceFactory {
public C3p0DataSourceFactory(){
this.dataSource = new ComboPooledDataSource();
}
}
来到第五步和后续步骤:
需要写一个方法,用mybatis完成对数据库的查询。
首先,比如完成对数据库goods表的全部查询。
@Test
public void testSelectAll(){
SqlSession session = null;
try {
session = MybatisUils.openSession();
List<Goods> list = session.selectList("goods.selectAll");
//goods.xml中的namespace="goods" select id="selectAll"相对应;
for(Goods good:list){
System.out.println(good.getTitle());
}
} catch (Exception e) {
e.printStackTrace();
}finally {
MybatisUils.CloseSession(session);
}
}
然后在goods.xml中写入对应的SQL,注意命名空间和方法保持一致。 id和后端的selectAll保持一致,而且在同一个xml文件中,方法id不要重名。 SQL语句如下,查询前十条商品按照id降序显示。 resultType就是上面的第八步,结果映射到实体。useCache表示是否适用缓存。 useCache=“false” 代表不使用缓存
<mapper namespace="goods">
<select id="selectAll" resultType="com.mybatis.entity.Goods" useCache="false">
select * from t_goods order by goods_id desc limit 10
select>
mapper>
提到了缓存,mybatis有一级缓存和二级缓存。
一级缓存是SqlSession级别的缓存,默认开启。
二级缓存是NameSpace级别(Mapper)的缓存,多个SqlSession可以共享,使用时需要进行配置开启。
缓存的查找顺序:二级缓存 => 一级缓存 => 数据库
从上面可以看到,二级缓存开启可以提高查询效率,减少数据库的访问次数,提高系统性能和响应速度。
怎么开启二级缓存呢?
这里可以在全局mybatis-config.xml配置,当然也可以在单个xml文件配置,例如,我这里在goods.xml配置。
在和 之间配置以下内容。一般放在SQL节点前。配置后的完整的goods.xml如下
相关参数,见下面的解释。
<mapper namespace="goods">
<cache eviction="LRU" flushInterval="600000" size="512" readOnly="true"/>
<select id="selectAll" resultType="com.mybatis.entity.Goods" useCache="false">
select * from t_goods order by goods_id desc limit 10
select>
mapper>
当然此刻,想必有同学注意到上面八个步骤,第五步和第六步貌似没有讲,因为这是框架完成的,那接下来讲Executor和MapperedStatement对象。
Executor:是执行器。
Mybatis有三种基本的Executor执行器,SimpleExecutor、ReuseExecutor、BatchExecutor。
SimpleExecutor:每执行一次update或select,就开启一个Statement对象,用完立刻关闭Statement对象。ReuseExecutor:执行update或select,以sql作为key查找Statement对象,存在就使用,不存在就创建,用完后,不关闭Statement对象。
BatchExecutor:执行update没有select,将所有sql都添加到批处理中(addBatch()),等待统一执行(executeBatch()),它缓存了多个Statement对象,每个Statement对象都是addBatch()完毕后,等待逐一执行executeBatch()批处理。
那么Mybatis中如何指定使用哪一种Executor执行器呢?
在Mybatis配置文件中,可以指定默认的ExecutorType,也可以手动给DefaultSqlSessionFactory的创建SqlSession的方法传递ExecutorType类型参数,如SqlSession openSession(ExecutorType execType)。配置默认的执行器。SIMPLE 就是普通的执行器;REUSE 执行器会重用预处理语句; BATCH 执行器将重用语句并执行批量更新。
MapperedStatement对象
Mybatis将所有Xml配置信息都封装到All-In-One对象Configuration内部。在Xml映射文件中,标签会被解析为ParameterMap对象,其每个子元素会被解析为ParameterMapping对象。标签会被解析为ResultMap对象,其每个子元素会被解析为ResultMapping对象。每一个select insert delete update 标签均会被解析为MappedStatement对象,标签内的sql会被解析为BoundSql对象
5 Mybatis延迟加载及其原理
Mybatis仅支持association关联对象和collection关联集合对象的延迟加载,association指的就是一对一,collection指的就是一对多查询。
在Mybatis配置文件中,可以配置是否启用延迟加载lazyLoadingEnabled=true|false。
原理
使用CGLIB创建目标对象的代理对象,当调用目标方法时,进入拦截器方法,比如调用a.getB().getName(),拦截器invoke()方法发现a.getB()是null值,那么就会单独发送事先保存好的查询关联B对象的sql,把B查询上来,然后调用a.setB(b),于是a的对象b属性就有值了,接着完成a.getB().getName()方法的调用。
今天先到这里,下期讲继续讲Mybatis的具体SQL语句的书写及其注意事项。