数据库建表是时
create table abc(*****)后面跟的sql代码含义:
enging=innodb (引擎)
Default charset=utf-8(字符集)
需要的依赖:mysql、mybatis
编写mybatis的核心配置文件mybatis-config.xm:
<?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>
<!--mybatis提供了一个logImpl日志工厂-->
<settings>
<!--默认有一个 STDOUT_LOGGING ,不用引入任何东西,直接配置一下就好-->
<!--<setting name="logImpl" value="STDOUT_LOGGING"/>-->
<!--LOG4J需要映入包-->
<setting name="logImpl" value="LOG4J"/>
</settings>
<!--实体类起别名-->
<typeAliases>
<!--1 给一个类起一个固定的别名-->
<!--<typeAlias type="com.tt.pojo.Books" alias="Book"/>-->
<!--2 扫描实体类的包,将每个实体类首字母小写作为动态的别名-->
<package name="com.tt.pojo"/>
<!-- 实体类上加注解@Ailas("bookss")来定义别名 , 但还是要用 <package name="com.tt.pojo"/> 扫描实体类的包-->
</typeAliases>
<environments default="development">
<environment id="development">
<!-- mybatis的事务管理器,有两个jdbc managed,(常用jdbc)-->
<transactionManager type="JDBC"/>
<!--mybatis数据源类型的控制 现有三个-->
<dataSource type="POOLED">
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/tyd?useSSL=true&useUnicode=true&characterEncoding=utf8&serverTimezone=UTC"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</dataSource>
</environment>
</environments>
<!-- 每一个Mapper.xml都需要在Mybatis核心配置文件中注册 !-->
<!-- mappers 标签就是用来配置 需要加载的 sql 映射配置文件路径的。-->
<mappers>
<!--1.相对资源路径配置-->
<!-- mapper标签,通过resource属性引入classpath路径的相对资源-->
<mapper resource="com/tt/dao/model/BookMapper.xml"/>
<!--2.使用映射器接口类注册引入-->
<!--mapper标签,通过class属性指定mapper接口名称,
此时对应的映射文件
必须与接口位于同一路径下,并且名称相同 -->
<!--<mapper class="com.tt.dao.mapper.BookMapper"/>-->
<!--3.接口所在包-->
<!--mapper接口所在的包名 . package标签,通过name属性指定mapper接口所在的包名 ,
此时对应的映射文件
必须与接口位于同一路径下,并且名称相同-->
<!--<package name="com.tt.dao.mapperr."/>-->
</mappers>
</configuration>
编写MyBatis工具类
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;
/**
* @author TT
*/
public class MybatisUtils {
private static SqlSessionFactory sqlSessionFactory;
static {
try {
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
/* SqlSessionFactoryBuilder就是建造者,通过获取配置文件信息,建造sqlSessionFactory工厂模式 */
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 获取SqlSession连接
* 获取SqlSession连接,也就是创建了能执行sql数据库的所有方法
*/
public static SqlSession getSession(){
/* 工厂里面生产SqlSession */
return sqlSessionFactory.openSession();
}
}
编写代码
实体类
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.apache.ibatis.type.Alias;
/**
* @author TT
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
@Alias("bookss")
public class Books {
private int bookID;
private String bookName;
private int bookCounts;
private String detail;
}
Dao接口
import java.util.List;
/**
* @author TT
*/
public interface BookMapper {
List<Books> selectUser();
List<Books> selectUser1(int id);
}
编写Mapper.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">
<mapper namespace="com.tt.dao.mapper.BookMapper">
<!--select 中 resultType="bookss" 是返回了一个实际类型,
这里还可以用resultMap 来给 实体类和数据库查出的字段名做一个对应,也就是映射-->
<!--type 用的也是起的别名,不起别名写的就是源路径-->
<resultMap id="aaa" type="bookss">
<!--column 是数据库字段 property 是实体类的字段-->
<result column="bookID" property="bookID"/>
<result column="bookName" property="bookName"/>
<result column="bookCounts" property="bookCounts"/>
<result column="detail" property="detail"/>
</resultMap>
<select id="selectUser" resultMap="aaa">
select * from books;
</select>
<select id="selectUser1" parameterType="int" resultType="bookss">
select * from books where bookID = #{id};
</select>
</mapper>
log4j (log4j.properties)配置文件
#将等级为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/rizhi.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
测试
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;
import java.util.List;
public class myTest {
@Test
public void selectUser() {
//调用工具类中getSession方法
SqlSession sqlsession = MybatisUtils.getSession();
//方法一:
//List users=session.selectList("com.kuang.mapper.UserMapper.selectUser");
//方法二:
BookMapper mapper=sqlsession.getMapper(BookMapper.class);
List<Books> users=mapper.selectUser();
System.out.println("==================");
for(Books books:users){
System.out.println(books);
}
System.out.println("==================");
List<Books> users1=mapper.selectUser1(1001);
for(Books books:users1){
System.out.println(books);
}
System.out.println("==================");
//提交事务
sqlsession.commit();
//建议写在finall里面
sqlsession.close();
}
}
常见问题:
1、在mybatis配置文件中必须注册每一个Mapper.xml文件,或者直接指向这个类
2、找不到Mapper.xml文件,一般放在resources文件夹中也行
在Pom.xml文件中在build中配置resources来防止资源导出失败问题
静态资源过滤问题
<build>
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
</resources>
</build>
技术点总结
Mybatis传参单个参数
传入参数类型(parameterType=”int”)
返回类型(resultType=”com.tt.pojo.user”)
Mapper接口那边如果为(int id),则在xml where条件后写:where id=#{id}
增删改需要提交事务
sqlsession.commit();
Map传参(野路子)
在xml文件中使用时用#{key值},传入参数类型为:parameterType=”Map”
模糊查询like语句:
(1)第1种:在Java代码中添加sql通配符
//接口
List names = mapper.selectlike(“%李%”);
//xml文件
select * from foo where bar like #{value}
(2)在sql语句中拼接通配符,会引起sql注入
List names = mapper.selectlike(“李”);
select * from foo where bar like “%”#{value}“%”
配置解析
核心配置文件
mybatis-config.xml 系统核心配置文件
MyBatis 的配置文件包含了会深深影响 MyBatis 行为的设置和属性信息。
能配置的内容如下:
configuration(配置)
properties(属性)
settings(设置)
typeAliases(类型别名)
typeHandlers(类型处理器)
objectFactory(对象工厂)
plugins(插件)
environments(环境配置)
environment(环境变量)
transactionManager(事务管理器) 、
dataSource(数据源)
databaseIdProvider(数据库厂商标识)
mappers(映射器)
Properties优化:把数据库连接信息写在db.properties文件中,然后在mybatis-config.xml文件中用参数来使用db.properties文件中的数据库连接需要的信息。
第一步 ; 在资源目录下新建一个db.properties
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=utf8
username=root
password=123456
第二步 : 将文件导入properties 配置文件
<!--导入properties文件-->
<properties resource="db.properties"/>
或
这里还可以把一部分配置文件写在properties 标签里面(这里优先使用外部配置文件)
<properties resource="db.properties">
<property name=”username” value=”root”/>
<property name=”password” value=”123456”/>
</properties >
第三步就可以引用了
<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>
<mapper resource="mapper/UserMapper.xml"/>
</mappers>
</configuration>
属性名与字段名不一致:
起别名:在xml文件中那as 别名
例子:数据库字段pwd,实体类属性password
select id,name,pwd as password from user
结果集映射(resultMap)
id name pwd
id name password
<resultMap id="UserMap" type="User">
<!-- id为主键 -->
<id column="id" property="id"/>
<!-- column是数据库表的列名 , property是对应实体类的属性名 -->
<result column="name" property="name"/>
<result column="pwd" property="password"/>
</resultMap>
<select id="selectUserById" resultMap="UserMap">
select id , name , pwd from user where id = #{id}
</select>
resultMap 元素是 MyBatis 中最重要最强大的元素。
ResultMap 的设计思想是,对简单的语句做到零配置,对于复杂一点的语句,只需要描述语句之间的关系就行了
ResultMap 的优秀之处——你完全可以不用显式地配置它们
分页的几种方式:
日志工厂
Mybatis内置的日志工厂提供日志功能,具体的日志实现有以下几种工具:
- SLF4J
- Log4j 【掌握】
- Log4j2
- JDK logging
- COMMONS_LOGGING
- STDOUT_LOGGING 【掌握】
- NO_LOGGING
STDOUT_LOGGING
指定 MyBatis 应该使用哪个日志记录实现。如果此设置不存在,则会自动发现日志记录实现。(写在mybatis核心配置文件中)
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
这样就可以直接使用
Log4j
简介:
Log4j是Apache的一个开源项目
通过使用Log4j,我们可以控制日志信息输送的目的地:控制台,文本,GUI组件…
我们也可以控制每一条日志的输出格式;
通过定义每一条日志信息的级别,我们能够更加细致地控制日志的生成过程。最令人感兴趣的就是,这些可以通过一个配置文件来灵活地进行配置,而不需要修改应用的代码。
使用步骤:
导入log4j的包
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
配置文件编写(log4j.properties)
#将等级为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/rizhi.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
setting设置日志实现(在mybatis核心配置文件中配置log4j日志实现)
<settings>
<setting name="logImpl" value="LOG4J"/>
</settings>
在程序中使用Log4j进行输出!
//注意导包:org.apache.log4j.Logger
//在哪个类(MyTest)里面使用它(log4j)就写哪个类。参数为当前类的class
static Logger logger = Logger.getLogger(MyTest.class);
@Test
public void selectUser() {
logger.info("info:进入selectUser方法");
logger.debug("debug:进入selectUser方法");
logger.error("error: 进入selectUser方法");
SqlSession session = MybatisUtils.getSession();
UserMapper mapper = session.getMapper(UserMapper.class);
List<User> users = mapper.selectUser();
for (User user: users){
System.out.println(user);
}
session.close();
}
测试,看控制台输出!
使用Log4j 输出日志
可以看到还生成了一个日志的文件 【需要修改file的日志级别】
分页三种方法
1 使用Limit实现分页
2 RowBounds分页
mapper接口
//选择全部用户RowBounds实现分页(不传参了)
List<User> getUserByRowBounds();
mapper文件(也不写limit或条件了)
<select id="getUserByRowBounds" resultType="user">
select * from user
</select>
测试类
在这里,我们需要使用RowBounds类
@Test
public void testUserByRowBounds() {
SqlSession session = MybatisUtils.getSession();
int currentPage = 2; //第几页
int pageSize = 2; //每页显示几个
RowBounds rowBounds = new RowBounds((currentPage-1)*pageSize,pageSize);
//通过session.**方法进行传递rowBounds,[此种方式现在已经不推荐使用了]
List<User> users = session.selectList("com.kuang.mapper.UserMapper.getUserByRowBounds", null, rowBounds);
for (User user: users){
System.out.println(user);
}
session.close();
}
PageHelper
导包
com.github.pagehelper
pagehelper
5.2.0
在mybatis配置文件中配置
<plugins>
<plugin interceptor="com.github.pagehelper.PageInterceptor">
<!-- <property name="dialect" value="mysql"/>-->
<property name="reasonable" value="true"/>
</plugin>
</plugins>
代码实现
PageHelper.startPage(1, 2);
//得到的对象结果
List<users> user = mapper.selectUser();
//将结果放入PageHelper
PageInfo<users> returnLists=new PageInfo<>(user);
//已分页后的结果
List<users> userList=returnLists.getList();
使用注解开发:
第一步:在DAO接口类中的方法加注解@select(select * from user)
@select ()
@update ()
@Insert ()
@delete ()
例子://查询全部用户
@Select("select id,name,pwd password from user")
public List<User> getAllUser();
第二布:在mybatis核心配置文件中加注解接口的绑定设置
例子:
<!--使用class绑定接口,绑定DAO接口类-->
<mappers>
<mapper class="com.kuang.mapper.UserMapper"/>
</mappers>
自动实现事务提交
修改工具类
改造MybatisUtils工具类的getSession( ) 方法,重载实现。
//获取SqlSession连接
public static SqlSession getSession(){
return getSession(true); //事务自动提交
}
//或
public static SqlSession getSession(boolean flag){
return sqlSessionFactory.openSession(flag);
}
关于@Param
@Param注解用于给方法参数起一个名字。以下是总结的使用原则:
在方法只接受一个参数的情况下,可以不使用@Param。
在方法接受多个参数的情况下,建议一定要使用@Param注解给参数命名。
如果参数是 JavaBean , 则不能使用@Param。
不使用@Param注解时,参数只能有一个,并且是Javabean。
例子:
//根据id查询用户
User selectUserById(@Param("ad") User user,@Param("id") int ids,String name);
则在xml调用时可
#{ad.password}
注:sql语句中取得的id是@Param()中的id.
动态SQL
什么是动态SQL:动态SQL就是根据不同的条件生成不同的SQL语句
所谓的动态SQL,本质上还是SQL语句,只是我们可以在SQL层面,去执行一个逻辑代码
If
<!--需求1:
根据作者名字和博客名字来查询博客!
如果作者名字为空,那么只根据博客名字查询,反之,则根据作者名来查询
select * from blog where title = #{title} and author = #{author}
-->
<select id="queryBlogIf" parameterType="map" resultType="user">
select * from User where 1=1
<if test="title != null">
and title = #{title}
</if>
<if test="author != null">
and author = #{author}
</if>
</select>
Where
修改上面的SQL语句;在where标签中如果标签返回的内容是以AND 或OR 开头的,则它会剔除掉
<select id="queryBlogIf" parameterType="map" resultType="user">
select * from user
<where>
<if test="title != null">
title = #{title}
</if>
<if test="author != null">
and author = #{author}
</if>
</where>
</select>
Set
在进行更新操作的时候
<!--注意set是用的逗号隔开-->
<update id="updateBlog" parameterType="map">
update blog
<set>
<if test="title != null">
title = #{title},
</if>
<if test="author != null">
author = #{author}
</if>
</set>
where id = #{id};
</update>
choose
有时候,我们不想用到所有的查询条件,只想选择其中的一个,查询条件有一个满足即可,使用 choose 标签可以解决此类问题,类似于 Java 的 switch 语句(就是在choose中只要有一个满足就跳出来,没有一个满足走otherwise标签)
<select id="queryBlogChoose" parameterType="map" resultType="blog">
select * from blog
<where>
<choose>
<when test="title != null">
title = #{title}
</when> <when test="author != null">
and author = #{author}
</when>
<otherwise>
and views = #{views}
</otherwise>
</choose>
</where>
</select>
Foreach
<select id="queryBlogForeach" parameterType="map" resultType="blog">
select * from blog
<where>
<!--
collection:指定输入对象中的集合属性
item:每次遍历生成的对象
open:开始遍历时的拼接字符串
close:结束时拼接的字符串
separator:遍历对象之间需要拼接的字符串
select * from blog where 1=1 and (id=1 or id=2 or id=3)
-->
<foreach collection="ids" item="id" open="and (" close=")" separator="or">
id=#{id}
</foreach>
</where>
</select>
测试:
HashMap map = new HashMap();
List<Integer> ids = new ArrayList<Integer>();
ids.add(1);
ids.add(2);
ids.add(3);
map.put("ids",ids);
缓存
经常查询但不经常改变的数据【可以使用缓存】
MyBatis包含一个非常强大的查询缓存特性,它可以非常方便地定制和配置缓存。缓存可以极大的提升查询效率。
MyBatis系统中默认定义了两级缓存:一级缓存和二级缓存
<1>默认情况下,只有一级缓存开启。(SqlSession级别的缓存,也称为本地缓存)
<2>二级缓存需要手动开启和配置,他是基于namespace级别的缓存。
<3>为了提高扩展性,MyBatis定义了缓存接口Cache。我们可以通过实现Cache接口来自定义二级缓存
一级缓存
一级缓存也叫本地缓存:
与数据库同一次会话期间查询到的数据会放在本地缓存中。
以后如果需要获取相同的数据,直接从缓存中拿,没必须再去查询数据库;
例子:测试
@Test
public void testQueryUserById(){
SqlSession session = MybatisUtils.getSession();
UserMapper mapper = session.getMapper(UserMapper.class);
User user = mapper.queryUserById(1);
System.out.println(user);
User user2 = mapper.queryUserById(1);
System.out.println(user2);
System.out.println(user==user2);
session.close();
}
一级缓存失效情况:没有使用到当前的一级缓存,效果就是,还需要再向数据库中发起一次查询请求
sqlSession不同
@Test
public void testQueryUserById(){
SqlSession session = MybatisUtils.getSession();
SqlSession session2 = MybatisUtils.getSession();
UserMapper mapper = session.getMapper(UserMapper.class);
UserMapper mapper2 = session2.getMapper(UserMapper.class);
User user = mapper.queryUserById(1);
System.out.println(user);
User user2 = mapper2.queryUserById(1);
System.out.println(user2);
System.out.println(user==user2);
session.close();
session2.close();
}
观察结果:发现发送了两条SQL语句!
结论:每个sqlSession中的缓存相互独立
sqlSession相同,查询条件不同
@Testpublic void testQueryUserById(){
SqlSession session = MybatisUtils.getSession();
UserMapper mapper = session.getMapper(UserMapper.class);
UserMapper mapper2 = session.getMapper(UserMapper.class);
User user = mapper.queryUserById(1);
System.out.println(user);
User user2 = mapper2.queryUserById(2);
System.out.println(user2);
System.out.println(user==user2);
session.close();
}
观察结果:发现发送了两条SQL语句!很正常的理解
结论:当前缓存中,不存在这个数据
sqlSession相同,两次查询之间执行了增删改操作!
增加方法
//修改用户int updateUser(Map map);
编写SQL
<update id="updateUser" parameterType="map">
update user set name = #{name} where id = #{id}</update>
测试
@Testpublic void testQueryUserById(){
SqlSession session = MybatisUtils.getSession();
UserMapper mapper = session.getMapper(UserMapper.class);
User user = mapper.queryUserById(1);
System.out.println(user);
HashMap map = new HashMap();
map.put("name","kuangshen");
map.put("id",4);
mapper.updateUser(map);
User user2 = mapper.queryUserById(1);
System.out.println(user2);
System.out.println(user==user2);
session.close();
}
观察结果:查询在中间执行了增删改操作后,重新执行了
结论:因为增删改操作可能会对当前数据产生影响
sqlSession相同,手动清除一级缓存
@Test
public void testQueryUserById(){
SqlSession session = MybatisUtils.getSession();
UserMapper mapper = session.getMapper(UserMapper.class);
User user = mapper.queryUserById(1);
System.out.println(user);
session.clearCache();//手动清除缓存
User user2 = mapper.queryUserById(1);
System.out.println(user2);
System.out.println(user==user2);
session.close();
}
一级缓存就是一个map
二级缓存
(1)二级缓存也叫全局缓存,一级缓存作用域太低了,所以诞生了二级缓存
(2)基于namespace级别的缓存,一个名称空间,对应一个二级缓存;
(3)工作机制
- 一个会话查询一条数据,这个数据就会被放在当前会话的一级缓存中;
- 如果当前会话关闭了,这个会话对应的一级缓存就没了;但是我们想要的是,会话关闭了,一级缓存中的数据被保存到二级缓存中;
- 新的会话查询信息,就可以从二级缓存中获取内容;
- 不同的mapper查出的数据会放在自己对应的缓存(map)中;
使用步骤:
1、开启全局缓存 【mybatis-config.xml】
<setting name="cacheEnabled" value="true"/>
2、去每个mapper.xml中配置使用二级缓存,这个配置非常简单;【xxxMapper.xml】
<cache/>
官方示例=====>查看官方文档<cache
eviction="FIFO"
flushInterval="60000"
size="512"
readOnly="true"/>
这个更高级的配置创建了一个 FIFO 缓存,每隔 60 秒刷新,最多可以存储结果对象或列表的 512 个引用,而且返回的对象被认为是只读的,因此对它们进行修改可能会在不同线程中的调用者产生冲突。
截图显示:
3、代码测试
所有的实体类先实现序列化接口
测试代码
@Test
public void testQueryUserById(){
SqlSession session = MybatisUtils.getSession();
SqlSession session2 = MybatisUtils.getSession();
UserMapper mapper = session.getMapper(UserMapper.class);
UserMapper mapper2 = session2.getMapper(UserMapper.class);
User user = mapper.queryUserById(1);
System.out.println(user);
session.close();
User user2 = mapper2.queryUserById(1);
System.out.println(user2);
System.out.println(user==user2);
session2.close();
}
缓存结论
只要开启了二级缓存,我们在同一个Mapper中的查询,可以在二级缓存中拿到数据
查出的数据都会被默认先放在一级缓存中
只有会话提交或者关闭以后,一级缓存中的数据才会转到二级缓存中;
缓存原理图:
自定义缓存:EhCache
1 Ehcache是一种广泛使用的java分布式缓存,用于通用缓存;
要在应用程序中使用Ehcache,需要引入依赖的jar包
<!-- 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 在mapper.xml中使用对应的缓存即可
<mapper namespace = “org.acme.FooMapper” >
<cache type = “org.mybatis.caches.ehcache.EhcacheCache” /></mapper>
图例:
3 编写ehcache.xml文件,如果在加载时未找到/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"
updateCheck="false">
<!--
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>
合理的使用缓存,可以让我们程序的性能大大提升!