Mybatis回顾总结
Mybatis是一个基于Java的持久层框架,内部封装了JDBC,使开发者只需要关注SQL语句本身,而不需要去处理加载驱动、创建连接、创建statement等复杂过程。
Mybatis通过xml或者注解,将要执行的statement配置起来,通过Java对象和statement中SQL的动态参数进行映射,生成最终执行的SQL语句。
1、开发步骤
- 导入Mybatis依赖的jar包
- [创建库表对应实体类]
- 编写映射文件xxxMapper.xml
- 编写核心配置文件xxx.xml
<dependencies> <!--Mybatis依赖的jar--> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.8</version> </dependency> <!--MySQL驱动--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.28</version> </dependency> <!--Oracle驱动--> <dependency> <groupId>com.oracle.database.jdbc</groupId> <artifactId>ojdbc8</artifactId> <version>21.3.0.0</version> </dependency> <!--测试jar--> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> <scope>test</scope> </dependency> </dependencies>
映射文件:empMapper.xml
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <!--namespace:命名空间,值可以任意,但是,若使用面向接口的方式开发时,必须是接口的全限定名--> <mapper namespace="org.silence.dao.EmpMapper"> <!--SQL语句, id:唯一标识,值可任意,但当使用面向接口的方式开发时,必须是接口中的方法名 resultType:返回值类型,若返回实体对象,则使用对象的全限定名--> <select id="queryAll" resultType="org.silence.entity.Emp"> select * from emp </select> </mapper>
核心配置文件:Mybatis-conf.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> <!--用于配置数据库环境--> <environments default="oracle"> <!--Oracle数据库配置--> <environment id="oracle"> <transactionManager type="JDBC"/> <dataSource type="POOLED"> <property name="driver" value="oracle.jdbc.driver.OracleDriver"/> <property name="url" value="jdbc:oracle:thin:@127.0.0.1:1521:orcl"/> <property name="username" value="scott"/> <property name="password" value="scott"/> </dataSource> </environment> <!--MySQL数据库配置--> <environment id="mysql"> <transactionManager type="JDBC"/> <dataSource type="POOLED"> <property name="driver" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql://127.0.0.1:3306/root/"/> <property name="username" value="root"/> <property name="password" value="root"/> </dataSource> </environment> </environments> <!--引入mapper映射文件--> <mappers> <mapper resource="mapper/EmpMapper.xml"/> </mappers> </configuration>
映射接口:
public interface EmpMapper { List<Emp> queryAll(); }
测试类:
@Test public void test1() throws IOException { String resource = "Mybatis-conf.xml"; // 加载配置文件 InputStream inputStream = Resources.getResourceAsStream(resource); // 获取SqlSession对象 SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); SqlSession session = sqlSessionFactory.openSession(); // 方式①:原始方式,直接通过session对象执行SQL语句,参数为mapper.xml中的namespace值+SQL语句的id List<Emp> list = session.selectList("org.silence.dao.EmpMapper.queryAll"); System.out.println(list); System.out.println("面向接口开发===================="); // 方式②:面向接口,通过session对象获取接口的实现类mapper对象(Mybatis自动实现) EmpMapper mapper = session.getMapper(EmpMapper.class); // 调用mapper中的方法即可 List<Emp> empList = mapper.queryAll(); System.out.println(empList); }
2、工作流程
-
加载配置并初始化
加载配置文件,将SQL的配置信息加载成一个个MappedStatement对象(包括了传入参数映射配置、执行的SQL语句、结果映射配置),存储在内存中。
-
接受调用请求
调用MyBatis提供的API,传入SQL的id和传入参数对象,将请求传递给下层的请求处理层进行处理。
-
处理操作请求
- 根据SQL的id查找对应的MappedStatement对象
- 根据传入参数对象解析MappedStatement对象,得到最重要执行的SQL和执行传入参数。
- 获取数据库连接,执行SQL。
- 根据MappedStatement对象中的结果映射配置对得到的执行结果进行转换处理,得到最终的处理结果。
- 释放数据库连接。
- 返回处理结果。
3、核心配置文件
MyBatis 的配置文件包含了会深深影响 MyBatis 行为的设置和属性信息。 配置文档的结构如下:
-
configuration(配置)
-
这些属性可以在外部进行配置,并可以进行动态替换。你既可以在典型的 Java 属性文件中配置这些属性,也可以在 properties 元素的子元素中设置。设置好的属性可以在整个配置文件中用来替换需要动态配置的属性值:
<!--配置数据库连接属性--> <properties resource="org/silence/db/jdbc.properties"> <property name="username" value="test"/> <property name="password" value="123456"/> </properties> <!--使用已经配置的数据库连接属性--> <dataSource type="POOLED"> <property name="driver" value="${driver}"/> <property name="url" value="${url}"/> <property name="username" value="${username}"/> <property name="password" value="${password}"/> </dataSource>
还可以在 SqlSessionFactoryBuilder.build() 方法中传入属性值:
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader, props); // 或者 SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader, environment, props);
通过方法参数传递的属性具有最高优先级,resource/url 属性中指定的配置文件次之,最低优先级的则是 properties 元素中指定的属性。
-
这是 MyBatis 中极为重要的调整设置,它们会改变 MyBatis 的运行时行为。
-
environments(环境配置)
- environment(环境变量)
- transactionManager(事务管理器)
- dataSource(数据源)
- environment(环境变量)
-
4、缓存
本地缓存(一级缓存)
默认开启。基于PerpetualCache的HashMap本地缓存,其存储作用域为session。
每当一个新 session 被创建,MyBatis 就会创建一个与之相关联的本地缓存。任何在 session 执行过的查询结果都会被保存在本地缓存中,所以,当再次执行参数相同的相同查询时,就不需要实际查询数据库了。本地缓存将会在做出修改、事务提交或回滚,以及关闭 session 时清空。
默认情况下,本地缓存数据的生命周期等同于整个 session 的周期。由于缓存会被用来解决循环引用问题和加快重复嵌套查询的速度,所以无法将其完全禁用。但是可以通过设置 localCacheScope=STATEMENT
来只在语句执行时使用缓存。
注意,如果 localCacheScope 被设置为 SESSION,对于某个对象,MyBatis 将返回在本地缓存中唯一对象的引用。对返回的对象(例如 list)做出的任何修改将会影响本地缓存的内容,进而将会影响到在本次 session 中从缓存返回的值。因此,不要对 MyBatis 所返回的对象作出更改,以防后患。
可以随时调用SqlSession 中的clearCache()
方法来清空本地缓存:
String resource = "Mybatis-conf.xml"; InputStream inputStream = Resources.getResourceAsStream(resource); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); SqlSession sqlSession = sqlSessionFactory.openSession(); EmpMapper mapper = sqlSession.getMapper(EmpMapper.class); // 手动清空缓存 sqlSession.clearCache(); ...
判断两次查询完全相同:
mybatis认为,对于两次查询,如果以下条件都完全一样,那么就认为它们是完全相同的两次查询。
-
传入的statementId。
-
查询时要求的结果集中的结果范围。
-
这次查询所产生的最终要传递给JDBC java.sql.Preparedstatement的Sql语句字符串(boundSql.getSql() )。
-
传递给java.sql.Statement要设置的参数值。
二级缓存
MyBatis 内置了一个强大的事务性查询缓存机制,它可以非常方便地配置和定制。
默认情况下,只启用了本地的会话缓存(一级缓存),它仅仅对一个会话中的数据进行缓存。
要启用全局的二级缓存,只需要在你的 SQL 映射文件中添加一行:
<cache/>
这个简单语句的效果如下:
- 映射语句文件中的所有 select 语句的结果将会被缓存。
- 映射语句文件中的所有 insert、update 和 delete 语句会刷新缓存。
- 缓存会使用最近最少使用算法(LRU, Least Recently Used)算法来清除不需要的缓存。
- 缓存不会定时进行刷新(也就是说,没有刷新间隔)。
- 缓存会保存列表或对象(无论查询方法返回哪种)的 1024 个引用。
- 缓存会被视为读/写缓存,这意味着获取到的对象并不是共享的,可以安全地被调用者修改,而不干扰其他调用者或线程所做的潜在修改。
同时,resultType的类必须实现Serializable接口。
提示 :缓存只作用于 cache 标签所在的映射文件中的语句。如果混合使用 Java API 和 XML 映射文件,在共用接口中的语句将不会被默认缓存。你需要使用 @CacheNamespaceRef
注解指定缓存作用域。
cache 元素的属性:
<!--
这个配置创建了一个 FIFO 缓存,每隔 60 秒刷新,最多可以存储结果对象或列表的 512 个引用,而且返回的对象被认为是只读的,因此对它们进行修改可能会在不同线程中的调用者产生冲突。
-->
<cache
eviction="FIFO"
flushInterval="60000"
size="512"
readOnly="true"/>
eviction(缓存清除策略):
可用的清除策略有:
LRU
– 最近最少使用:移除最长时间不被使用的对象。FIFO
– 先进先出:按对象进入缓存的顺序来移除它们。SOFT
– 软引用:基于垃圾回收器状态和软引用规则移除对象。WEAK
– 弱引用:更积极地基于垃圾收集器状态和弱引用规则移除对象。
默认的清除策略是 LRU。
flushInterval(刷新间隔):可以被设置为任意的正整数,设置的值应该是一个以毫秒为单位的合理时间量。 默认情况是不设置,也就是没有刷新间隔,缓存仅仅会在调用语句时刷新。
size(引用数目):可以被设置为任意正整数,要注意欲缓存对象的大小和运行环境中可用的内存资源。默认值是 1024。
readOnly(只读):可以被设置为 true 或 false。只读的缓存会给所有调用者返回缓存对象的相同实例。 因此这些对象不能被修改。这就提供了可观的性能提升。而可读写的缓存会(通过序列化)返回缓存对象的拷贝。 速度上会慢一些,但是更安全,因此默认值是 false。
注!:二级缓存是事务性的。
**只有当 SqlSession **
- 完成并提交时
- 完成并回滚,但没有执行 flushCache=true 的 insert/delete/update 语句时
- 关闭时
二级缓存才会生效,对查询进行缓存。
两次查询之间进行了任意的数据修改操作,会使一级缓存和二级缓存同时失效。
在Mapper.xml的具体SQL语句中通过 useCahe="false"
可以关闭当前语句的缓存:
<select id="findById" useCahe="false" ... /></select>
cache-ref
对某一命名空间的语句,只会使用该命名空间的缓存进行缓存或刷新。 可以使用 cache-ref
元素来引用另一个缓存来实现在多个命名空间中共享相同的缓存配置和实例。
<cache-ref namespace="com.someone.application.data.SomeMapper"/>