系列文章目录
IDEA项目实践——创建Java项目以及创建Maven项目案例、使用数据库连接池创建项目简介
IDEA创建项目的操作步骤以及在虚拟机里面创建Scala的项目简单介绍_intellij 创建scala
IDEA项目实践——动态SQL、关系映射、注解开发
文章目录
系列文章目录
1.MyBatis
1.1 MyBatis入门
1.ORM框架
2.MyBatis简介
4. MyBatis的工作流程和核心对象
5. 配置日志:
1.2 MyBatis接口开发(代理开发)【较为重要】
1. 实现步骤:
1.创建Mapper接口
2.修改mapper.xml文件中的namespace,放到mapper文件夹
3. 修改主配置文件中映射文件的路径
4.测试接口开发
2. MyBatis动态代理原理
3. MyBatisX 插件
1.3 MyBatis核心配置
0.核心配置文件概览
常见配置:
1.properties元素
2.typeAliases
3.mappers
4.settings
1.4 MyBatis映射文件
1.映射文件概述
2.模糊查询总结
1.5 MyBatis缓存
1.一级缓存(本地缓存)【重点了解一级缓存】
演示一级缓存的案例,实现步骤:
1. 添加MyBatis的依赖
2.添加MyBatis的核心配置文件
3.创建POJO - 基于Lombok
4.创建Mapper接口
5.创建Mapper映射文件
6.创建MybatisUtil工具类
7.测试一级缓存
2.二级缓存(全局缓存)
总结
1.MyBatis
1.1 MyBatis入门
1.ORM框架
当今企业级应用的开发环境中,对象和关系数据是业务实体的两种表现形式。业务实体在内存中表现为对象,在数据库中变现为关系数据。当采用面向对象的方法编写程序时,一旦需要访问数据库,就需要回到关系数据的访问方式,这种转换为开发人员带来了很大的麻烦。 ORM框架是一个对象-关系映射的系统化解决方案,当ORM框架完成转换后,开发人员可以直接取用对象。常用的ORM框架有Hibernate和MyBatis,其作用是将数据库查询的数据封装为实体类对象。
ORM框架将数据库查询到的数据封装为实体类对象,ORM映射流程如上图所示。从图中可以看出,实体类与数据库之间通过ORM框架相互映射,应用程序可以直接获取映射完成的实体类。
2.MyBatis简介
MyBatis 是一款优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射。MyBatis免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。
4. MyBatis的工作流程和核心对象
1.工作流程
MyBatis的工作流程是MyBatis中重要的知识点,整个MyBatis工作流程分为5个步骤。
编写配置文件与映射文件,其中,配置文件设置数据库连接,映射文件设置与SQL文件相关的操作。
MyBatis通过配置文件和映射文件生成SqlSessionFactory对象,此对象在MyBatis整个生命周期中只存在一份,它负责为每一个连接生成SqlSession对象。
通过SqlSessionFactory对象生成SqlSession对象,SqlSession对象在每次连接中只有一个,它封装了操作数据库的所有方法。
在每一次连接中,通过SqlSession对象操作数据库,SqlSession通过底层的Executor执行器执行对应操作。Executor执行器分为两种,一种是普通执行器,另一种是缓存执行器。
Executor执行器将此次操作封装为MappedStatement对象,在执行SQL语句之前,Executor执行器通过MappedStatement对象将输入的实体对象或基本类型数据映射到SQL语句,在执行SQL语句之后,Executor执行器通过MappedStatement对象将SQL语句执行的结果映射为实体对象或基本类型数据
核心在于将关系型数据映射为MappedStatement对象
2.核心对象
(1)SqlSessionFactory
SqlSessionFactory是MyBatis中的核心类,它采用工厂设计模式构建,负责创建SqlSession对象。构建SqlSessionFactory对象需要使用SqlSessionFactoryBuilder类 调用SqlSessionFactoryBuilder类中的builder()方法即可创建SqlSessionFactory对象 build()方法有多种重载,参数可以选填Reader和InputStream的实现类
构建SqlSessionFactory对象
build方法的参数中需要MyBatis配置文件的输入流,接下来创建输入流。调用Resource类的getResourceAsStream()方法,传入配置文件的绝对路径
获得配置文件流之后,将其作为参数传入SqlSessionFactoryBuilder的build()方法中,调用build()方法,返回值就是SqlSessionFactory对象
使用SqlSessionFactory对象
在SqlSessionFactory类中存在openSession()方法与getConfiguration()方法,其中,openSession()方法可以创建SqlSession对象,也可以在该方法中传入参数来设置创建的SqlSession对象,getConfiguration()方法用于获取SqlSessionFactory的配置。
产生SqlSession对象
SqlSessionFactory类的主要作用是生产SqlSession对象,调用SqlSessionFactory对象的openSession()方法就可以产生SqlSession对象。 通过SqlSessionFactory对象生成的SqlSession对象,在每次连接中只有一个,它负责通过各种方法操作数据库。
(2)SqlSession
使用SqlSession对象
SqlSession对象是MyBatis中的核心类对象,在日常开发中,常用SqlSession对象与数据库进行交互。除此之外,SqlSession对象贯穿于整个数据库访问的过程,一定时间段内没有使用SqlSession对象时,需要及时调用SqlSession对象的close()方法,将其关闭。 SqlSession对象提供了执行SQL,提交事务或回滚事务,使用映射器等方法,在方法中需要指定映射文件中的方法。
方法名称 | 说明 |
---|---|
T selectOne(String var1) | 执行单条记录的查询操作,需传入执行查询的方法,返回映射的对象 |
T selectOne(String var1,Object var2) | 执行单条记录的查询操作,需传入执行查询的方法和参数,返回映射的对象 |
List<E> selectList(String var1) | 执行多条记录的查询操作,需传入执行查询的方法,返回查询结果的集合 |
List<E> selectList(String var1,Object var2) | 执行多条记录的查询操作,需传入执行查询的方法和参数,返回查询结果的集合 |
Map<K,V> selectMap(String var1,String var2) | 执行查询操作,返回一个映射查询结果的Map集合 |
Map<K,V> selectMap(String var1,Object var2,String var3) | 执行查询操作,需传入查询的方法和参数,返回Map集合 |
int insert(String var1) | 执行插入操作,需传入映射文件中的方法名,返回数据库中受影响的数据行数 |
int insert(String var1,Object var2) | 执行插入操作,需传入映射文件中的方法名和参数对象,返回数据库中受影响的数据行数 |
int update(String var1) | 执行更新操作,需传入映射文件中的方法名,返回数据库中受影响的数据行数 |
int update(String var1,Object var2) | 执行更新操作,需传入映射文件中的方法名和参数对象,返回数据库中受影响的数据行数 |
int delete(String var1) | 执行删除操作,需传入映射文件中的方法名,返回数据库中受影响的数据行数 |
int delete(String var1,Object var2) | 执行删除操作,需传入映射文件中的方法名和参数对象,返回数据库中受影响的数据行数 |
commit() | 提交事务 |
commit(boolean var1) | var1默认为false,参数值为true时表示强制提交 |
rollback() | 回滚 |
rollback(boolean var1) | 强制回滚 |
close() | 关闭SqlSession对象 |
T getMapper(Class<T> var1) | 获取映射器 |
5. 配置日志:
有很多内容,了解即可
-
导入log4j的依赖
<dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.12</version> </dependency>
-
在应用的类路径中创建一个名为
log4j.properties
的文件,文件的具体内容如下:# 全局日志配置 log4j.rootLogger=ERROR, stdout # MyBatis 日志配置 log4j.logger.org.mybatis.example.BlogMapper=TRACE # 控制台输出 #用到的类 log4j.appender.stdout=org.apache.log4j.ConsoleAppender #打印的格式 log4j.appender.stdout.layout=org.apache.log4j.PatternLayout #具体的格式指定 log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n
日志记录器(Logger)的行为是分等级的:
1.分为OFF、FATAL【致命错误】、ERROR、WARN、INFO【普通运行信息】、DEBUG【断点调试】、TRACE【跟踪信息】、ALL或者您定义的级别。2.Log4j建议只使用四个级别,优先级从高到低分别是ERROR、WARN、INFO、DEBUG。
3.如果log level设置在某一个级别上,那么比此级别优先级高的log都能打印出来,就拿我们常用的4个等级ERROR、WARN、INFO、DEBUG,如果我们设置在INFO上,那程序中所有DEBUG级别的日志将不会打印。
常用几个等级的说明:
1、DEBUG 指定细粒度信息事件是最有用的应用程序调试,一般使用log.debug()进行跟踪调试。2、INFO 指定能够突出在粗粒度级别的应用程序运行情况的信息的消息,就是输出提示信息。info级别监控系统运行情况,可以帮助程序员有效的了解程序的流转。
3、WARN 指定具有潜在危害的情况,一般很少使用。
4、ERROR 错误事件可能仍然允许应用程序继续运行。就是显示错误信息。比如接口访问超时,用try/catch 捕获异常,发生异常的时候log.error输出错误信息,并不影响程序的运行。
-
上述配置将使 Log4J 详细打印
org.mybatis.example.BlogMapper【详细的包】
的日志,对于应用的其它部分,只打印错误信息。为了实现更细粒度的日志输出,你也可以只打印特定语句的日志。以下配置将只打印语句
selectBlog
的日志:log4j.logger.org.mybatis.example.BlogMapper.selectBlog=TRACE
-
或者,你也可以打印一组映射器的日志,只需要打开映射器所在的包的日志功能即可:
log4j.logger.org.mybatis.example=TRACE
某些查询可能会返回庞大的结果集。这时,你可能只想查看 SQL 语句,而忽略返回的结果集。为此,SQL 语句将会在 DEBUG 日志级别下记录(JDK 日志则为 FINE)。返回的结果集则会在 TRACE 日志级别下记录(JDK 日志则为 FINER)。因此,只要将日志级别调整为 DEBUG 即可:
log4j.logger.org.mybatis.example=DEBUG
使用和指定语句的参数和返回值相匹配的接口(比如 UserMapper.class),现在你的代码不仅更清晰,更加类型安全,还不用担心可能出错的字符串字面值以及强制类型转换。
1.2 MyBatis接口开发(代理开发)【较为重要】
使用XML文件进行开发,在调用SqlSession进行操作时,需要指定MyBatis映射文件中的方法,这种调用方式过于烦琐。为解决此问题,MyBatis提供了接口开发的方式。
接口开发的目的:
-
解决原生方式中的硬编码
-
简化后期执行SQL
需修改的地方:
1.在mapper文件夹下创建XxxMapper接口,并定义相应的抽象方法。
2.在mapper文件夹下创建映射文件XxxMapper.xml,并指定其namespace为对应Mapper接口的绝对路径。
3.在MyBatis主配置文件中,将mapper包下所有的Mapper接口引入
<mappers>
<!--<mapper resource="com/ambow/mapper/UserMapper.xml"/>-->
<package name="com.foxbill.mapper">
<mappers>
4.在pom.xml中配置resource,指定打包资源,使mapper包中的映射文件可以被打包到classes中【另一种方式:也可以不做如下配置,而是把映射文件,放在resources对应的文件夹中】
<build>
<!--加入 resource 插件-->
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.xml</include>
</includes>
</resource>
</resources>
</build>
1. 实现步骤:
1.创建Mapper接口
package com.ambow.mapper;
import com.ambow.pojo.User;
import java.util.List;
public interface UserMapper {
public List<User> selectUser();
public List<User> searchUser(String keywords);
public int insertUser(User user);
public int updateUser(User user);
public int deleteUser(int id);
}
2.修改mapper.xml文件中的namespace,放到mapper文件夹
<?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">
<!--MyBatis接口开发,需要修改namespace-->
<mapper namespace="com.ambow.mapper.UserMapper">
<select id="selectUser" resultType="com.ambow.pojo.User">
select * from tb_user
</select>
<select id="searchUser" resultType="com.ambow.pojo.User">
select * from tb_user where username like '%${_parameter}%'
</select>
<!--新增用户-->
<insert id="insertUser" parameterType="com.ambow.pojo.User">
insert into tb_user values(null,#{username},#{password},#{gender},#{addr})
</insert>
<update id="updateUser" parameterType="com.ambow.pojo.User">
update tb_user set username = #{username},password = #{password}, gender = #{gender},addr = #{addr}
where id = #{id}
</update>
<delete id="deleteUser">
delete from tb_user where id = #{id}
</delete>
</mapper>
3. 修改主配置文件中映射文件的路径
<mappers>
<!--<mapper resource="com/ambow/mapper/UserMapper.xml"/>-->
<package name="com.ambow.mapper"/>
</mappers>
4.测试接口开发
@Test
public void test() throws IOException {
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
//获取SqlSessionFactory - 工厂对象
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
// System.out.println(sqlSessionFactory);
//获取SqlSession - 连接对象
SqlSession sqlSession = sqlSessionFactory.openSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
List<User> list = userMapper.selectUser();
for (User user : list) {
System.out.println(user);
}
}
2. MyBatis动态代理原理
3. MyBatisX 插件
MybatisX 是一款基于 IDEA 的快速开发插件,为效率而生。
主要功能:
-
XML 和 接口方法 相互跳转
-
根据接口方法生成 statement
安装:
1.3 MyBatis核心配置
0.核心配置文件概览
MyBatis配置文件中有MyBatis框架的核心配置,负责对MyBatis进行全局管理。它包含许多控制MyBatis功能的重要元素。
<configuration>
<!--设置配置文件-->
<properties>
<property name="" value=""/>
</properties>
<!--MyBatis设置-->
<settings>
<setting name="" value=""/>
</settings>
<!--包名简化缩进-->
<typeAliases>
<typeAlias type=""/>
</typeAliases>
<!--配置数据类型转换-->
<typeHandlers>
<typeHandler handler=""/>
</typeHandlers>
<!--自定义结果集对象-->
<objectFactory type="">
</objectFactory>
<!--配置插件-->
<plugins>
<plugin interceptor="">
</plugin>
</plugins>
<!--配置环境-->
<environments default="">
<!--配置mysql环境-->
<environment id="">
<!--配置事务管理器-->
<transactionManager type=""/>
<!--配置数据库连接-->
<dataSource type="">
<!--配置数据库连接驱动-->
<property name="" value=""/>
<!--配置数据库连接地址-->
<property name="" value=""/>
<!--配置用户名-->
<property name="" value=""/>
<!--配置密码-->
<property name="" value=""/>
</dataSource>
</environment>
</environments>
<!--数据厂商标识-->
<databaseIdProvider type="">
</databaseIdProvider>
<!--配置mapper映射文件-->
<mappers>
<mapper
resource="com/mapper/DogMapper.xml"/>
</mappers>
</configuration>
常见配置:
1.properties元素
第一种方式:
<properties>
<property name="driver" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://localhost:3306/jdbc" />
<property name="username" value="root" />
<property name="password" value="root" />
</properties>
第二种方式:
<properties resource="jdbc.properties" />
在resources文件夹里面配置配置文件,就可以就直接访问
对应的jdbc.properties源码如下:
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/jdbc
username=root
password=root
属性值的引用方式为${属性名},具体代码参考如下:
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
</environments>
2.typeAliases
<typeAliases>
<!--给单个实体类配置别名-->
<!--<typeAlias type="com.ambow.pojo.Student" alias="Student" />-->
<!--给多个实体类配置别名,默认的别名就是类名【不区分大小写 Student、student】-->
<package name="com.ambow.pojo" />
</typeAliases>
3.mappers
<mappers>
<!-- 1.使用相对于类路径的资源引用 -->
<!--<mapper resource="com/ambow/dao/StudentMapper.xml"/>-->
<!-- 2.使用完全限定资源定位符(URL) -->
<!--<mapper url="file:///E:\workspace\LearnMyBatis\mybatis03\src\main\java\com\ambow\dao\StudentMapper.xml" />-->
<!-- 3.使用映射器接口实现类的完全限定类名 -->
<!--<mapper class="com.ambow.dao.StudentMapper" />-->
<!-- 4.将包内的映射器接口实现全部注册为映射器【推荐】 -->
<package name="com.ambow.dao" />
</mappers>
4.settings
了解一下即可,可以指定日志
<settings>
<setting name="logImpl" value="LOG4J"/>
</settings>
Mybatis 通过使用内置的日志工厂提供日志功能。内置日志工厂将会把日志工作委托给下面的实现之一:
-
SLF4J
-
Apache Commons Logging
-
Log4j 2
-
Log4j
-
JDK logging
MyBatis 内置日志工厂会基于运行时检测信息选择日志委托实现。它会(按上面罗列的顺序)使用第一个查找到的实现。当没有找到这些实现时,将会禁用日志功能。
如果你想选择某种日志实现,你可以通过上面的setting来指定。
1.4 MyBatis映射文件
映射文件是MyBatis中的重要组成部分,它包含了开发中编写的SQL语句、参数、结果集等。映射文件需要通过MyBatis配置文件中的<mapper>元素引入才能生效。MyBatis规定了映射文件的层次结构。
1.映射文件概述
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="MyDog">
<!--开启此映射文件的缓存-->
<cache/>
<!--指定引用的命名空间,当此命名空间执行DML操作修改时,
被引用的命名空间中的缓存也会失效-->
<cache-ref namespace=""/>
<!--参数映射集-->
<parameterMap id="" type="">
<parameter property="" jdbcType="" javaType="" typeHandler=""/>
</parameterMap>
<!--sql块-->
<sql id=""></sql>
<!--映射结果集-->
<resultMap id="" type="">
<id property="" column=""/>
<result property="" column="" />
</resultMap>
<!--查询元素-->
<select id="" resultType="" parameterType=""></select>
<!--新增元素-->
<insert id="" parameterType=""></insert>
<!--删除元素-->
<delete id=""></delete>
<!--更新元素-->
<update id="" parameterType=""></update>
</mapper>
< select >元素的常用属性:【作为了解内容,后续开发的时候直接查即可】
<sql>元素:
通过<sql>元素来包裹常用的列名,当需要使用此列名时,可以利用<include>元素的refid属性来指定。在需要修改列名时,通过修改<sql>元素即可实现全文件的列名修改。
<sql>元素设计的初衷在于简化列名的书写,在一个命名空间内,重复的列名将会给维护带来很大的麻烦,因此,MyBatis使用<sql>元素来包含这些列名,使其得到重用。
2.模糊查询总结
写法为:
1、使用“${...}”,语法为“like '${...}'”; -->and sName LIKE '%${sName}%'
<select id="query" resultType="student">
SELECT * FROM students
<where>
<if test="null != sId and '' != sId">
and sId = #{sId}
</if>
<if test="null != sName and '' != sName">
and sName LIKE '%${sName}%'
</if>
<if test="null != sAge and '' != sAge">
and sAge = #{sAge}
</if>
</where>
</select>
2、使用“#{...}”,语法为“like '#{...}'”; -->and sName LIKE "%"#{sName}"%"
<select id="query" resultType="student">
SELECT * FROM students
<where>
<if test="null != sId and '' != sId">
and sId = #{sId}
</if>
<if test="null != sName and '' != sName">
and sName LIKE "%"#{sName}"%"
</if>
<if test="null != sAge and '' != sAge">
and sAge = #{sAge}
</if>
</where>
</select>
3、使用[CONCAT]函数连接参数形式,语法为“like CONCAT('%',#{...},'%')”。-->and sName LIKE concat("%",#{sName},"%")
<select id="query" resultType="student">
SELECT * FROM students
<where>
<if test="null != sId and '' != sId">
and sId = #{sId}
</if>
<if test="null != sName and '' != sName">
and sName LIKE concat("%",#{sName},"%")
</if>
<if test="null != sAge and '' != sAge">
and sAge = #{sAge}
</if>
</where>
</select>
4、使用<bind> --><bind name="bindName" value="'%'+sName+'%'" />
<select id="query" resultType="student">
<!--命名元素-->
<bind name="bindName" value="'%'+sName+'%'" />
SELECT * FROM students
<where>
<if test="null != sId and '' != sId">
and sId = #{sId}
</if>
<if test="null != sName and '' != sName">
and sName LIKE #{bindName}
</if>
<if test="null != sAge and '' != sAge">
and sAge = #{sAge}
</if>
</where>
</select>
参考网址:
mybatis中LIKE模糊查询的几种写法以及注意点_mybatis 模糊查询_槐月十九的博客-CSDN博客
#{}和${}的区别:
“#{}”表示占位符,在组成SQL的过程中,先将此位置占位,之后将取得的值赋值到此位置,在类型上有严格的限制。【相当于用PreparedStatement,不会有SQL注入风险】
“${}”表示拼接符,在组成SQL的过程中,此符号将取得的值直接拼接到相应位置。【相当于用Statement,拼接SQL,有SQL注入风险】
1.5 MyBatis缓存
为了减少重复查询给数据库带来的压力,MyBatis提供了缓存机制,这种机制能够缓存查询的结果,避免重复的查询。
MyBatis提供了两种缓存方式,一种为针对于SqlSession的缓存,此种缓存方式默认开启;另一种为针对于全局的缓存,需要手动开启。一级缓存存在SqlSession对象中,二级缓存横跨全部的SqlSession,对所有的查询都生效。
1.一级缓存(本地缓存)【重点了解一级缓存】
在没有配置的情况下,MyBatis默认开启一级缓存。在实际开发时,使用同一个SqlSession对象调用同一个Mapper方法,往往只执行一次SQL,这是因为,当开启一级缓存时,第一次查询,MyBatis会将查询结果放在缓存中,当再次使用这个SqlSession进行同一个查询时,如果数据库的数据没有被更改,则直接将缓存中的数据返回,不会再次发送SQL到数据库。
1.用户发送查询请求给MyBatis,MyBatis接收到请求时创建一个SqlSession对象处理本次请求的数据库操作,每个SqlSession对象有对应的执行器,执行器在执行SQL语句时会查询Local Cache中是否存在此查询的缓存,如果不存在,则执行此次查询,并将缓存放到Local Cache中;如果存在,则直接将此次查询的缓存返回。
2.当会话结束,即调用SqlSession的close()方法时,会释放此SqlSession中的所有缓存,并将此SqlSession禁用。如果想要清除缓存中的数据,而不关闭SqlSession对象,可以调用SqlSession的clearCache()方法,此方法会清空该SqlSession一级缓存中的所有内容。除此之外,当SqlSession中执行任何一个DML操作,即增加、删除或更改操作时,都将清空此SqlSession的一级缓存
在MyBatis中,对于两次查询,有以下四个条件来判定它们是否是完全相同的两次查询。 1)传入的statementId是否相同 2)查询时结果集范围是否相同 3)查询的最终SQL语句是否相同 4)传递给Statement的参数是否相同 当这些判断都相同时,认为这两次查询完全相同。
如果想要清除缓存中的数据,而不关闭SqlSession对象,可以调用SqlSession的clearCache()方法,此方法会清空该SqlSession一级缓存中的所有内容。除此之外,当SqlSession中执行任何一个DML操作,即增加、删除或更改操作时,都将清空此SqlSession的一级缓存
演示一级缓存的案例,实现步骤:
1. 添加MyBatis的依赖
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<java.version>1.8</java.version>
</properties>
<dependencies>
<!-- mybatis的依赖 -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.6</version>
</dependency>
<!-- mysql-connector-java 的依赖 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.6</version>
</dependency>
<!-- lombok 的依赖-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.24</version>
<!-- <scope>provided</scope>-->
</dependency>
<!-- junit的依赖 -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.xml</include>
</includes>
</resource>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*</include>
</includes>
</resource>
</resources>
</build>
2.添加MyBatis的核心配置文件
<?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">
<!--MyBatis的核心配置文件-->
<configuration>
<properties resource="jdbc.properties" />
<settings>
<!--开启数据库日志检测-->
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
<!--类型别名-->
<typeAliases>
<package name="com.ambow.pojo" />
</typeAliases>
<!--配置环境-->
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
</environments>
<!--配置映射器-->
<mappers>
<!-- 4.将包内的映射器接口实现全部注册为映射器【推荐】 -->
<package name="com.ambow.dao" />
</mappers>
</configuration>
核心配置文件,需要读取的jdbc.properties文件:
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/jdbc
username=root
password=root
3.创建POJO - 基于Lombok
package com.ambow.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Dog {
private int id;
private String name;
private int age;
}
4.创建Mapper接口
//DogMapper.java
package com.ambow.dao;
import com.ambow.pojo.Dog;
public interface DogMapper {
Dog selectDog();
}
5.创建Mapper映射文件
<?xml version="1.0" encoding="UTF-8" ?>
<!--DogMapper.xml-->
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--MyBatis接口开发,需要修改namespace-->
<mapper namespace="com.ambow.dao.DogMapper">
<select id="selectDog" resultType="dog">
SELECT * from dog where id = 1
</select>
</mapper>
6.创建MybatisUtil工具类
package com.ambow.util;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import java.io.IOException;
import java.io.InputStream;
public class MyBatisUtil {
//获取SqlSession
public static SqlSession getSqlSesssion(){
//获取SqlSession
String resource = "mybatis-config.xml";
InputStream inputStream = null;
try {
inputStream = Resources.getResourceAsStream(resource);
} catch (IOException e) {
e.printStackTrace();
}
//获取SqlSessionFactory - 工厂对象
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
// System.out.println(sqlSessionFactory);
//获取SqlSession - 连接对象
SqlSession sqlSession = sqlSessionFactory.openSession();
return sqlSession;
}
//关闭SqlSession
public static void closeSqlSession(SqlSession session){
if (session != null) {
session.close();
}
}
}
7.测试一级缓存
package com.ambow.test;
import com.ambow.dao.DogMapper;
import com.ambow.pojo.Dog;
import com.ambow.util.MyBatisUtil;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;
public class CacheTest {
/*
一级缓存:
SqlSession级别的缓存,也就是说,同一个SqlSession共用一个缓存对象
*/
@Test
public void test01(){
//获取SqlSession
SqlSession sqlSesssion01 = MyBatisUtil.getSqlSesssion();
//第一次查询 - id为1
DogMapper dogMapper01 = sqlSesssion01.getMapper(DogMapper.class);
Dog dog1 = dogMapper01.selectDog();
System.out.println(dog1);
//第二次查询 - id为1
DogMapper dogMapper02 = sqlSesssion01.getMapper(DogMapper.class);
Dog dog2 = dogMapper02.selectDog();
System.out.println(dog2);
}
/*
一级缓存:
两个SqlSession对象,不会共用一个缓存对象
*/
@Test
public void test02(){
//获取SqlSession
SqlSession sqlSesssion01 = MyBatisUtil.getSqlSesssion();
SqlSession sqlSesssion02 = MyBatisUtil.getSqlSesssion();
//第一次查询 - id为1
DogMapper dogMapper01 = sqlSesssion01.getMapper(DogMapper.class);
Dog dog1 = dogMapper01.selectDog();
System.out.println(dog1);
//第二次查询 - id为1
DogMapper dogMapper02 = sqlSesssion02.getMapper(DogMapper.class);
Dog dog2 = dogMapper02.selectDog();
System.out.println(dog2);
}
/*
一级缓存:
SqlSession调用close()方法,缓存会被释放
*/
@Test
public void test03(){
//获取SqlSession
SqlSession sqlSesssion01 = MyBatisUtil.getSqlSesssion();
//第一次查询 - id为1
DogMapper dogMapper01 = sqlSesssion01.getMapper(DogMapper.class);
Dog dog1 = dogMapper01.selectDog();
System.out.println(dog1);
sqlSesssion01.close();
//第二次查询 - id为1
DogMapper dogMapper02 = sqlSesssion01.getMapper(DogMapper.class);
Dog dog2 = dogMapper02.selectDog();
System.out.println(dog2);
}
/*
一级缓存:
调用SqlSession的clearCache()方法,可以释放缓存
*/
@Test
public void test04(){
//获取SqlSession
SqlSession sqlSesssion01 = MyBatisUtil.getSqlSesssion();
//第一次查询 - id为1
DogMapper dogMapper01 = sqlSesssion01.getMapper(DogMapper.class);
Dog dog1 = dogMapper01.selectDog();
System.out.println(dog1);
//清除缓存
sqlSesssion01.clearCache();
//第二次查询 - id为1
DogMapper dogMapper02 = sqlSesssion01.getMapper(DogMapper.class);
Dog dog2 = dogMapper02.selectDog();
System.out.println(dog2);
}
/*
一级缓存:
当SqlSession中执行任何一个DML操作,即增加、删除或更改操作时,都将清空此SqlSession的一级缓存
*/
@Test
public void test05(){
//获取SqlSession
SqlSession sqlSesssion01 = MyBatisUtil.getSqlSesssion();
//第一次查询 - id为1
DogMapper dogMapper01 = sqlSesssion01.getMapper(DogMapper.class);
Dog dog1 = dogMapper01.selectDog();
System.out.println(dog1);
//执行DML操作 - 数据更新
int row = dogMapper01.updateDog();
System.out.println("执行了更新语句");
//第二次查询 - id为1
DogMapper dogMapper02 = sqlSesssion01.getMapper(DogMapper.class);
Dog dog2 = dogMapper02.selectDog();
System.out.println(dog2);
}
}
2.二级缓存(全局缓存)
MyBatis的二级缓存是Application级别的缓存,与一级缓存的原理类似,不同的是,二级缓存的作用域扩大到了每个命名空间,在同一个命名空间中的所有查询都将被缓存。
MyBatis二级缓存的执行流程:
1.MyBatis中的二级缓存默认关闭,需要手动开启,当开启后,用户发送有关数据库操作的请求会被CacheExecutor拦截。
2.CacheExecutor拦截数据库操作后,到Configuration对象中查看对应命名空间中的缓存,如果发现存在相同查询的缓存,则直接返回该缓存;如果不存在,则进入一级缓存中查找。即先经过二级缓存查找后,再从一级缓存中寻找。
MyBatis在执行到DML语句时,会清空当前命名空间中所有的缓存。此外,MyBatis开启二级缓存后可能会有脏读问题:按照开发规范,每个类都有自己的命名空间,命名空间不允许有针对其他类的更改,但如果在B类的命名空间中对A类做出更改时,B类命名空间中的二级缓存将会被清除,A类中的缓存不会被清除,当A类命名空间中有针对于A类的查询操作时,就会寻找二级缓存中的旧数据并将其返回。
演示案例
演示1:不开启二级缓存,一级缓存无法实现跨SqlSession之间的缓存。
演示2:开启二级缓存,可以实现跨SqlSession的缓存。
<!--设置 -->
<settings>
<!--缓存二级配置的全局开关-->
<setting name="cacheEnabled" value="true" />
<!--开启数据库日志检测-->
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
使用MyBatis的二级缓存,需要以下几步:
-
在主配置文件中开启全局二级缓存配置
<setting name="cacheEnabled" value="true">
-
在映射文件中加入<cache />标签
-
对应的pojo需要实现序列化
-
注意:测试二级缓存需要commit提交,如果不提交是不会保存到二级缓存的
演示3:执行DML操作后,二级缓存会清空。
演示4:在不规范开发时,二级缓存会出现脏读情况。
-
按照开发规范,每个类都有自己的命名空间,命名空间不允许有针对其他类的更改,但如果在B类的命名空间中对A类做出更改时,B类命名空间中的二级缓存将会被清除,A类中的缓存不会被清除,当A类命名空间中有针对于A类的查询操作时,就会寻找二级缓存中的旧数据并将其返回。
面试题:说一说MyBatis的缓存机制?
首先MyBatis设计了二级缓存这样一个机制来提升数据检索效率,避免每一次检索都去查询数据库。
一级缓存是SqlSession级别的缓存,也叫本地缓存。因为每一个用户在执行查询的时候,都需要使用SqlSession来执行,为了避免每一次都去查询数据库,MyBatis把查询出来的数据缓存到SqlSession的本地缓存里面,后续的SQL查询,如果命中缓存的情况下,就可以直接从本地缓存去读取数据。
如果要实现跨SqlSession级别的缓存,那么一级缓存无法做到,因此MyBatis引入了二级缓存的设计。当多个用户查询数据的时候,只要有任何一个SqlSession拿到了数据,就会放到二级缓存里面,其他SqlSession就可以直接从二级缓存里面加载数据。
下面我再来解释一下一二级缓存的实现原理,首先看一级缓存,在SqlSession里面会持有一个Executor,每个Executor里面会有一个LocalCache的对象,当用户发起查询的时候,MyBatis会根据执行语句,在LocalCache里面去查找,如果命中了就只把数据返回,如果没有命中,再去数据库中查找,再写入到LocalCache里面,所以,一级缓存的生命周期是SqlSession.需要注意一点:在多个SqlSession或者分布式环境下,可能会因为一级缓存导致脏读的问题。
而二级缓存的实现原理呢,是在原来的Executor上去做了一个装饰,引入了叫CachingExecutor的装饰器,在进入一级缓存的查询之前,会先通过CachingExecutor进行二级缓存的查询。开启二级缓存之后,会被多个SqlSession共享,因此它是一个全局的缓存,所以它的查询流程就变成了,先去查询二级缓存,再去查询一级缓存,最后再去查数据库。另外,二级缓存相比一级缓存,实现了SqlSession之间的缓存数据的共享,同时缓存粒度可以控制到namespace级别,并且还可以通过Cache接口来实现不同缓存实现类的一个组合,对Cache的可控度也更高了。
注意:
第一版:MyBatisUtil.java
package com.ambow.util;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import java.io.IOException;
import java.io.InputStream;
public class MyBatisUtil {
//获取SqlSession
public static SqlSession getSqlSesssion(){
//获取SqlSession
String resource = "mybatis-config.xml";
InputStream inputStream = null;
try {
inputStream = Resources.getResourceAsStream(resource);
} catch (IOException e) {
e.printStackTrace();
}
//获取SqlSessionFactory - 工厂对象
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
// System.out.println(sqlSessionFactory);
//获取SqlSession - 连接对象
SqlSession sqlSession = sqlSessionFactory.openSession();
return sqlSession;
}
//关闭SqlSession
public static void closeSqlSession(SqlSession session){
if (session != null) {
session.close();
}
}
}
第二版:MyBatisUtil.java
package com.ambow.util;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import java.io.IOException;
import java.io.InputStream;
public class MyBatisUtil {
private static SqlSessionFactoryBuilder builder;
private static SqlSessionFactory sqlSessionFactory;
//静态代码块 - 类加载的时候,只执行一次
static {
//获取SqlSession
String resource = "mybatis-config.xml";
InputStream inputStream = null;
try {
inputStream = Resources.getResourceAsStream(resource);
} catch (IOException e) {
e.printStackTrace();
}
builder = new SqlSessionFactoryBuilder();
//获取SqlSessionFactory - 工厂对象
sqlSessionFactory = builder.build(inputStream);
}
//获取SqlSession
public static SqlSession getSqlSesssion(){
//获取SqlSession - 连接对象
SqlSession sqlSession = sqlSessionFactory.openSession();
return sqlSession;
}
//关闭SqlSession
public static void closeSqlSession(SqlSession session){
if (session != null) {
session.close();
}
}
public static SqlSessionFactoryBuilder getBuilder(){
return builder;
}
public static SqlSessionFactory getSqlSessionFactory(){
return sqlSessionFactory;
}
}
第二版才能共用一个SqlSessionFactory,而第一版拿到的是两个。
总结
以上就是今天的内容~
欢迎大家点赞👍,收藏⭐,转发🚀,
如有问题、建议,请您在评论区留言💬哦。
最后:转载请注明出处!!!