Mybatis深入学习
java日志处理框架
对于一个应用程序来说日志记录是必不可少的一部分。线上问题追踪,错误排查,基于 日志的业务逻辑统计分析等都离不日志。
Log4j 简介
Log For Java(Java 的日志) 是 Apache 提供的一个开源的 Java 主流的日志框架。
Log4j有8个日志级别
在 Log4j 中建议只使用 DEBUG、INFO、WARN、ERROR 四个日志级别。
ALL 最低等级的,用于打开所有日志记录。
DEBUG 指出细粒度信息事件对调试应用程序是非常有帮助的,主要用于开发过程中打 印一些运行信息。
INFO 消息在粗粒度级别上突出强调应用程序的运行过程。打印一些你感兴趣的或者重 要的信息,这个可以用于生产环境中输出程序运行的一些重要信息,但是不能滥用,避 免打印过多的日志。
WARN 表明会出现潜在错误的情形,有些信息不是错误信息,但是也要给程序员的一 些提示。
ERROR 指出虽然发生错误事件,但仍然不影响系统的继续运行。打印错误和异常信息, 如果不想输出太多的日志,可以使用这个级别。
FATAL 指出每个严重的错误事件将会导致应用程序的退出。这个级别比较高了。重大错 误,这种级别你可以直接停止程序了。
OFF 最高等级的,用于关闭所有日志记录。
log4j配置文件详解
Log4j配置文件名
og4j 配置文件名:log4j.properties Log4j
配值文件存放位置:项目的 src 的根目录中
log4j.rootLogger=debug,console,logfile
### appender.console输出到控制台 ###
log4j.appender.console=org.apache.log4j.ConsoleAppender
log4j.appender.console.layout=org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern=<%d> %5p (%F:%L) [%t] (%c) - %m%n
log4j.appender.console.Target=System.out
### appender.logfile输出到日志文件 ###
log4j.appender.logfile=org.apache.log4j.RollingFileAppender
log4j.appender.logfile.File=SysLog.log
log4j.appender.logfile.MaxFileSize=500KB
log4j.appender.logfile.MaxBackupIndex=7
log4j.appender.logfile.layout=org.apache.log4j.PatternLayout
log4j.appender.logfile.layout.ConversionPattern=<%d> %p (%F:%L) [%t] %c - %m%n
配置根Logger
log4j.rootLogger = [level],appenderName,appenderName2,…
level 是日志记录的优先级,优先级从低到高分别是 DEBUG,INFO,WARN,ERROR。通过在 这里定义的级别,您可以控制到应用程序中相应级别的日志信息的开关,比如在这里定义了 INFO 级别,则应用程序中所有 DEBUG 级别的日志信息将不被打印出来。
appenderName 就是指定日志信息输出到哪个地方。可同时指定多个输出目的地。
Log4j 中的 appender
向控制台输出的 appender
### appender.console 输出到控制台 ###
log4j.appender.console=org.apache.log4j.ConsoleAppender
log4j.appender.console.layout=org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern=<%d> %5p (%F:%L) [%t] (%c) - %m%n
log4j.appender.console.Target=System.out
向文件输出的 appender
### appender.logfile 输出到日志文件 ###
log4j.appender.logfile=org.apache.log4j.RollingFileAppender
log4j.appender.logfile.File=SysLog.log
log4j.appender.logfile.MaxFileSize=500KB
log4j.appender.logfile.MaxBackupIndex=7
log4j.appender.logfile.layout=org.apache.log4j.PatternLayout
log4j.appender.logfile.layout.ConversionPattern=<%d> %p (%F:%L) [%t] %c - %m%n
向数据库输出的 appender
log4j.appender.logDB=org.apache.log4j.jdbc.JDBCAppender
log4j.appender.logDB.layout=org.apache.log4j.PatternLayout
log4j.appender.logDB.Driver=com.mysql.jdbc.Driver
log4j.appender.logDB.URL=jdbc:mysql://localhost:3306/bjsxt
log4j.appender.logDB.User=root log4j.appender.logDB.Password=root
log4j.appender.logDB.Sql=INSERT INTO
logs(project_name,create_date,level,category,file_name,thread_name,line,all_ category,message)values('logDemo','%d{yyyy-MM-ddHH:mm:ss}','%p','%c','%F','% t','%L','%l','%m')
通过包名控制日志输出级别
log4j.logger.org.apache=FATAL
log4j.logger.org.apache.commons=ERROR
log4j.logger.org.springframework=ERROR
log4j.logger.com.bjsxt=ERROR
Log4j 的输出格式
Log4J 采用类似 C 语言中的 printf 函数的打印格式格式化日志信息,打印参数如下:
%m 输出代码中指定的消息
%p 输出优先级,即 DEBUG,INFO,WARN,ERROR,FATAL
%r 输出自应用启动到输出该 log 信息耗费的毫秒数
%c 输出所属的类目,通常就是所在类的全名
%t 输出产生该日志事件的线程名
%n 输出一个回车换行符,Windows 平台为“\r\n”,Unix 平台为“\n”
%d 输出日志时间点的日期或时间,默认格式为 ISO8601,也可以在其后指定格式 如:%d{yyyy 年 MM 月 dd 日 HH:mm:ss,SSS},输出类似:2020 年 05 月 01 日 22:10:28,921
%l 输出日志事件的发生位置,包括类目名、发生的线程,以及在代码中的行数 如: Testlog.main(TestLog.java:10) %F 输出日志消息产生时所在的文件名称
%L 输出代码中的行号
%x 输出和当前线程相关联的 NDC(嵌套诊断环境),像 java servlets 多客户多线程的应用 中
%% 输出一个"%"字符
可以在%与模式字符之间加上修饰符来控制其最小宽度、最大宽度、和文本的对齐方式。 如:
%5c: 输出 category 名称,最小宽度是 5,category<5,默认的情况下右对齐
%-5c:输出 category 名称,最小宽度是 5,category<5,"-"号指定左对齐,会有空格
%.5c:输出 category 名称,最大宽度是 5,category>5,就会将左边多出的字符截掉,<5 不会有空格
%20.30c:category 名称<20 补空格,并且右对齐,>30 字符,就从左边交远销出的字符截
掉
Log4j 的使用方式
添加 jar 包:log4j.jar
添加配置文件:log4j.properties
通过 Log4j 的 API 完成日志
import org.apache.log4j.Logger;
public class LogDemo {
private final static Logger logger = Logger.getLogger(LogDemo.class);
public static void main(String[]args){
String str = "Bjsxt";
logger.debug(str);
}
}
在log4j.properties内:
log4j.rootLogger=debug,console,logfile
添加debug
所有的控制台输出的日志信息都被系统存放到SysLog.log文件内:
将log4j.properties内的debug改为info:
log4j.rootLogger=info,console,logfile
public class LogDemo {
private final static Logger logger = Logger.getLogger(LogDemo.class);
public static void main(String[]args){
String str = "Bjsxt";
logger.debug(str);
try{
String temp = null;
temp.length();
}catch (Exception e){
logger.debug(e);
logger.error(e);
}
}
}
只输出logger.error(e); 不输出logger.debug(e);
commons-logging + log4j(经常用到)
commons-logging 是 Apache 的 Commons 项目中提供的一个高层的日志框架,是门面模式的典型应用。
默认情况下,Commons Loggin 自动搜索并使用 Log4j,如果没有找到 Log4j,再使用 JDK Logging。
需要添加的jar包:
log4j.jar
commons-loggin.jar
我们将info改回为debug:
log4j.rootLogger=debug,console,logfile
public class CommonsDemo {
private final static Log log = LogFactory.getLog(LogDemo.class);
public static void main(String[]args){
String str = "Bjsxt";
log.debug(str);
}
}
模拟一个空指针异常:
public class SessionsDemo {
private final static Log log = LogFactory.getLog(LogDemo.class);
public static void main(String[]args){
try{
//人工模拟一个空指针异常
String str = null;
str.length();
}catch (Exception e){
log.error("输出",e);
}
}
}
slf4j-api + slf4j-log4j + log4j
slf4j 对日志支持的操作更多,SLF4J 并不是具体 的日志框架,而是作为一个简单门面服务于各类日志框架,
slf4j-api.jar slf4j-log4j.jar log4j.jar
需要添加的jar包:
slf4j-api.jar
slf4j-log4j.jar
log4j.jar
log4j.rootLogger=debug,console,logfile
package com.bjsxt.test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class SLF4jDemo {
private final static Logger logger = LoggerFactory.getLogger(SLF4jDemo.class);
public static void main(String[] args) {
logger.debug("SLF4J...");
try{
String str = null;
str.length();
}catch(Exception e){
logger.error("aaaa",e);
}
}
}
Mybatis 配置的完善
Mybatis 的日志管理
Mybatis 的内置日志工厂(LogFactory)提供日志处理功能, MyBatis 内置日志工厂基于运行时自省机制选择合适的日志工具。
它会使用第一个查 找得到的工具(按上文列举的顺序查找)。如果一个都未找到,日志功能就会被禁用。也就是说在项目中把日志工具环境配置出来后,不用再 MyBatis 进行配置就可以让日志生效.
指定 SLF4J 作为日志处理器
settings标签必须添加到Properties标签下:
<settings>
<setting name="logImpl" value="SLF4J"/>
</settings>
也就是说,我们在mybatis内处理日志的方式,等同于原来在slf4j内做的日志处理方式
使用别名 alias
Mybatis 中的别名配置它只和 XML 配置有关, 只用来减少类完全限定名的多余部分。注 意:别名都是大小写不敏感的。
在配置文件中为类的完整路径定义别名,可以采用两种方式:
使用typeAilas指定单个类的别名
<typeAliases>
<typeAlias type="com.bjsxt.pojo.Users" alias="u"/>
</typeAliases>
<select id="selectUsersById" parameterType="int" resultType="u">
select * from users where userid = #{suibian}
</select>
使用 package 指定某个包下所有类的默认别名:
<typeAliases>
<package name="com.bjsxt.pojo"/>
</typeAliases>
引入别名后的映射文件
<select id="selectUsersById" parameterType="int" resultType="users">
select * from users where userid = #{suibian}
</select>
<!--配置别名-->
<typeAliases>
<typeAlias type="com.bjsxt.pojo.Users" alias="u"/>
//将包名作为别名
<package name="com.bjsxt.pojo"/>
</typeAliases>
SqlSession 对象下的常用 API
SqlSession 对象下的方法是用来执行定义在映射配置文件中的 SELECT,INSERT,UPDATE 和 DELETE 语句。通过 SqlSession 对象下的 API 向 SQL 语句传递参数时,参数可以是基本数 据类型、包装类类型 ,POJO 或 Map。但是参数的数量只能是一个。
SqlSession 对象下的方法需要 namespace + id 来定位需要执行的 SQL 语句。
namespace + id 的作用: 即:namespace 定位到唯一的 mapper 映射文件,id 定位到这 个 mapper 映射文件的指定的 sql 语句。
查询操作
- selectOne方法
T selectOne(String namespace + id , Object parameter) - selectMap方法
<K,V> Map<K,V> selectMap(String namespace + id , Object parameter, String mapKey)
上一个mybatis示例里的预更新,使用的就是selectOne方法
在这里,我们在上一个示例项目的基础上,使用selectMap方法,实现根据用户名与用户性别查询用户
修改映射配置文件
<!--根据用户名与用户性别查询用户-->
<select id="selectUsersByNameAndSex" resultType="u">
select * from users where username =#{name} and usersex = #{sex}
</select>
修改UsersDao接口
//根据用户名与用户性别查询用户
Map<Integer,Users> selectUserByNameAndSex(String username,String usersex);
修改UsersDaoImpl实现类
//根据用户名与用户性别查询用户
@Override
public Map<Integer, Users> selectUserByNameAndSex(String username, String usersex) {
SqlSession sqlSession = MybatisUtils.getSqlSession();
Map<String,String> param = new HashMap<>();
param.put("name",username);
param.put("sex",usersex);
Map<Integer, Users> users = sqlSession.selectMap("com.bjsxt.mapper.UserMapper.selectUsersByNameAndSex", param, "userid");
return users;
}
修改UsersDaoServlet接口
//根据用户名与用户性别查询用户的抽象方法
Map<Integer,Users> findUsersByNameAndSex(String username, String usersex);
修改UsersDaoServletImpl接口实现类
//根据用户名与用户性别查询用户
@Override
public Map<Integer, Users> findUsersByNameAndSex(String username, String usersex) {
Map<Integer, Users> map = null;
try{
UsersDao usersDao = new UsersDaoImpl();
map = usersDao.selectUserByNameAndSex(username,usersex);
}catch(Exception e){
e.printStackTrace();
}finally{
MybatisUtils.closeSqlSession();
}
return map;
}
创建测试类
package com.bjsxt.test;
import com.bjsxt.pojo.Users;
import com.bjsxt.service.UsersService;
import com.bjsxt.service.impl.UsersServiceImpl;
import java.util.Map;
import java.util.Set;
public class SelectMapTest {
public static void main(String[] args) {
UsersService usersService = new UsersServiceImpl();
Map<Integer, Users> map = usersService.findUsersByNameAndSex("oldliu", "male");
//获取到map的key
Set<Integer> integers = map.keySet();
for(Integer key:integers){
Users users = map.get(key);
//输出map的key值
System.out.println(key);
System.out.println(users.getUserid()+"\t"+users.getUsername()+"\t"+users.getUsersex());
}
}
}
DML(添加;修改;删除) 操作
我们在之前的实例内使用过:
Mapper 动态代理
基于 Mybatis 的 Dao 层设计
目前有两种,一种是不使用Mapper动态代理的方式,讲解如下:
不使用动态代理是指在 Dao 层需要我们自己来创建 Dao 层的接口与接口实现类。 在接口实现类的方法中我们自己通过调用 SqlSession 对象的方法完成数据库的操作。
目前我们的入门案例就是通过这种方式完成了对 users 表的 CRUD 操作。
但这样做,在SqlSession对象的常用方法内只可以向sql语句传递一个参数,如果要传递多个参数,则需要封装到pojo或者Map内。
同时在调用SqlSession对象的方法时会有硬编码的现象namespace+id。
我们最常用的还是在Dao层使用Mapper动态代理
既:Mapper 动态代理(或称为接口 绑定)的操作方式。这种方式下程序员只需要写 Dao 接口,不需要创建 Dao 的接口实现类, **Mybatis 会自动生成接口实现类的代理对象。在 Dao 层我们只要创建接口与映射配置文件即 可。**这种方式可以大大简化 Dao 层的代码结构,是在开发中最常见的使用方式。
Mapper动态代理规范
Mapper 动态代理的使用
搭建环境
创建项目
创建一个java项目,名为mapperDemo
添加jar包
创建实体类
package com.bjsxt.pojo;
public class Users {
private int userid;
private String username;
private String usersex;
public int getUserid() {
return userid;
}
public void setUserid(int userid) {
this.userid = userid;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getUsersex() {
return usersex;
}
public void setUsersex(String usersex) {
this.usersex = usersex;
}
@Override
public String toString() {
return "Users{" +
"userid=" + userid +
", username='" + username + '\'' +
", usersex='" + usersex + '\'' +
'}';
}
}
创建Mybatis工具类
package com.bjsxt.utils;
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 MybatisUtils {
private static ThreadLocal<SqlSession> threadLocal = new ThreadLocal<>();
private static SqlSessionFactory sqlSessionFactory = null;
static{
//创建SqlSessionFactory
InputStream is = null;
try{
is = Resources.getResourceAsStream("mybatis-cfg.xml");
}catch (IOException e){
e.printStackTrace();
}
sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
}
//获取SqlSession
public static SqlSession getSqlSession(){
SqlSession sqlSession = threadLocal.get();
if(sqlSession == null){
sqlSession = sqlSessionFactory.openSession();
threadLocal.set(sqlSession);
}
return sqlSession;
}
//关闭SqlSession
public static void closeSqlSession(){
SqlSession sqlSession = threadLocal.get();
if(sqlSession != null){
sqlSession.close();
threadLocal.set(null);
}
}
}
配置Mybatis框架(使用动态代理)
添加db.properties文件
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3307/sys?useSSL=false
jdbc.username=root
jdbc.password=1234567
添加log4j.properties文件
log4j.rootLogger=debug,console,logfile
### appender.console输出到控制台 ###
log4j.appender.console=org.apache.log4j.ConsoleAppender
log4j.appender.console.layout=org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern=<%d> %5p (%F:%L) [%t] (%c) - %m%n
log4j.appender.console.Target=System.out
### appender.logfile输出到日志文件 ###
log4j.appender.logfile=org.apache.log4j.RollingFileAppender
log4j.appender.logfile.File=SysLog.log
log4j.appender.logfile.MaxFileSize=500KB
log4j.appender.logfile.MaxBackupIndex=7
log4j.appender.logfile.layout=org.apache.log4j.PatternLayout
log4j.appender.logfile.layout.ConversionPattern=<%d> %p (%F:%L) [%t] %c - %m%n
添加全局配置文件mybatis-cfg.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>
<!--引入db.properties属性文件-->
<properties resource="db.properties"/>
<settings>
<setting name="useGeneratedKeys" value="true"/>
</settings>
<!--配置别名-->
<typeAliases>
<package name="com.bjsxt.pojo"/>
</typeAliases>
<!--配置环境-->
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"></transactionManager>
<dataSource type="POOLED">
<property name="driver" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</dataSource>
</environment>
</environments>
<!--引入Mapper映射配置文件-->
<mappers>
<package name="com.bjsxt.mapper"/>
</mappers>
</configuration>
添加 UsersMapper 接口
package com.bjsxt.mapper;
public interface UsersMapper {
}
添加UsersMapper映射配置文件
<?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.bjsxt.mapper.UsersMapper">
</mapper>
实现查询所有用户
修改映射配置文件
<!--查询所有用户-->
<select id="selectUsersAll" resultType="users">
select * from users
</select>
修改UsersMapper接口添加抽象方法
public interface UsersMapper {
//这个抽象方法的方法名必须和它有关系的映射配置文件里对应的id一样
List<Users> selectUsersAll();
}
创建业务层接口
public interface UsersService {
//这个起名随意
List<Users> findUsersAll();
}
创建业务层接口实现类
package com.bjsxt.serivce.impl;
import com.bjsxt.mapper.UsersMapper;
import com.bjsxt.pojo.Users;
import com.bjsxt.serivce.UsersService;
import com.bjsxt.utils.MybatisUtils;
import org.apache.ibatis.session.SqlSession;
import java.util.List;
/**
* 通过mybatis动态代理的方式去操作数据库
*/
public class UsersServiceImpl implements UsersService {
@Override
public List<Users> findUsersAll() {
List<Users> list = null;
try{
SqlSession sqlSession = MybatisUtils.getSqlSession();
//getMapper方法的作用是根据给定的接口的Class对象,生成该接口的接口实现类的代理对象
UsersMapper mapper = sqlSession.getMapper(UsersMapper.class);
list = mapper.selectUsersAll();
}catch(Exception e){
e.printStackTrace();
}finally{
MybatisUtils.closeSqlSession();
}
return list;
}
}
创建测试类
package com.bjsxt.test;
import com.bjsxt.pojo.Users;
import com.bjsxt.serivce.UsersService;
import com.bjsxt.serivce.impl.UsersServiceImpl;
import java.util.List;
public class SelectUsersAllTest {
public static void main(String[] args) {
UsersService usersService = new UsersServiceImpl();
List<Users> list = usersService.findUsersAll();
/**
* 简化的循环,相当于将迭代之后的list赋值给了变量e 然后输出e
* list.forEach(e -> System.out.println(e));
*
* 相当于
* for(Users e:list){
* System.out.println(e);
* }
*/
list.forEach(System.out::println);
}
}
实现根据用户 ID 查询用户
修改映射配置文件
<!--根据用户ID查询用户-->
<select id="selectUsersById" resultType="users">
select * from users where userid = #{userid}
</select>
修改UsersMapper接口
Users selectUsersById(int userid);
修改业务层接口
Users findUsersById(int userid);
修改业务层接口实现类
@Override
public Users findUsersById(int userid) {
Users users = null;
try{
SqlSession sqlSession = MybatisUtils.getSqlSession();
UsersMapper mapper = sqlSession.getMapper(UsersMapper.class);
users = mapper.selectUsersById(userid);
}catch(Exception e){
e.printStackTrace();
}finally{
MybatisUtils.closeSqlSession();
}
return users;
}
创建测试类
package com.bjsxt.test;
import com.bjsxt.pojo.Users;
import com.bjsxt.serivce.UsersService;
import com.bjsxt.serivce.impl.UsersServiceImpl;
public class SelectUsersByIdTest {
public static void main(String[] args) {
UsersService usersService = new UsersServiceImpl();
Users users = usersService.findUsersById(1);
System.out.println(users);
}
}
Mapper动态代理模式下的多参数处理
顺序传参法
在映射文件中,SQL 语句中的参数需要使用 arg0,arg1…或者 param1,param2…表示参 数的顺序。此方法可读性低,且要求参数的顺序不能出错,在开发中不建议使用。
<!--根据用户姓名与性别查询用户,使用顺序传参法-->
<select id="selectUsersOrderParam" resultType="users">
select * from users where username = #{param1} and usersex= #{param2}
</select>
在UsersMapper接口内添加一个和上面的id有关的抽象方法:
selectUsersOrderParam方法
List<Users> selectUsersOrderParam(String username,String usersex);
使用一个测试类进行测试:
package com.bjsxt.test;
import com.bjsxt.mapper.UsersMapper;
import com.bjsxt.pojo.Users;
import com.bjsxt.utils.MybatisUtils;
import org.apache.ibatis.session.SqlSession;
import java.util.List;
public class SelectUsersOrderParamTest {
public static void main(String[] args) {
SqlSession sqlSession = MybatisUtils.getSqlSession();
UsersMapper usersMapper = sqlSession.getMapper(UsersMapper.class);
List<Users> list = usersMapper.selectUsersOrderParam("oldliu","male");
list.forEach(System.out::println);
MybatisUtils.closeSqlSession();
}
}
@Param 注解传参法
在接口方法的参数列表中通过@Param 注解来定义参数名称,在 SQL 语句中通过注解中 所定义的参数名称完成参数位置的指定。
此方式在参数不多的情况还是比较直观的,推荐使用。
<!--根据用户姓名与性别查询用户,使用@Param注解传参法-->
<select id="selectUsersAnnParam" resultType="users">
select * from users where username = #{name} and usersex= #{sex}
</select>
在UsersMapper接口内创建一个和映射配置文件内对应的 id有关的抽象方法
List<Users> selectUsersAnnParam(@Param("name") String username,@Param("sex") String usersex);
package com.bjsxt.test;
import com.bjsxt.mapper.UsersMapper;
import com.bjsxt.pojo.Users;
import com.bjsxt.utils.MybatisUtils;
import org.apache.ibatis.session.SqlSession;
import java.util.List;
public class SelectUsersAnnParamTest {
public static void main(String[] args) {
SqlSession sqlSession = MybatisUtils.getSqlSession();
UsersMapper usersMapper = sqlSession.getMapper(UsersMapper.class);
List<Users> list = usersMapper.selectUsersAnnParam("oldliu","male");
list.forEach(System.out::println);
MybatisUtils.closeSqlSession();
}
}
根据这三条代码,我们可以看出,已将参数绑定到sql语句内,且查询出结果
POJO 传参法
在 Mapper 动态代理中也可以使用 POJO 作为传递参数的载体,在 SQL 语句中绑定参数 时使用 POJO 的属性名作为参数名即可。此方式推荐使用。
也就是使用实体类作为载体去传递参数
<!--根据用户姓名与性别查询用户,使用POJO传参法-->
<select id="selectUsersPOJOParam" resultType="users">
select * from users where username = #{username} and usersex= #{usersex}
</select>
List<Users> selectUsersPOJOParam(Users users);
package com.bjsxt.test;
import com.bjsxt.mapper.UsersMapper;
import com.bjsxt.pojo.Users;
import com.bjsxt.utils.MybatisUtils;
import org.apache.ibatis.session.SqlSession;
import java.util.List;
public class SelectUsersPOJOParamTest {
public static void main(String[] args) {
SqlSession sqlSession = MybatisUtils.getSqlSession();
UsersMapper usersMapper = sqlSession.getMapper(UsersMapper.class);
Users users = new Users();
users.setUsername("oldliu");
users.setUsersex("male");
List<Users> list = usersMapper.selectUsersPOJOParam(users);
list.forEach(System.out::println);
MybatisUtils.closeSqlSession();
}
}
Map传参法
在 Mapper 动态代理中也可以使用 Map 作为传递参数的载体,在 SQL 语句中绑定参数 时使用 Map 的 Key 作为参数名即可。此方法适合在传递多参数时,如果没有 POJO 能与参数 匹配,可以使用该方式传递参数。推荐使用。
MyBatis 传递 map 参数时,如果传递参数中没有对应的 key 值,在执行 sql 语句时默认 取的是 null。
<!--根据用户姓名与性别查询用户,使用Map传参法-->
<select id="selectUsersMapParam" resultType="users">
select * from users where username = #{keyname} and usersex= #{keysex}
</select>
List<Users> selectUsersMapParam(Map<String,String> map);
package com.bjsxt.test;
import com.bjsxt.mapper.UsersMapper;
import com.bjsxt.pojo.Users;
import com.bjsxt.utils.MybatisUtils;
import org.apache.ibatis.session.SqlSession;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class SelectUsersMapParamTest {
public static void main(String[] args) {
SqlSession sqlSession = MybatisUtils.getSqlSession();
UsersMapper usersMapper = sqlSession.getMapper(UsersMapper.class);
Map<String,String> map = new HashMap<>();
map.put("keyname","oldliu");
map.put("keysex","male");
List<Users> list = usersMapper.selectUsersMapParam(map);
list.forEach(System.out::println);
MybatisUtils.closeSqlSession();
}
}
映射配置文件中的特殊字符处理
在 Mybatis 的映射配置文件中不可以使用一些特殊字符,如:<,>。
使用符号实体
<!--查询用户ID大于1的用户-->
<select id="selectUsers" resultType="users">
select * from users where userid > #{userid}
</select>
List<Users> selectUsers(int userid);
package com.bjsxt.test;
import com.bjsxt.mapper.UsersMapper;
import com.bjsxt.pojo.Users;
import com.bjsxt.utils.MybatisUtils;
import org.apache.ibatis.session.SqlSession;
import java.util.List;
public class SelectUsersTest {
public static void main(String[] args) {
SqlSession sqlSession = MybatisUtils.getSqlSession();
UsersMapper usersMapper = sqlSession.getMapper(UsersMapper.class);
List<Users> list = usersMapper.selectUsers(1);
list.forEach(System.out::println);
MybatisUtils.closeSqlSession();
}
}
CDATA区
CDATA:全称为 Character Data,以"<![CDATA[ "内容" ]]>",CDATA 中的内容不会被解析程 序解析。
<!--查询用户ID大于1的用户-->
<select id="selectUsers" resultType="users">
select * from users where userid <![CDATA[ > ]]> #{userid}
</select>
Mybatis 的分页查询
使用 RowBounds
RowBounds 是 Mybatis 提供的一个专门处理分页的对象。在 RowBounds 对象中有两个 成员变量:
offset:偏移量, 从 0 开始计数
limit:限制条数
使用 RowBounds 进行分页,非常方便,不需要在 SQL 语句中写 limit关键字,即可完成分页的功能。但是由于它是在 SQL 查询出所有结果的基础上截取数据的,所以在数据量大的 SQL 中并不适用,它更适合在返回数据结果较少的查询中使用。
修改映射配置文件
<!--查询所有数据使用RowBounds实现分页处理-->
<select id="selectUsersRowBounds" resultType="users">
select * from users
</select>
List<Users> selectUsersRowBounds(RowBounds rowBounds);
package com.bjsxt.test;
import com.bjsxt.mapper.UsersMapper;
import com.bjsxt.pojo.Users;
import com.bjsxt.utils.MybatisUtils;
import org.apache.ibatis.session.RowBounds;
import org.apache.ibatis.session.SqlSession;
import java.util.List;
public class SelectUsersRowBoundsTest {
public static void main(String[] args) {
SqlSession sqlSession = MybatisUtils.getSqlSession();
UsersMapper usersMapper = sqlSession.getMapper(UsersMapper.class);
//创建一个RowBounds对象
// (1,1) 第一个1是从第2个数据开始显示,第二个1是显示1条数据
RowBounds rowBounds = new RowBounds(1,1);
List<Users> list = usersMapper.selectUsersRowBounds(rowBounds);
list.forEach(System.out::println);
MybatisUtils.closeSqlSession();
}
}
当显示两条数据时:
// (1,1) 第一个1是从第2个数据开始显示,第二个2是显示2条数据
RowBounds rowBounds = new RowBounds(1,2);
使用SQL语句分页
在分页查询时,如果返回的结果较多,那么需要使用特定的 SQL 语句来实现分页处理。 在 MySQL 数据库中我们可以使用 limit 实现分页。
修改映射配置文件
<!--查询所有数据使用limit实现分页处理-->
<select id="selectUsersLimit" resultType="users">
select * from users limit #{offset},#{limit}
</select>
List<Users> selectUsersLimit(@Param("offset") int offset,@Param("limit") int limit);
package com.bjsxt.test;
import com.bjsxt.mapper.UsersMapper;
import com.bjsxt.pojo.Users;
import com.bjsxt.utils.MybatisUtils;
import org.apache.ibatis.session.RowBounds;
import org.apache.ibatis.session.SqlSession;
import java.util.List;
public class SelectUsersLimitTest {
public static void main(String[] args) {
SqlSession sqlSession = MybatisUtils.getSqlSession();
UsersMapper usersMapper = sqlSession.getMapper(UsersMapper.class);
List<Users> list = usersMapper.selectUsersLimit(1,1);
list.forEach(System.out::println);
MybatisUtils.closeSqlSession();
}
}
Mapper 动态代理模式下的 DML 操作
实现添加用户的操作
修改映射配置文件
<!--添加用户-->
<insert id="insertUsers" >
insert into users values(default ,#{username},#{usersex})
</insert>
修改 UsersMapper 接口
int insertUsers(Users users);
创建测试类
package com.bjsxt.test;
import com.bjsxt.mapper.UsersMapper;
import com.bjsxt.pojo.Users;
import com.bjsxt.utils.MybatisUtils;
import org.apache.ibatis.session.SqlSession;
public class InsertUsersTest {
public static void main(String[] args) {
SqlSession sqlSession = MybatisUtils.getSqlSession();
UsersMapper usersMapper = sqlSession.getMapper(UsersMapper.class);
Users users = new Users();
users.setUsername("itbz");
users.setUsersex("male");
int flag = usersMapper.insertUsers(users);
sqlSession.commit();
System.out.println(flag);
MybatisUtils.closeSqlSession();
}
}
主键值回填
应用场景:
有一个表older,它的主键是olderId,是自增的,使用这个表的主键作为订单号,在插入一个数据的同时应该返回一个订单号,像这种场景就会用到主键值回填。
在数据库中插入数据时,有时我们是需要获取新数据的主键值。在 Mybatis 中支持主键 值回填,可以让我们更够更方便的获取新添加数据的主键值。
Mybatis 中支持两种方法获取主键:
获取自增主键的值。如:MySQL、SqlServer
获取非自增主键的值。如 Oracle
获取自增主键值
开启自动获取自增主键值
局部配置
映射配置文件
useGeneratedKeys="true"只对当前得到insert有作用
<!--添加用户时获取主键值[自增]-->
<insert id="insertUsersGetKey" useGeneratedKeys="true" keyProperty="userid">
insert into users values(default ,#{username},#{usersex})
</insert>
void insertUsersGetKey(Users users);
package com.bjsxt.test;
import com.bjsxt.mapper.UsersMapper;
import com.bjsxt.pojo.Users;
import com.bjsxt.utils.MybatisUtils;
import org.apache.ibatis.session.SqlSession;
public class InsertUsersGetKeyTest {
public static void main(String[] args) {
SqlSession sqlSession = MybatisUtils.getSqlSession();
UsersMapper usersMapper = sqlSession.getMapper(UsersMapper.class);
Users users = new Users();
//设置插入的数据
users.setUsername("itbz-sxt3");
users.setUsersex("male");
//调用方法,将数据通过方法放到sql语句内
usersMapper.insertUsersGetKey(users);
sqlSession.commit();
System.out.println(users.getUserid());
MybatisUtils.closeSqlSession();
}
}
全局配置
全局配置文件
<settings>
<setting name="useGeneratedKeys" value="true"/>
</settings>
省去useGeneratedKeys=“true”,使的这个效果产生在全局上
同时,我们的映射配置文件可以修改为:
<!--添加用户获取主键值[自增]-->
<insert id="insertUsersGetKey" keyProperty="userid">
insert into users values(default ,#{username},#{usersex})
</insert>
获取非自增主键值
适用于数据库内不支持自增的数据表,也适用于支持自增的额数据表。
修改映射配置文件
<!--添加用户获取主键值[非自增] oracle数据库用[order:Before] mysql数据库用[order:after]-->
<insert id="insertUsersGetKey2">
<selectKey order="AFTER" keyProperty="userid" resultType="int">
<!--在MySQL数据库中获取插入数据的主键的值: SELECT LAST_INSERT_ID(),SELECT @@Identity 是SELECT LAST_INSERT_ID()的同义词。作用是相同的。 -->
SELECT LAST_INSERT_ID()
</selectKey>
insert into users values(default ,#{username},#{usersex})
</insert>
修改 UsersMapper 接口
void insertUsersGetKey2(Users users);
创建测试类
package com.bjsxt.test;
import com.bjsxt.mapper.UsersMapper;
import com.bjsxt.pojo.Users;
import com.bjsxt.utils.MybatisUtils;
import org.apache.ibatis.session.SqlSession;
public class InsertUserGetKey2Test {
public static void main(String[] args) {
SqlSession sqlSession = MybatisUtils.getSqlSession();
UsersMapper usersMapper = sqlSession.getMapper(UsersMapper.class);
Users users = new Users();
users.setUsername("itbz-sxt4");
users.setUsersex("male");
usersMapper.insertUsersGetKey2(users);
sqlSession.commit();
System.out.println(users.getUserid());
MybatisUtils.closeSqlSession();
}
}
下去可以详细了解下Mapper动态代理的原理是什么
动态SQL
在 MyBatis 中提供了动态 SQL 功能。将使用 Java 代码拼接 SQL 语句,改变为在 XML 映射文件中使用标签拼接 SQL 语句。
MyBatis 中动态 SQL 是编写在 mapper.xml 中的,其语法和 JSTL 类似,但是却是基于强大 的 OGNL 表达式实现的。
if 标签
if 标签单分支判断语句
修改映射配置文件
<!-- if标签的使用 根据用户给定的条件进行查询 当id不为0,其他参数不为空时添加sql语句 -->
<select id="selectUsersByProperty" resultType="users">
select * from users where 1=1
<if test="userid != 0">
and userid = #{userid}
</if>
<if test="username != null and username != ''">
and username = #{username}
</if>
<if test="usersex != null and usersex != ''">
and usersex = #{usersex}
</if>
</select>
修改UsersMapper接口
List<Users> selectUsersByProperty(Users users);
创建测试类
package com.bjsxt.test;
import com.bjsxt.mapper.UsersMapper;
import com.bjsxt.pojo.Users;
import com.bjsxt.utils.MybatisUtils;
import org.apache.ibatis.session.SqlSession;
import java.util.List;
public class SelectUsersByPropertyTest {
public static void main(String[] args) {
SqlSession sqlSession = MybatisUtils.getSqlSession();
UsersMapper usersMapper = sqlSession.getMapper(UsersMapper.class);
Users users = new Users();
users.setUsername("itbz-sxt4");
users.setUserid(13);
List<Users> list = usersMapper.selectUsersByProperty(users);
list.forEach(System.out::println);
}
}
choose,when,otherwise标签
从多个条件中选择一个使用
修改映射配置文件
<!-- choose,when,otherwise标签的使用 多选一条件-->
<select id="selectUsersByChoose" resultType="users">
select * from users where 1=1
<choose>
<when test="username != null and username != ''">
and username = #{username}
</when>
<when test="usersex != null and usersex != ''">
and usersex = #{usersex}
</when>
<otherwise>
and userid = 1
</otherwise>
</choose>
</select>
修改UsersMapper接口
List<Users> selectUsersByChoose(Users users);
创建测试类
package com.bjsxt.test;
import com.bjsxt.mapper.UsersMapper;
import com.bjsxt.pojo.Users;
import com.bjsxt.utils.MybatisUtils;
import org.apache.ibatis.session.SqlSession;
import java.util.List;
public class SelectUsersByChooseTest {
public static void main(String[] args) {
SqlSession sqlSession = MybatisUtils.getSqlSession();
UsersMapper usersMapper = sqlSession.getMapper(UsersMapper.class);
Users users = new Users();
users.setUsername("itbz-sxt4");
users.setUsersex("male");
List<Users> list = usersMapper.selectUsersByChoose(users);
list.forEach(System.out::println);
}
}
where标签
使用 where 标签,就不需要提供 where 1=1 这样的条件了。如果判断条件不为空则自 动添加 where 关键字,并且会自动去掉第一个条件前面的 and 或 or。
修改映射配置文件
<!-- where标签的使用 根据用户给定的条件进行查询使用where标签实现-->
<select id="selectUsersByPropertyWhere" resultType="users">
select * from users
<where>
<if test="userid != 0">
and userid = #{userid}
</if>
<if test="username != null and username != ''">
and username = #{username}
</if>
<if test="usersex != null and usersex != ''">
and usersex = #{usersex}
</if>
</where>
</select>
修改UsersMapper接口
List<Users> selectUsersByPropertyWhere(Users users);
创建测试类
package com.bjsxt.test;
import com.bjsxt.mapper.UsersMapper;
import com.bjsxt.pojo.Users;
import com.bjsxt.utils.MybatisUtils;
import org.apache.ibatis.session.SqlSession;
import java.util.List;
public class SelectUsersByPropertyWhereTest {
public static void main(String[] args) {
SqlSession sqlSession = MybatisUtils.getSqlSession();
UsersMapper usersMapper = sqlSession.getMapper(UsersMapper.class);
Users users = new Users();
users.setUsername("itbz-sxt4");
users.setUserid(6);
List<Users> list = usersMapper.selectUsersByPropertyWhere(users);
list.forEach(System.out::println);
}
}
bind标签
bind 标签允许我们在 OGNL 表达式以外创建一个变量,并可以将其绑定到当前的 SQL 语句中。一般应用于模糊查询,通过 bind 绑定通配符和查询值。
修改映射配置文件
<!-- bind标签 根据用户姓名模糊查询-->
<select id="selectUsersByLikeName" resultType="users">
<bind name="likeName" value="'%'+name+'%'"/>
select * from users where username like #{likeName}
</select>
修改UsersMapper接口
List<Users> selectUsersByLikeName(String name);
创建测试类
package com.bjsxt.test;
import com.bjsxt.mapper.UsersMapper;
import com.bjsxt.pojo.Users;
import com.bjsxt.utils.MybatisUtils;
import org.apache.ibatis.session.SqlSession;
import java.util.List;
public class SelectUsersByLikeNameTest {
public static void main(String[] args) {
SqlSession sqlSession = MybatisUtils.getSqlSession();
UsersMapper usersMapper = sqlSession.getMapper(UsersMapper.class);
List<Users> list = usersMapper.selectUsersByLikeName("a");
list.forEach(System.out::println);
}
}
set标签
set 标签用在 update 语句中。借助 if 标签,可以只对有具体值的字段进行更新。set 标 签会自动添加 set 关键字,自动去掉最后一个 if 语句的多余的逗号。
修改映射配置文件
<!-- set标签的使用 选择更新-->
<update id="usersUpdate">
update users
<set>
<if test="username != null and username != ''">
username = #{username},
</if>
<if test="usersex != null and usersex != ''">
usersex = #{usersex},
</if>
</set>
where userid = #{userid}
</update>
修改UsersMapper接口
void usersUpdate(Users users);
创建测试类
package com.bjsxt.test;
import com.bjsxt.mapper.UsersMapper;
import com.bjsxt.pojo.Users;
import com.bjsxt.utils.MybatisUtils;
import org.apache.ibatis.session.SqlSession;
public class UsersUpdateTest {
public static void main(String[] args) {
SqlSession sqlSession = MybatisUtils.getSqlSession();
UsersMapper usersMapper = sqlSession.getMapper(UsersMapper.class);
Users users = new Users();
users.setUsername("LiSi");
users.setUsersex("male");
users.setUserid(2);
usersMapper.usersUpdate(users);
sqlSession.commit();
}
}
更新前:
更新后:
foreach标签
foreach 标签的功能非常强大,我们可以将任何可迭代对象如 List、Set 、Map 或者数 组对象作为集合参数传递给 foreach 标签进行遍历。它也允许我们指定开头与结尾的字符串 以及集合项迭代之间的分隔符。
迭代List,Set
- 类型:
collection=”collection”
- 名称:
修改映射配置文件
<!--查询用户ID为1或者2的用户-->
<select id="selectUsersByIdUseCollection" resultType="users">
select * from users where userid in
<foreach collection="collection" item="userid" open="(" separator="," close=")">
#{userid}
</foreach>
</select>
修改UsersMapper接口
List<Users> selectUsersByIdUseCollection(Collection collection);
创建测试类
package com.bjsxt.test;
import com.bjsxt.mapper.UsersMapper;
import com.bjsxt.pojo.Users;
import com.bjsxt.utils.MybatisUtils;
import org.apache.ibatis.session.SqlSession;
import org.omg.PortableInterceptor.INACTIVE;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
public class SelectUsersByIdUseCollectionTest {
public static void main(String[] args) {
SqlSession sqlSession = MybatisUtils.getSqlSession();
UsersMapper usersMapper = sqlSession.getMapper(UsersMapper.class);
/*List<Integer> list = new ArrayList<>();
list.add(1);
list.add(2);*/
Set<Integer> set = new HashSet<>();
set.add(1);
set.add(2);
List<Users> users = usersMapper.selectUsersByIdUseCollection(set);
users.forEach(System.out::println);
}
}
迭代数组
类型:collection=”array”
修改映射配置文件
<!--查询用户ID为1或者2的用户使用数组传递参数-->
<select id="selectUsersByIdUseArray" resultType="users">
select * from users where userid in
<foreach collection="array" item="userid" open="(" separator="," close=")">
#{userid}
</foreach>
</select>
修改UsersMapper接口
List<Users> selectUsersByIdUseArray(int[] arr);
创建测试类
package com.bjsxt.test;
import com.bjsxt.mapper.UsersMapper;
import com.bjsxt.pojo.Users;
import com.bjsxt.utils.MybatisUtils;
import org.apache.ibatis.session.SqlSession;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
public class SelectUsersByIdUseArrayTest {
public static void main(String[] args) {
SqlSession sqlSession = MybatisUtils.getSqlSession();
UsersMapper usersMapper = sqlSession.getMapper(UsersMapper.class);
int[] arr = new int[]{1,2};
List<Users> users = usersMapper.selectUsersByIdUseArray(arr);
users.forEach(System.out::println);
}
}
迭代Map
类型:collection=”suibian”或者 collection=”suibian.entrySet()”
修改映射配置文件
<!--根据给定的条件做计数处理-->
<!-- select count(*) from users where username = 'itbz-sxt4' and usersex = 'male'-->
<select id="selectUsersCount" resultType="int">
select count(*) from users where
<foreach collection="suibian" separator="and" item="value" index="key">
${key} = #{value}
</foreach>
</select>
修改UsersMapper接口
int selectUsersCount(@Param("suibian") Map<String,String> map);
创建测试类
package com.bjsxt.test;
import com.bjsxt.mapper.UsersMapper;
import com.bjsxt.utils.MybatisUtils;
import org.apache.ibatis.session.SqlSession;
import java.util.HashMap;
import java.util.Map;
public class SelectUsersCountTest {
public static void main(String[] args) {
SqlSession sqlSession = MybatisUtils.getSqlSession();
UsersMapper usersMapper = sqlSession.getMapper(UsersMapper.class);
Map<String,String> map= new HashMap<>();
map.put("usersex","male");
int count = usersMapper.selectUsersCount(map);
System.out.println(count);
}
}
使用foreach标签完成批量添加
修改映射配置文件
<!--批量添加用户-->
<!--insert into users values(DEFAULT,'itbz-sxt5','male'),(DEFAULT,'itbz-sxt6','male')-->
<insert id="insertUsersBatch" >
insert into users values
<foreach collection="collection" item="user" separator=",">
(default ,#{user.username},#{user.usersex})
</foreach>
</insert>
修改UsersMapper接口
int insertUsersBatch(List<Users> list);
创建测试类
package com.bjsxt.test;
import com.bjsxt.mapper.UsersMapper;
import com.bjsxt.pojo.Users;
import com.bjsxt.utils.MybatisUtils;
import org.apache.ibatis.session.SqlSession;
import java.util.ArrayList;
import java.util.List;
public class InsertUsersBatchTest {
public static void main(String[] args) {
SqlSession sqlSession = MybatisUtils.getSqlSession();
UsersMapper usersMapper = sqlSession.getMapper(UsersMapper.class);
List<Users> list = new ArrayList<>();
Users users = new Users();
users.setUsername("sxt6");
users.setUsersex("male");
Users users1 = new Users();
users1.setUsername("sxt7");
users1.setUsersex("male");
list.add(users);
list.add(users1);
int flag = usersMapper.insertUsersBatch(list);
sqlSession.commit();
System.out.println(flag);
}
}
Mybatis缓存
缓存是一般的 ORM 框架都会提供的功能,目的就是提升查询的效率和减少数据库的压力。
MyBatis 缓存方式分为一级缓存和二级缓存,同时也可配置关于缓存设置。
一级缓存是将结果缓存在 SqlSession 对象中,二级缓存是存储在 SqlSessionFactory 对象 中。默认情况下,MyBatis 开启一级缓存,没有开启二级缓存。
一级缓存的使用
一级缓存也叫本地缓存,MyBatis 的一级缓存是在会话(SqlSession)层面进行缓存的。 MyBatis 的一级缓存是默认开启的,不需要任何的配置。
一级缓存的生命周期
- MyBatis 在开启一个数据库会话时,会创建一个新的 SqlSession 对象,SqlSession 对象中 会有一个新的 Executor 对象。Executor 对象中持有一个新的 PerpetualCache 对象;当会 话结束时,SqlSession 对象及其内部的 Executor 对象还有 PerpetualCache 对象也一并释 放掉。
- 如果 SqlSession 调用了 close()方法,会释放掉一级缓存 PerpetualCache 对象,一级缓存 将不可用。
- 如果 SqlSession 调用了 clearCache(),会清空 PerpetualCache 对象中的数据,但是该对象 仍可使用。
- SqlSession 中执行了任何一个 update 操作(update()、delete()、insert()) ,都会清空 PerpetualCache 对象的数据,但是该对象可以继续使用。
如何判断两次查询是完全相同的查询
Mybatis 认为,对于两次查询,如果以下条件都完全一样,那么就认为它们是完全相同 的两次查询。
- 传入的 statementId。
- 查询时要求的结果集中的结果范围。
- 这次查询所产生的最终要传递给 Preparedstatement 的 Sql 语句字符串。
- 传递的参数值
测试一级缓存示例
package com.bjsxt.test;
import com.bjsxt.mapper.UsersMapper;
import com.bjsxt.pojo.Users;
import com.bjsxt.utils.MybatisUtils;
import org.apache.ibatis.session.SqlSession;
public class SelectUsersByIdCacheOneTest {
public static void main(String[] args) {
SqlSession sqlSession = MybatisUtils.getSqlSession();
UsersMapper usersMapper = sqlSession.getMapper(UsersMapper.class);
Users users = usersMapper.selectUsersById(1);
System.out.println(users);
Users users2 = new Users();
users2.setUsername("aaaa");
users2.setUsersex("male");
usersMapper.insertUsers(users);
System.out.println("-----------------");
Users users1 = usersMapper.selectUsersById(1);
System.out.println(users1);
}
}
当执行两次输出语句时,由于一级缓存的存在,只会调用一次sql查询语句。
输出整体结果:
二级缓存的使用
MyBatis 的二级缓存是 Application 级别的缓存,它可以提高对数据库查询的效率,以提高应用的性能。二级缓存是 SqlSessionFactory 上的缓存,可以是由一个 SqlSessionFactory 创 建的不同的 SqlSession 之间共享缓存数据。默认并不开启。
SqlSession 在执行 commit()或者 close()的时候将数据放入到二级缓存
Sqlsession 共享二级缓存
二级缓存的配置方式
实现二级缓存的时候,MyBatis 要求缓存的 POJO 必须 是可序列化的, 也就是要求实现 Serializable 接口。在映射配置文件中配置就可以 开启缓存了。
二级缓存特点
- 映射语句文件中的所有 select 语句将会被缓存。
- 映射语句文件中的所有 insert、update 和 delete 语句会刷新缓存。
- 二级缓存是以 namespace 为单位的,不同 namespace 下的操作互不影响
- 如果在加入<cache/>标签的前提下让个别 select 元素不使用缓存,可以使用 useCache 属性,设置为 false。
- 缓存会使用默认的 Least Recently Used(LRU,最近最少使用的)算法来收回。
- 根据时间表,比如 No Flush Interval,(CNFI 没有刷新间隔),缓存不会以任何时间顺序 来刷新。
- 缓存会存储列表集合或对象(无论查询方法返回什么)的 1024 个引用
- 缓存会被视为是 read/write(可读/可写)的缓存,意味着对象检索不是共享的,而且可以 安全的被调用者修改,不干扰其他调用者或线程所做的潜在修改。
二级缓存示例
在 mybatis-config.xml 文件中的标签配置开启二级缓存。cacheEnabled 的默认 值就是 true,所以这步的设置可以省略。
<settings>
<setting name="cacheEnabled" value="true"/>
</settings>
在映射配置文件中添加
<mapper namespace="com.bjsxt.mapper.UsersMapper">
<cache/>
</mapper>
JavaBean 对象必须实现序列化接口( implements Serializable )
package com.bjsxt.pojo;
import java.io.Serializable;
public class Users implements Serializable{
private int userid;
private String username;
private String usersex;
public int getUserid() {
return userid;
}
public void setUserid(int userid) {
this.userid = userid;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getUsersex() {
return usersex;
}
public void setUsersex(String usersex) {
this.usersex = usersex;
}
@Override
public String toString() {
return "Users{" +
"userid=" + userid +
", username='" + username + '\'' +
", usersex='" + usersex + '\'' +
'}';
}
}
创建测试类
package com.bjsxt.test;
import com.bjsxt.mapper.UsersMapper;
import com.bjsxt.pojo.Users;
import com.bjsxt.utils.MybatisUtils;
import org.apache.ibatis.session.SqlSession;
public class SelectUsersByIdCacheTwoTest {
public static void main(String[] args) {
SqlSession sqlSession = MybatisUtils.getSqlSession();
UsersMapper usersMapper = sqlSession.getMapper(UsersMapper.class);
Users users = usersMapper.selectUsersById(1);
System.out.println(users);
//第一次查询结束后关闭sqlSession
MybatisUtils.closeSqlSession();
System.out.println("-----------------------");
//再创建一个新的sqlSession对象
SqlSession sqlSession1 = MybatisUtils.getSqlSession();
UsersMapper usersMapper1 = sqlSession1.getMapper(UsersMapper.class);
//再次执行查询操作
Users users1 = usersMapper1.selectUsersById(1);
System.out.println(users1);
}
}
我们可以从下图看出,第二次查询是直接从缓存调用的查询语句和查询结果,二级缓存使用有效。