MyBatis学习
备注:官方文档
本篇文章通过:b站的狂神说进行学习
初识MyBatis
第一个程序(简单的实现增删改查):
-
工程目录
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9urGmvHF-1602072233304)(F:/文档/typora文档/mybatis/img/屏幕截图 2020-09-25 090027.jpg)]
-
代码
-
配置文件
<?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="development"> <!-- 定义环境id--> <environment id="development"> <!-- 事务管理器的配置 JDBC – 这个配置直接使用了 JDBC 的提交和回滚设施,它依赖从数据源获得的连接来管理事务作用域。 MANAGED(了解) – 这个配置几乎没做什么。(过时了没有)--> <transactionManager type="JDBC"/> <!-- 数据源的配置 UNPOOLED(了解)– 这个数据源的实现会每次请求时打开和关闭连接。(没有池的概念) POOLED– 这种数据源的实现利用“池”的概念将 JDBC 连接对象组织起来,避免了创建新的连接实例时所必需的初始化和认证时间 备注:数据连接池就是用完可以回收 JNDI(了解) – 这个数据源实现是为了能在如 EJB 或应用服务器这类容器中使用,--> <dataSource type="POOLED"> <property name="driver" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=false&useUnicode=true&characterEncoding=utf8"/> <property name="username" value="root"/> <property name="password" value="root"/> </dataSource> </environment> </environments> <!--映射器--> <mappers> <mapper resource="com/minjiang/UserMapper.xml"/> </mappers> </configuration>
-
接口
public interface UserMapper { /**查询所有 * * @return */ public List<User> selectAll(); /** * 条件查询 * @param id * @return */ public User selectById(int id); /** * 增加用户 * @param user * @return */ public int addUser(User user); /** * 修改用户 * @param usr * @return */ public int updateUser(User usr); /** * 根据id删除用户 * @param id * @return */ public int deleteUser(int id); }
- 实体类
public class User { private int id; private String name; private String pwd; public User(){ } public User(int id, String name, String pwd){ this.id = id; this.name = name; this.pwd =pwd; } public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getPwd() { return pwd; } public void setPwd(String pwd) { this.pwd = pwd; } @Override public String toString() { return "User{" + "id=" + id + ", name='" + name + '\'' + ", pwd='" + pwd + '\'' + '}'; } }
- 工具类
/** * 工具类: * 每个基于 MyBatis 的应用都是以一个 SqlSessionFactory 的实例为核心的 */ public class MybatisUtils { private static String resource = "mybatis-config.xml"; /** * 保证只有一个 */ private static InputStream inputStream; private static SqlSessionFactory sqlSessionFactory; /** * 保证可以直接调用,静态代码块只能用静态类 */ static { try { inputStream = Resources.getResourceAsStream(resource); sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); } catch (IOException e) { e.printStackTrace(); } } /**SqlSessionFactory,可以从中获得 SqlSession 的实例。SqlSession 提供了在数据库执行 SQL 命令所需的所有方法 * * @return */ public static SqlSession getSession(){ SqlSession session = sqlSessionFactory.openSession(); return session; } }
- 映射文件
<?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"> <mapper namespace="com.minjiang.mapper.UserMapper"> <select id="selectAll" resultType="com.minjiang.projo.User"> select * from mybatis.user </select> <select id="selectById" resultType="com.minjiang.projo.User" parameterType="com.minjiang.projo.User"> select * from mybatis.user where id = #{id} </select> <insert id="addUser" parameterType="com.minjiang.projo.User"> insert into mybatis.user value (#{id}, #{name}, #{pwd}) </insert> <update id="updateUser" parameterType="com.minjiang.projo.User"> update mybatis.user set name = #{name}, pwd = #{pwd} where id = #{id} </update> <delete id="deleteUser" parameterType="com.minjiang.projo.User"> delete from mybatis.user where id = #{id} </delete> </mapper>
- 测试文件
public class MyBtaisTest { SqlSession session = MybatisUtils.getSession(); UserMapper mapper = session.getMapper(UserMapper.class); @Test public void selectAllTest(){ // 方法1 // List<User> users = session.selectList("com.minjiang.mapper.UserMapper.selectAll"); //方法2(推荐) List<User> users = mapper.selectAll(); for (User user : users){ System.out.println(user); } session.close(); } @Test public void selectById(){ User user =mapper.selectById(1); System.out.println(user); session.close(); } @Test public void addUser(){ int i = mapper.addUser(new User(5, "小红", "456")); if (i > 0){ System.out.println("添加成功"); } //提交事务 session.commit(); session.close(); } @Test public void updateUser(){ User user = mapper.selectById(3); user.setName("小黑"); int i = mapper.updateUser(user); if (i > 0){ System.out.println("修改成功"); } //提交事务 session.commit(); session.close(); } @Test public void deleteUser(){ int i =mapper.deleteUser(5); if (i > 0){ System.out.println("删除成功"); } //提交事务 session.commit(); session.close(); } }
注意点:
增删改操作都需要提交事务
-
mybatis的便利使用(mapper)
备注:可以给用于参数过多使用,当然一直使用这种方式是极好的
查询方法
接口:
/**
* 直接通过键值对的方式(这里的对象名可以随意取)
* 参数直接传Map
* @param map
* @return
*/
public User selectUserByNP2(Map<String,Object> map);
映射文件:
<!-- 传递参数为map-->
<select id="selectUserByNP2" resultType="com.minjiang.projo.User" parameterType="map">
select * from user where name = #{username} and pwd = #{password}
</select>
测试文件:
@Test
public void selectUserByNP2(){
//创建一个会话
SqlSession session = MybatisUtils.getSession();
//通过会话创获取一个mapper(映射)对象
UserMapper mapper = session.getMapper(UserMapper.class);
Map<String,Object> map = new HashMap<String, Object>();
map.put("username","小白");
map.put("password","456");
//映射文件中中包含着映射出来的方法
User user = mapper.selectUserByNP2(map);
System.out.println(user);
//提交事务
session.commit();
//关闭事务
session.close();
}
添加方法
接口
/**
* 通过键值对进行增加
*/
public int addUserByNP2(Map<String,Object> map);
映射文件
测试
@Test
public void addUserByNP2(){
//创建一个会话
SqlSession session = MybatisUtils.getSession();
//通过会话创获取一个mapper(映射)对象
UserMapper mapper = session.getMapper(UserMapper.class);
Map<String,Object> map = new HashMap<String, Object>();
map.put("id",3);
map.put("userName","红蓝CP");
int i = mapper.addUserByNP2(map);
if (i > 0){
System.out.println("添加成功");
}
//提交事务
session.commit();
//关闭事务
session.close();
}
<!-- 传递参数为map-->
<insert id="addUserByNP2" parameterType="map">
insert into user(id,name) value (#{id},#{userName})
</insert>
好处:
无需要去改动实体类的东西,提高了效率
模糊查询
- 接口类:
/**
* 模糊查询
* @param name
* @return
*/
public List<User> selectLike(String name);
- 映射文件
<select id="selectLike" parameterType="String" resultType="com.minjiang.projo.User">
select * from mybatis.user where name like #{id}
</select>
- 测试类:
@Test
public void selectLike(){
//创建一个会话
SqlSession session = MybatisUtils.getSession();
//通过会话创获取一个mapper(映射)对象
UserMapper mapper = session.getMapper(UserMapper.class);
//映射文件中中包含着映射出来的方法
List<User> users = mapper.selectLike("%白%");
for (User user : users){
System.out.println(user);
}
//提交事务
session.commit();
//关闭事务
session.close();
}
配置解析
配置展示(备注:这也是mybaits的核心配置文件)
<?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="development">
<!-- 定义环境id-->
<environment id="development">
<!-- 事务管理器的配置
JDBC – 这个配置直接使用了 JDBC 的提交和回滚设施,它依赖从数据源获得的连接来管理事务作用域。
MANAGED(了解) – 这个配置几乎没做什么。(过时了没有)-->
<transactionManager type="JDBC"/>
<!-- 数据源的配置
UNPOOLED(了解)– 这个数据源的实现会每次请求时打开和关闭连接。(没有池的概念)
POOLED– 这种数据源的实现利用“池”的概念将 JDBC 连接对象组织起来,避免了创建新的连接实例时所必需的初始化和认证时间
备注:数据连接池就是用完可以回收
JNDI(了解) – 这个数据源实现是为了能在如 EJB 或应用服务器这类容器中使用,-->
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=false&useUnicode=true&characterEncoding=utf8"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</dataSource>
</environment>
</environments>
<!--映射器-->
<mappers>
<mapper resource="com/minjiang/UserMapper.xml"/>
</mappers>
</configuration>
MyBatis 的配置文件包含了会深深影响 MyBatis 行为的设置和属性信息。 配置文档的顶层结构如下:
- configuration(配置)
- properties(属性)
- settings(设置)
- typeAliases(类型别名)
- typeHandlers(类型处理器)
- objectFactory(对象工厂)
- plugins(插件)
- environments(环境配置)
- environment(环境变量)
- transactionManager(事务管理器)
- dataSource(数据源)
- environment(环境变量)
- databaseIdProvider(数据库厂商标识)
- mappers(映射器)
重点:注意配置文档的顶层结构顺序,顺序不对会报错
环境配置
mybatis可以配置成适应多种环境
<!-- 默认使用环境-->
<environments default="development">
<!-- 定义环境(development)-->
<environment id="development"...>
</environment>
<environment id="test"...>
</environment>
</environments>
切换成不同的环境
<!-- 默认使用环境,可以用于切换,切换成相对应的id就可以了-->
<environments default="development">
属性设置
另一种配置方式(配置文件的改变==》请根据配置文档的顶层结构顺序配置):
<properties>
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value=" jdbc:mysql://localhost:3306/mybatis?useSSL=false&useUnicode=true&characterEncoding=utf8"/>
<property name="userName" value="root"/>
<property name="password" value="root"/>
</properties>
<dataSource type="POOLED">
<!-- 可以使用${XXX}进行调用-->
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${userName}"/>
<property name="password" value="${password}"/>
</dataSource>
备注:详细看官方文档
简要概括要点:
- 事务管理器(transactionManager):JDBC、MANAGED(了解)
JDBC – 这个配置直接使用了 JDBC 的提交和回滚设施,它依赖从数据源获得的连接来管理事务作
MANAGED(了解) – 这个配置几乎没做什么。(过时了没有)
-
数据源(dataSource):UNPOOLED(了解)、POOLED(关注点)、JNDI(了解)
UNPOOLED(了解)– 这个数据源的实现会每次请求时打开和关闭连接。(没有池的概念)
POOLED– 这种数据源的实现利用“池”的概念将 JDBC 连接对象组织起来,避免了创建新的连接实例时所必需的初始化和认证时间
备注:数据连接池就是用完可以回收
JNDI(了解) – 这个数据源实现是为了能在如 EJB 或应用服务器这类容器中使用,
配置设置
太多了。。。看官方文档吧(条件:只要有遵守文档的顶层结构顺序配置可以了)
配置文件中的mappers元素
mappers(映射器):定义SQL语句文件
映射文件文件的引用方法
方法1(推荐)
<!--映射器-->
<mappers>
<mapper resource="com/minjiang/UserMapper.xml"/>
</mappers>
方法2
<!--映射器-->
<mappers>
<!-- <mapper resource="com/minjiang/UserMapper.xml"/>-->
<mapper class="com.minjiang.mapper.UserMapper"></mapper>
</mappers>
注意点:用类去注册
映射文件和接口要放在同一目录下,同时文件要相同
方法3:
<!--映射器-->
<mappers>
<!-- <mapper resource="com/minjiang/UserMapper.xml"/>-->
<package name="com.minjiang.mapper"/>
</mappers>
注意点:用类去注册
映射文件和接口要放在同一目录下,同时文件要相同
标注:typeHandlers(类型处理器),objectFactory(对象工厂),plugins(插件)==》推荐(mybatis-Plus)了解即可
优化代码
优化1.0
可以通过引入外部配置简化外部文件
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MudAE6Ka-1602072233306)(img/屏幕截图 2020-09-25 152515.jpg)]
外部文件:
driver = com.mysql.jdbc.Driver
url = jdbc:mysql://localhost:3306/mybatis?useSSL=false&useUnicode=true&characterEncoding=utf8
userName = root
password = root
配置文件的改变(请根据配置文档的顶层结构顺序配置):
<!--导入文件properties配置文件-->
<properties resource="db.properties"></properties>
<dataSource type="POOLED">
<!-- 在导入外部文件后,可以使用${XXX}进行调用-->
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${userName}"/>
<property name="password" value="${password}"/>
</dataSource>
优化2.0(类型别名)
配置文件的改变(请根据配置文档的顶层结构顺序配置):
<!-- 类型别名-->
<typeAliases>
<typeAlias alias="user" type="com.minjiang.projo.User"></typeAlias>
</typeAliases>
映射文件的改变:
<select id="selectById" resultType="user" parameterType="user">
select * from mybatis.user where id = #{id}
</select>
映射
结果集映射
简介:
强调!!!!resultMap
元素是 MyBatis 中最重要最强大的元素
1、resultMap
元素是 MyBatis 中最重要最强大的元素。它可以让你从 90% 的 JDBC ResultSets
数据提取代码中解放出来,并在一些情形下允许你进行一些 JDBC 不支持的操作。
2、ResultMap 的设计思想是,对简单的语句做到零配置,对于复杂一点的语句,只需要描述语句之间的关系就行了。
带问题举例(java的实体类属性和数据库的字段不同)
public class User {
private int id;
private String name;
//passWord和数据库的字段不同
private String passWord;
public User() {
}
public User(int id, String name, String passWord) {
this.id = id;
this.name = name;
this.passWord = passWord;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPassWord() {
return passWord;
}
public void setPassWord(String passWord) {
this.passWord = passWord;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
", passWord='" + passWord + '\'' +
'}';
}
}
解决方法
1、在映射文件中的查询语句添加别名
<mapper namespace="com.minjiang.mapper.UserMapper">
<select id="selectAll" resultType="user">
select id, name, pwd as passWord from mybatis.user
</select>
</mapper>
2、启用结果集映射(推荐)
<mapper namespace="com.minjiang.mapper.UserMapper">
<resultMap id="UserMap" type="user">
<!-- 设置主键-->
<id column="id" property="id"></id>
<!-- column是数据库表的列名 , property是对应实体类的属性名 -->
<result column="name" property="name"></result>
<!-- ===================================================================-->
<!-- 熟悉之后,只要添加与数据库不同的部分就可以了(这就是自动手动的混合策略)-->
<result column="pwd" property="passWord"></result>
</resultMap>
<select id="selectAll" resultMap="UserMap">
select * from mybatis.user
</select>
</mapper>
自动映射
在简单的场景下,MyBatis 可以为你自动映射查询结果。
在 ResultSet 出现的列,如果没有设置手动映射,将被自动映射。
<resultMap id="map" type="user"></resultMap>
<select id="selectById" resultMap="map">
select * from mybatis.user where id=#{id}
</select>
总结
但如果遇到复杂的场景,你需要构建一个结果映射。混合使用这两种策略
备注
无论设置的自动映射等级是哪种,你都可以通过在结果映射上设置 autoMapping
属性来为指定的结果映射设置启用/禁用自动映射(mybatis默认autoMapping
属性为true)。
<resultMap id="userResultMap" type="User" autoMapping="false">
<result property="password" column="hashed_password"/>
</resultMap>
注释开发
普及概念(面对接口编程)
面对接口编程
根本原因 : 解耦 , 可拓展 , 提高复用 , 分层开发中 , 上层不用管具体的实现 , 大家都遵守共同的标准 , 使得开发变得容易 , 规范性更好
Mybatis的注释开发
备注:maybatis提供的基于注解的的配置java注解的表达力和灵活性十分有限。mybatis映射并不能用注解来构建
-
sql 类型主要分成 :
-
- @select ()
- @update ()
- @Insert ()
- @delete ()
**注意:**利用注解开发就不需要mapper.xml映射文件了 .
运用注解的查询例子
1、添加接口方法
public interface UserMapper {
@Select("select * from user")
public List<User> selectAll();
}
2、修改核心配置文件
<mappers>
<mapper class="com.minjiang.mapper.UserMapper"></mapper>
</mappers>
3、测试文件
public class TestUserMapper {
@Test
public void selectAll(){
//获取会话
SqlSession session = MybatisUtils.getSession();
//获取映射文件
UserMapper mapper = session.getMapper(UserMapper.class);
//通过映射文件调用方法
List<User> users = mapper.selectAll();
// 遍历得出结果
for(User user : users){
System.out.println(user);
}
}
}
日志
作用:测试SQL的时候,有更快的排错效率
Mybatis 通过使用内置的日志工厂提供日志功能
- SLF4J
- Apache Commons Logging
- Log4j 2
- Log4j
- JDK logging
Mybatis会(按上面罗列的顺序)使用第一个查找到的实现。当没有找到这些实现时,将会禁用日志功能。
标准日志实现
指定 MyBatis 应该使用哪个日志记录实现。如果此设置不存在,则会自动发现日志记录实现。
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
测试,可以看到控制台有大量的输出!我们可以通过这些输出来判断程序到底哪里出了Bug
备注
value可选的值有:SLF4J、LOG4J、LOG4J2、JDK_LOGGING、COMMONS_LOGGING、STDOUT_LOGGING、NO_LOGGING
mybatis中log4j的实现
简介:
- Log4j是Apache的一个开源项目
- 通过使用Log4j,我们可以控制日志信息输送的目的地:控制台,文本,GUI组件…
- 我们也可以控制每一条日志的输出格式;
- 通过定义每一条日志信息的级别,我们能够更加细致地控制日志的生成过程。最令人感兴趣的就是,这些可以通过一个配置文件来灵活地进行配置,而不需要修改应用的代码。
步骤
1、导入依赖
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
</dependencies>
2、配置文件编写
#将等级为DEBUG的日志信息输出到console和file这两个目的地,console和file的定义在下面的代码
log4j.rootLogger=DEBUG,console,file
#控制台输出的相关设置
log4j.appender.console = org.apache.log4j.ConsoleAppender
log4j.appender.console.Target = System.out
log4j.appender.console.Threshold=DEBUG
log4j.appender.console.layout = org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern=[%c]-%m%n
#文件输出的相关设置
log4j.appender.file = org.apache.log4j.RollingFileAppender
log4j.appender.file.File=./log/kuang.log
log4j.appender.file.MaxFileSize=10mb
log4j.appender.file.Threshold=DEBUG
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=[%p][%d{yy-MM-dd}][%c]%m%n
#日志输出级别
log4j.logger.org.mybatis=DEBUG
log4j.logger.java.sql=DEBUG
log4j.logger.java.sql.Statement=DEBUG
log4j.logger.java.sql.ResultSet=DEBUG
log4j.logger.java.sql.PreparedStatement=DEBUG
3、在Mybatis的核心配置文件中进行设置(注意:)
<!-- log4j日志输出-->
<settings>
<setting name="logImpl" value="LOG4J"/>
</settings>
4、测试:
public class TestUserMapper {
//获取当前类的日志
Logger logger = Logger.getLogger(TestUserMapper.class);
@Test
public void selectAll(){
//可以有选择的输入一些提示信息
logger.info("info:进入selectUser方法");
logger.debug("debug:进入selectUser方法");
logger.error("error: 进入selectUser方法");
}
}
注意点:导包为org.apache.log4j.Logger
结果:不仅可以在控制台输出,还可以生成一个日志文件【看不到的可以尝试下重新启动软件或是重新生成下目录】
动态SQL
推荐官方文档讲的足够清除的了
重点掌握:if 语句、Where、Set、choose语句、SQL片段
SQL片段
有时候可能某个 sql 语句我们用的特别多,为了增加代码的重用性,简化代码,我们需要将这些代码抽取出来,然后使用时直接调用。
提取SQL片段:
<sql id="if-title-author">
<if test="title != null">
title = #{title}
</if>
<if test="author != null">
and author = #{author}
</if>
</sql>
引用SQL片段:
<select id="queryBlogIf" parameterType="map" resultType="blog">
select * from blog
<where>
<!-- 引用 sql 片段,如果refid 指定的不在本文件中,那么需要在前面加上 namespace -->
<include refid="if-title-author"></include>
<!-- 在这里还可以引用其他的 sql 片段 -->
</where>
</select>
注意:
①、最好基于 单表来定义 sql 片段,提高片段的可重用性
②、在 sql 片段中不要包括 where
Mybatis:缓存的使用
缓存的认知
缓存定义:存在内存中的临时数据
缓存的目的:将经常查询的数据放在缓存中,用户不用从磁盘上(关系型数据库数据文件)查询
缓存的使用数据:经常查询并且不经常改变的数据。
备注:在mybatis中分为一级缓存和二级缓存
一级缓存(本地缓存)
简介
与数据库同一次会话期间查询到的数据会放在本地缓存中(如果获取相同的数据,直接从缓存中拿,没必须再去查询数据库)(在mybatis中默认开启)
实体
public class User {
private int id;
private String name;
private String pwd;
public User(){
}
public User(int id, String name, String pwd){
this.id = id;
this.name = name;
this.pwd =pwd;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPwd() {
return pwd;
}
public void setPwd(String pwd) {
this.pwd = pwd;
}
@Override
public String toString() {
return "com.minjiang.pojo.User{" +
"id=" + id +
", name='" + name + '\'' +
", pwd='" + pwd + '\'' +
'}';
}
}
接口
public interface UserMapper {
/**
* 条件查询
* @param id
* @return
*/
public User selectById(@Param("id") int id);
}
映射文件
<?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">
<mapper namespace="com.minjiang.mapper.UserMapper">
<select id="selectById" parameterType="_int" resultType="user">
select * from mybatis.user where id = #{id}
</select>
</mapper>
工具类
public class MyBatisUtils {
private static String resource = "mybatis-config.xml";
private static InputStream inputStream;
private static SqlSessionFactory sqlSessionFactory;
static {
try {
inputStream = Resources.getResourceAsStream(resource);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
} catch (IOException e) {
e.printStackTrace();
}
}
public static SqlSession getSession(){
SqlSession session = sqlSessionFactory.openSession();
return session;
}
}
测试
public class CacheTest02 {
/**
* 开启日志
* */
Logger logger = Logger.getLogger(CacheTest02.class);
@Test
public void selectById(){
SqlSession session = MyBatisUtils.getSession();
UserMapper mapper = session.getMapper(UserMapper.class);
UserMapper mapper1 = session.getMapper(UserMapper.class);
User user = mapper.selectById(1);
User user1 = mapper1.selectById(1);
System.out.println(user);
System.out.println(user1);
System.out.println(user == user1);
//要记住关闭缓存,不然可能会无法使用
session.close();
}
}
结果
可以通过显示的日志发现查询语句只执行了一次
备注
默认情况下,只有一级缓存开启。(SqlSession级别的缓存,也称为本地缓存)
为便于以后的维护建议显性表示
一级缓存失败的四种情况
1、sqlSession不同(每个sqlSession中的缓存相互独立)
2、sqlSession相同,你要的查询条件不同(缓存会进行刷新)
3、sqlSession相同,两次查询之间执行了增删改操作!(缓存会进行刷新)
4、sqlSession相同,手动清除一级缓存
清除一级缓存
SqlSession session = MybatisUtils.getSession();
session.clearCache();
关闭缓存
SqlSession session = MybatisUtils.getSession();
session.close();
二级缓存(全局缓存)
简介
基于namespace级别的缓存,一个名称空间,对应一个二级缓存;
-
工作机制
-
- 一个会话查询一条数据,这个数据就会被放在当前会话的一级缓存中;
- 如果当前会话关闭了,这个会话对应的一级缓存就没了;但是我们想要的是,会话关闭了,一级缓存中的数据被保存到二级缓存中;
- 新的会话查询信息,就可以从二级缓存中获取内容;
- 不同的mapper查出的数据会放在自己对应的缓存(map)中;
备注
默认情况下,只启用了本地的会话缓存,它仅仅对一个会话中的数据进行缓存。 要启用全局的二级缓存,只需要在你的 SQL 映射文件(只作用于一个映射文件)中添加一行:
<cache/>
建议同时开启全局缓存 【mybatis-config.xml】(便于维护)
<setting name="cacheEnabled" value="true"/>
配置二级缓存
1、开启全局缓存 【mybatis-config.xml】(官方文档规定)
<setting name="cacheEnabled" value="true"/>
2、在映射文件中配置(只作用于一个映射文件)
<!-- 可通过Mybatis观看详细说明-->
<cache
eviction="FIFO"
flushInterval="60000"
size="512"
readOnly="true"/>
eviction可用的清除策略有:
LRU
– 最近最少使用:移除最长时间不被使用的对象。FIFO
– 先进先出:按对象进入缓存的顺序来移除它们。SOFT
– 软引用:基于垃圾回收器状态和软引用规则移除对象。WEAK
– 弱引用:更积极地基于垃圾收集器状态和弱引用规则移除对象。
flushInterval(刷新间隔)属性可以被设置为任意的正整数
size(引用数目)属性可以被设置为任意正整数
readOnly(只读)属性可以被设置为 true 或 false
自定义二级缓存
条件:使用第三方缓存实现–EhCache:
步骤
1、导入依赖包
<!-- https://mvnrepository.com/artifact/org.mybatis.caches/mybatis-ehcache -->
<dependency>
<groupId>org.mybatis.caches</groupId>
<artifactId>mybatis-ehcache</artifactId>
<version>1.1.0</version>
</dependency>
2、在SQL映射文件中填加
<cache type="org.mybatis.caches.ehcache.EhcacheCache"/>
3、编写ehcache.xml文件
<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd">
<!--
diskStore:为缓存路径,ehcache分为内存和磁盘两级,此属性定义磁盘的缓存位置。参数解释如下:
user.home – 用户主目录
user.dir – 用户当前工作目录
java.io.tmpdir – 默认临时文件路径
-->
<diskStore path="./tmpdir/Tmp_EhCache"/>
<defaultCache
eternal="false"
maxElementsInMemory="10000"
overflowToDisk="false"
diskPersistent="false"
timeToIdleSeconds="1800"
timeToLiveSeconds="259200"
memoryStoreEvictionPolicy="LRU"/>
<cache
name="cloud_user"
eternal="false"
maxElementsInMemory="5000"
overflowToDisk="false"
diskPersistent="false"
timeToIdleSeconds="1800"
timeToLiveSeconds="1800"
memoryStoreEvictionPolicy="LRU"/>
<!--
defaultCache:默认缓存策略,当ehcache找不到定义的缓存时,则使用这个缓存策略。只能定义一个。
-->
<!--
name:缓存名称。
maxElementsInMemory:缓存最大数目
maxElementsOnDisk:硬盘最大缓存个数。
eternal:对象是否永久有效,一但设置了,timeout将不起作用。
overflowToDisk:是否保存到磁盘,当系统当机时
timeToIdleSeconds:设置对象在失效前的允许闲置时间(单位:秒)。仅当eternal=false对象不是永久有效时使用,可选属性,默认值是0,也就是可闲置时间无穷大。
timeToLiveSeconds:设置对象在失效前允许存活时间(单位:秒)。最大时间介于创建时间和失效时间之间。仅当eternal=false对象不是永久有效时使用,默认是0.,也就是对象存活时间无穷大。
diskPersistent:是否缓存虚拟机重启期数据 Whether the disk store persists between restarts of the Virtual Machine. The default value is false.
diskSpoolBufferSizeMB:这个参数设置DiskStore(磁盘缓存)的缓存区大小。默认是30MB。每个Cache都应该有自己的一个缓冲区。
diskExpiryThreadIntervalSeconds:磁盘失效线程运行时间间隔,默认是120秒。
memoryStoreEvictionPolicy:当达到maxElementsInMemory限制时,Ehcache将会根据指定的策略去清理内存。默认策略是LRU(最近最少使用)。你可以设置为FIFO(先进先出)或是LFU(较少使用)。
clearOnFlush:内存数量最大时是否清除。
memoryStoreEvictionPolicy:可选策略有:LRU(最近最少使用,默认策略)、FIFO(先进先出)、LFU(最少访问次数)。
FIFO,first in first out,这个是大家最熟的,先进先出。
LFU, Less Frequently Used,就是上面例子中使用的策略,直白一点就是讲一直以来最少被使用的。如上面所讲,缓存的元素有一个hit属性,hit值最小的将会被清出缓存。
LRU,Least Recently Used,最近最少使用的,缓存的元素有一个时间戳,当缓存容量满了,而又需要腾出地方来缓存新的元素的时候,那么现有缓存元素中时间戳离当前时间最远的元素将被清出缓存。
-->
</ehcache>
mybatis偷懒点
改造MybatisUtils工具类的getSession( ) 方法,重载实现。
//获取SqlSession连接
public static SqlSession getSession(){
return getSession(true); //事务自动提交
}
public static SqlSession getSession(boolean flag){
return sqlSessionFactory.openSession(flag);
}
overflowToDisk:是否保存到磁盘,当系统当机时
timeToIdleSeconds:设置对象在失效前的允许闲置时间(单位:秒)。仅当eternal=false对象不是永久有效时使用,可选属性,默认值是0,也就是可闲置时间无穷大。
timeToLiveSeconds:设置对象在失效前允许存活时间(单位:秒)。最大时间介于创建时间和失效时间之间。仅当eternal=false对象不是永久有效时使用,默认是0.,也就是对象存活时间无穷大。
diskPersistent:是否缓存虚拟机重启期数据 Whether the disk store persists between restarts of the Virtual Machine. The default value is false.
diskSpoolBufferSizeMB:这个参数设置DiskStore(磁盘缓存)的缓存区大小。默认是30MB。每个Cache都应该有自己的一个缓冲区。
diskExpiryThreadIntervalSeconds:磁盘失效线程运行时间间隔,默认是120秒。
memoryStoreEvictionPolicy:当达到maxElementsInMemory限制时,Ehcache将会根据指定的策略去清理内存。默认策略是LRU(最近最少使用)。你可以设置为FIFO(先进先出)或是LFU(较少使用)。
clearOnFlush:内存数量最大时是否清除。
memoryStoreEvictionPolicy:可选策略有:LRU(最近最少使用,默认策略)、FIFO(先进先出)、LFU(最少访问次数)。
FIFO,first in first out,这个是大家最熟的,先进先出。
LFU, Less Frequently Used,就是上面例子中使用的策略,直白一点就是讲一直以来最少被使用的。如上面所讲,缓存的元素有一个hit属性,hit值最小的将会被清出缓存。
LRU,Least Recently Used,最近最少使用的,缓存的元素有一个时间戳,当缓存容量满了,而又需要腾出地方来缓存新的元素的时候,那么现有缓存元素中时间戳离当前时间最远的元素将被清出缓存。
–>
```mybatis偷懒点
改造MybatisUtils工具类的getSession( ) 方法,重载实现。
//获取SqlSession连接
public static SqlSession getSession(){
return getSession(true); //事务自动提交
}
public static SqlSession getSession(boolean flag){
return sqlSessionFactory.openSession(flag);
}
【注意】确保实体类和数据库字段对应